Server : Apache System : Linux iZ2vcgyutqttsd1p850kl8Z 3.10.0-1160.92.1.el7.x86_64 #1 SMP Tue Jun 20 11:48:01 UTC 2023 x86_64 User : www ( 1000) PHP Version : 5.6.40 Disable Function : passthru,exec,system,putenv,chroot,chgrp,chown,shell_exec,popen,proc_open,pcntl_exec,ini_alter,ini_restore,dl,openlog,syslog,readlink,symlink,popepassthru,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,imap_open,apache_setenv Directory : /www/wwwroot/saimikebio.com/mobile/include/kernel/cache/ |
<?php // +---------------------------------------------------------------------- // | EcTouch [ 专注移动电商: 商创网络科技 ] // +---------------------------------------------------------------------- // | Copyright (c) 2014 http://ectouch.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- // | Author: EcTouch Team <zhong@ecmoban.com> (QQ: 2880175560) // +---------------------------------------------------------------------- // 文件缓存类 基于secache修改 class EcFileCache { private $idx_node_size = 40; private $data_base_pos = 262588; //40+20+24*16+16*16*16*16*4; private $schema_item_size = 24; private $header_padding = 20; //保留空间 放置php标记防止下载 private $info_size = 20; //保留空间 4+16 maxsize|ver //40起 添加20字节保留区域 private $idx_seq_pos = 40; //id 计数器节点地址 private $dfile_cur_pos = 44; //id 计数器节点地址 private $idx_free_pos = 48; //id 空闲链表入口地址 private $idx_base_pos = 444; //40+20+24*16 private $schema_struct = array('size', 'free', 'lru_head', 'lru_tail', 'hits', 'miss'); private $ver = '$Rev: 3 $'; public $config = array(); public function __construct($config = array()) { $this->config = array( 'DB_CACHE_PATH' => 'data/db_cache/', //缓存目录 'DB_CACHE_CHECK' => 'false', //是否验证数据 'DB_CACHE_FILE' => 'cachedata', //缓存的数据文件名 'DB_CACHE_SIZE' => '15M', //预设的缓存大小 'DB_CACHE_FLOCK' => 'true', //是否存在文件锁,设置为false,将模拟文件锁 ); $this->config = array_merge($this->config, (array) $config); //检查缓存目录 $this->_checkDir(); $this->workat($this->config['DB_CACHE_PATH'] . $this->config['DB_CACHE_FILE']); } //读取缓存 public function get($key) { $key = md5($key); //设置索引值 if ($this->fetch($key, $content)) { $expire = (int) substr($content, 0, 12); if ($expire != -1 && time() >= $expire) { return false; } if ($this->config['DB_CACHE_CHECK']) { //开启数据校验 $check = substr($content, 12, 32); $content = substr($content, 44); if ($check != md5($content)) { return false; //校验错误 } } else { $content = substr($content, 12); } return unserialize($content); //解序列化数据 } else { return false; } } //设置缓存 public function set($key, $value, $expire = 1800) { $key = md5($key); //设置索引值 $value = serialize($value); //将数据序列化 $expire = ($expire == -1) ? $expire : (time() + $expire); //过期时间 //是否开启数据校验 $check = $this->config['DB_CACHE_CHECK'] ? md5($value) : ''; $value = sprintf('%012d', $expire) . $check . $value; return $this->store($key, $value); //存储数据 } //自增1 public function inc($key, $value = 1) { return $this->set($key, intval($this->get($key)) + intval($value), -1); } //自减1 public function des($key, $value = 1) { return $this->set($key, intval($this->get($key)) - intval($value), -1); } //删除 public function del($key) { return $this->set($key, '', 0); } //清空缓存 public function clear() { return $this->_format(true); } /********** 下面是缓存算法的具体实现 **********/ //检查缓存目录 private function _checkDir() { // 如果缓存目录不存在或者不是目录,则创建缓存目录 if ((!file_exists($this->config['DB_CACHE_PATH'])) || (!is_dir($this->config['DB_CACHE_PATH']))) { //创建缓存目录 if (!@mkdir($this->config['DB_CACHE_PATH'], 0777, true)) { return false; } } //检查缓存目录是否可写,不可写则修改它的属性 if (!is_writable($this->config['DB_CACHE_PATH'])) { return @chmod($this->config['DB_CACHE_PATH'], 0777); } return true; } public function workat($file) { $this->_file = $file . '.php'; $this->_bsize_list = array( 512 => 10, 3 << 10 => 10, 8 << 10 => 10, 20 << 10 => 4, 30 << 10 => 2, 50 << 10 => 2, 80 << 10 => 2, 96 << 10 => 2, 128 << 10 => 2, 224 << 10 => 2, 256 << 10 => 2, 512 << 10 => 1, 1024 << 10 => 1, ); $this->_node_struct = array( 'next' => array(0, 'V'), 'prev' => array(4, 'V'), 'data' => array(8, 'V'), 'size' => array(12, 'V'), 'lru_right' => array(16, 'V'), 'lru_left' => array(20, 'V'), 'key' => array(24, 'H*'), ); if (!file_exists($this->_file)) { $this->create(); } else { $this->_rs = fopen($this->_file, 'rb+') or $this->trigger_error('Can\'t open the cachefile: ' . realpath($this->_file), E_USER_ERROR); $this->_seek($this->header_padding); $info = unpack('V1max_size/a*ver', fread($this->_rs, $this->info_size)); if ($info['ver'] != $this->ver) { $this->_format(true); } else { $this->max_size = $info['max_size']; } } $this->idx_node_base = $this->data_base_pos + $this->max_size; $this->_block_size_list = array_keys($this->_bsize_list); sort($this->_block_size_list); return true; } public function fetch($key, &$return) { if ($this->lock(false)) { $locked = true; } if ($this->search($key, $offset)) { $info = $this->_get_node($offset); $schema_id = $this->_get_size_schema_id($info['size']); if ($schema_id === false) { if ($locked) $this->unlock(); return false; } $this->_seek($info['data']); //去除反序列化数据 // $data = fread($this->_rs,$info['size']); // $return = unserialize($data); $return = fread($this->_rs, $info['size']); if ($return === false) { if ($locked) $this->unlock(); return false; } if ($locked) { $this->_lru_push($schema_id, $info['offset']); $this->_set_schema($schema_id, 'hits', $this->_get_schema($schema_id, 'hits') + 1); return $this->unlock(); } else { return true; } } else { if ($locked) $this->unlock(); return false; } } public function store($key, $data) { if ($this->lock(true)) { //save data //去除序列化数据 //$data = serialize($value); $size = strlen($data); //get list_idx $has_key = $this->search($key, $list_idx_offset); $schema_id = $this->_get_size_schema_id($size); if ($schema_id === false) { $this->unlock(); return false; } if ($has_key) { $hdseq = $list_idx_offset; $info = $this->_get_node($hdseq); if ($schema_id == $this->_get_size_schema_id($info['size'])) { $dataoffset = $info['data']; } else { //破掉原有lru $this->_lru_delete($info); if (!($dataoffset = $this->_dalloc($schema_id))) { $this->unlock(); return false; } $this->_free_dspace($info['size'], $info['data']); $this->_set_node($hdseq, 'lru_left', 0); $this->_set_node($hdseq, 'lru_right', 0); } $this->_set_node($hdseq, 'size', $size); $this->_set_node($hdseq, 'data', $dataoffset); } else { if (!($dataoffset = $this->_dalloc($schema_id))) { $this->unlock(); return false; } $hdseq = $this->_alloc_idx(array( 'next' => 0, 'prev' => $list_idx_offset, 'data' => $dataoffset, 'size' => $size, 'lru_right' => 0, 'lru_left' => 0, 'key' => $key, )); if ($list_idx_offset > 0) { $this->_set_node($list_idx_offset, 'next', $hdseq); } else { $this->_set_node_root($key, $hdseq); } } if ($dataoffset > $this->max_size) { $this->trigger_error('alloc datasize:' . $dataoffset, E_USER_WARNING); return false; } $this->_puts($dataoffset, $data); $this->_set_schema($schema_id, 'miss', $this->_get_schema($schema_id, 'miss') + 1); $this->_lru_push($schema_id, $hdseq); $this->unlock(); return true; } else { $this->trigger_error("Couldn't lock the file !", E_USER_WARNING); return false; } } //锁定文件 protected function lock($is_block, $whatever = false) { if ($this->config['DB_CACHE_FLOCK']) return flock($this->_rs, $is_block ? LOCK_EX : LOCK_EX + LOCK_NB); ignore_user_abort(true); $support_usleep = version_compare(PHP_VERSION, 5, '>=') ? 20 : 1; $lockfile = $this->_file . '.lck'; if (file_exists($lockfile)) { if (time() - filemtime($lockfile) > 0) { unlink($lockfile); } elseif (!$is_block) { return false; } } $lock_ex = @fopen($lockfile, 'x'); for ($i = 0; ($lock_ex === false) && ($whatever || $i < 10); $i++) { clearstatcache(); if ($support_usleep == 1) { usleep(rand(9, 999)); } else { sleep(1); } $lock_ex = @fopen($lockfile, 'x'); } return ($lock_ex !== false); } //解除文件锁定 protected function unlock() { if ($this->config['DB_CACHE_FLOCK']) return flock($this->_rs, LOCK_UN); ignore_user_abort(false); return @unlink($this->_file . '.lck'); } protected function create() { $this->_rs = @fopen($this->_file, 'wb+') or $this->trigger_error('创建缓存文件失败: ' . $this->_file, E_USER_ERROR); ; fseek($this->_rs, 0); fputs($this->_rs, '<' . '?php exit()?' . '>'); return $this->_format(); } private function _puts($offset, $data) { if ($offset < $this->max_size * 1.5) { $this->_seek($offset); return fputs($this->_rs, $data); } else { $this->trigger_error('Offset over quota:' . $offset, E_USER_ERROR); } } private function _seek($offset) { return fseek($this->_rs, $offset); } protected function delete($key, $pos = false) { if ($pos || $this->search($key, $pos)) { if ($info = $this->_get_node($pos)) { //删除data区域 if ($info['prev']) { $this->_set_node($info['prev'], 'next', $info['next']); $this->_set_node($info['next'], 'prev', $info['prev']); } else { //改入口位置 $this->_set_node($info['next'], 'prev', 0); $this->_set_node_root($key, $info['next']); } $this->_free_dspace($info['size'], $info['data']); $this->_lru_delete($info); $this->_free_node($pos); return $info['prev']; } } return false; } /** * search * 查找指定的key * 如果找到节点则$pos=节点本身 返回true * 否则 $pos=树的末端 返回false * * @param mixed $key * @access public * @return void */ protected function search($key, &$pos) { return $this->_get_pos_by_key($this->_get_node_root($key), $key, $pos); } private function _get_size_schema_id($size) { foreach ($this->_block_size_list as $k => $block_size) { if ($size <= $block_size) { return $k; } } return false; } private function _parse_str_size($str_size, $default) { if (preg_match('/^([0-9]+)\s*([gmk]|)$/i', $str_size, $match)) { switch (strtolower($match[2])) { case 'g': if ($match[1] > 1) { $this->trigger_error('Max cache size 1G', E_USER_ERROR); } $size = $match[1] << 30; break; case 'm': $size = $match[1] << 20; break; case 'k': $size = $match[1] << 10; break; default: $size = $match[1]; } if ($size <= 0) { $this->trigger_error('Error cache size ' . $this->max_size, E_USER_ERROR); return false; } elseif ($size < 10485760) { return 10485760; } else { return $size; } } else { return $default; } } private function _format($truncate = false) { if ($this->lock(true, true)) { if ($truncate) { $this->_seek(0); ftruncate($this->_rs, $this->idx_node_base); } $this->max_size = $this->_parse_str_size($this->config['DB_CACHE_SIZE'], 15728640); //default:15m $this->_puts($this->header_padding, pack('V1a*', $this->max_size, $this->ver)); ksort($this->_bsize_list); $ds_offset = $this->data_base_pos; $i = 0; foreach ($this->_bsize_list as $size => $count) { //将预分配的空间注册到free链表里 $count *= min(3, floor($this->max_size / 10485760)); $next_free_node = 0; for ($j = 0; $j < $count; $j++) { $this->_puts($ds_offset, pack('V', $next_free_node)); $next_free_node = $ds_offset; $ds_offset+=intval($size); } $code = pack(str_repeat('V1', count($this->schema_struct)), $size, $next_free_node, 0, 0, 0, 0); $this->_puts(60 + $i * $this->schema_item_size, $code); $i++; } $this->_set_dcur_pos($ds_offset); $this->_puts($this->idx_base_pos, str_repeat("\0", 262144)); $this->_puts($this->idx_seq_pos, pack('V', 1)); $this->unlock(); return true; } else { $this->trigger_error("Couldn't lock the file !", E_USER_ERROR); return false; } } private function _get_node_root($key) { $this->_seek(hexdec(substr($key, 0, 4)) * 4 + $this->idx_base_pos); $a = fread($this->_rs, 4); list(, $offset) = unpack('V', $a); return $offset; } private function _set_node_root($key, $value) { return $this->_puts(hexdec(substr($key, 0, 4)) * 4 + $this->idx_base_pos, pack('V', $value)); } private function _set_node($pos, $key, $value) { if (!$pos) { return false; } if (isset($this->_node_struct[$key])) { return $this->_puts($pos * $this->idx_node_size + $this->idx_node_base + $this->_node_struct[$key][0], pack($this->_node_struct[$key][1], $value)); } else { return false; } } private function _get_pos_by_key($offset, $key, &$pos) { if (!$offset) { $pos = 0; return false; } $info = $this->_get_node($offset); if ($info['key'] == $key) { $pos = $info['offset']; return true; } elseif ($info['next'] && $info['next'] != $offset) { return $this->_get_pos_by_key($info['next'], $key, $pos); } else { $pos = $offset; return false; } } private function _lru_delete($info) { if ($info['lru_right']) { $this->_set_node($info['lru_right'], 'lru_left', $info['lru_left']); } else { $this->_set_schema($this->_get_size_schema_id($info['size']), 'lru_tail', $info['lru_left']); } if ($info['lru_left']) { $this->_set_node($info['lru_left'], 'lru_right', $info['lru_right']); } else { $this->_set_schema($this->_get_size_schema_id($info['size']), 'lru_head', $info['lru_right']); } return true; } private function _lru_push($schema_id, $offset) { $lru_head = $this->_get_schema($schema_id, 'lru_head'); $lru_tail = $this->_get_schema($schema_id, 'lru_tail'); if ((!$offset) || ($lru_head == $offset)) return; $info = $this->_get_node($offset); $this->_set_node($info['lru_right'], 'lru_left', $info['lru_left']); $this->_set_node($info['lru_left'], 'lru_right', $info['lru_right']); $this->_set_node($offset, 'lru_right', $lru_head); $this->_set_node($offset, 'lru_left', 0); $this->_set_node($lru_head, 'lru_left', $offset); $this->_set_schema($schema_id, 'lru_head', $offset); if ($lru_tail == 0) { $this->_set_schema($schema_id, 'lru_tail', $offset); } elseif ($lru_tail == $offset && $info['lru_left']) { $this->_set_schema($schema_id, 'lru_tail', $info['lru_left']); } return true; } private function _get_node($offset) { $this->_seek($offset * $this->idx_node_size + $this->idx_node_base); $info = unpack('V1next/V1prev/V1data/V1size/V1lru_right/V1lru_left/H*key', fread($this->_rs, $this->idx_node_size)); $info['offset'] = $offset; return $info; } private function _lru_pop($schema_id) { if ($node = $this->_get_schema($schema_id, 'lru_tail')) { $info = $this->_get_node($node); if (!$info['data']) { return false; } $this->delete($info['key'], $info['offset']); if (!$this->_get_schema($schema_id, 'free')) { $this->trigger_error('pop lru,But nothing free...', E_USER_ERROR); } return $info; } else { return false; } } private function _dalloc($schema_id, $lru_freed = false) { if ($free = $this->_get_schema($schema_id, 'free')) { //如果lru里有链表 $this->_seek($free); list(, $next) = unpack('V', fread($this->_rs, 4)); $this->_set_schema($schema_id, 'free', $next); return $free; } elseif ($lru_freed) { $this->trigger_error('Bat lru poped freesize', E_USER_ERROR); return false; } else { $ds_offset = $this->_get_dcur_pos(); $size = $this->_get_schema($schema_id, 'size'); if ($size + $ds_offset > $this->max_size) { if ($info = $this->_lru_pop($schema_id)) { return $this->_dalloc($schema_id, $info); } else { $this->trigger_error('Can\'t alloc dataspace', E_USER_ERROR); return false; } } else { $this->_set_dcur_pos($ds_offset + $size); return $ds_offset; } } } private function _get_dcur_pos() { $this->_seek($this->dfile_cur_pos); list(, $ds_offset) = unpack('V', fread($this->_rs, 4)); return $ds_offset; } private function _set_dcur_pos($pos) { return $this->_puts($this->dfile_cur_pos, pack('V', $pos)); } private function _free_dspace($size, $pos) { if ($pos > $this->max_size) { $this->trigger_error('free dspace over quota:' . $pos, E_USER_ERROR); return false; } $schema_id = $this->_get_size_schema_id($size); if ($free = $this->_get_schema($schema_id, 'free')) { $this->_puts($free, pack('V1', $pos)); } else { $this->_set_schema($schema_id, 'free', $pos); } $this->_puts($pos, pack('V1', 0)); } private function _dfollow($pos, &$c) { $c++; $this->_seek($pos); list(, $next) = unpack('V1', fread($this->_rs, 4)); if ($next) { return $this->_dfollow($next, $c); } else { return $pos; } } private function _free_node($pos) { $this->_seek($this->idx_free_pos); list(, $prev_free_node) = unpack('V', fread($this->_rs, 4)); $this->_puts($pos * $this->idx_node_size + $this->idx_node_base, pack('V', $prev_free_node) . str_repeat("\0", $this->idx_node_size - 4)); return $this->_puts($this->idx_free_pos, pack('V', $pos)); } private function _alloc_idx($data) { $this->_seek($this->idx_free_pos); list(, $list_pos) = unpack('V', fread($this->_rs, 4)); if ($list_pos) { $this->_seek($list_pos * $this->idx_node_size + $this->idx_node_base); list(, $prev_free_node) = unpack('V', fread($this->_rs, 4)); $this->_puts($this->idx_free_pos, pack('V', $prev_free_node)); } else { $this->_seek($this->idx_seq_pos); list(, $list_pos) = unpack('V', fread($this->_rs, 4)); $this->_puts($this->idx_seq_pos, pack('V', $list_pos + 1)); } return $this->_create_node($list_pos, $data); } private function _create_node($pos, $data) { $this->_puts($pos * $this->idx_node_size + $this->idx_node_base , pack('V1V1V1V1V1V1H*', $data['next'], $data['prev'], $data['data'], $data['size'], $data['lru_right'], $data['lru_left'], $data['key'])); return $pos; } private function _set_schema($schema_id, $key, $value) { $info = array_flip($this->schema_struct); return $this->_puts(60 + $schema_id * $this->schema_item_size + $info[$key] * 4, pack('V', $value)); } private function _get_schema($id, $key) { $info = array_flip($this->schema_struct); $this->_seek(60 + $id * $this->schema_item_size); unpack('V1' . implode('/V1', $this->schema_struct), fread($this->_rs, $this->schema_item_size)); $this->_seek(60 + $id * $this->schema_item_size + $info[$key] * 4); list(, $value) = unpack('V', fread($this->_rs, 4)); return $value; } private function _all_schemas() { $schema = array(); for ($i = 0; $i < 16; $i++) { $this->_seek(60 + $i * $this->schema_item_size); $info = unpack('V1' . implode('/V1', $this->schema_struct), fread($this->_rs, $this->schema_item_size)); if ($info['size']) { $info['id'] = $i; $schema[$i] = $info; } else { return $schema; } } } protected function schemaStatus() { $return = array(); foreach ($this->_all_schemas() as $k => $schemaItem) { if ($schemaItem['free']) { $this->_dfollow($schemaItem['free'], $schemaItem['freecount']); } $return[] = $schemaItem; } return $return; } protected function status(&$curBytes, &$totalBytes) { $totalBytes = $curBytes = 0; $hits = $miss = 0; $schemaStatus = $this->schemaStatus(); $totalBytes = $this->max_size; $freeBytes = $this->max_size - $this->_get_dcur_pos(); foreach ($schemaStatus as $schema) { $freeBytes+=$schema['freecount'] * $schema['size']; $miss += $schema['miss']; $hits += $schema['hits']; } $curBytes = $totalBytes - $freeBytes; $return[] = array('name' => '缓存命中', 'value' => $hits); $return[] = array('name' => '缓存未命中', 'value' => $miss); return $return; } protected function trigger_error($errstr, $errno) { throw new Exception($errstr); //输出错误信息 } }