PageRenderTime 48ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/aoliz/core/include/secache.php

http://phpfor.googlecode.com/
PHP | 680 lines | 567 code | 81 blank | 32 comment | 57 complexity | 097c21d4fe52d19f13d5205a283f83db MD5 | raw file
  1. <?php
  2. /**
  3. * fcache
  4. * ??bs 16m
  5. * ??16???
  6. *
  7. * @package
  8. * @version $Id: secache.php 38548 2009-12-28 03:42:28Z flaboy $
  9. * @copyright 2003-2007 ShopEx
  10. * @author Wanglei <flaboy@shopex.cn>
  11. * @license Commercial
  12. * [20][20][384][16*16*16*4]
  13. */
  14. if(!defined('SECACHE_SIZE')){
  15. define('SECACHE_SIZE','15M');
  16. }
  17. class secache extends cachemgr{
  18. var $idx_node_size = 40;
  19. var $data_base_pos = 262588; //40+20+24*16+16*16*16*16*4;
  20. var $schema_item_size = 24;
  21. var $header_padding = 20; //???? ??php??????
  22. var $info_size = 20; //???? 4+16 maxsize|ver
  23. //40? ??20??????
  24. var $idx_seq_pos = 40; //id ???????
  25. var $dfile_cur_pos = 44; //id ???????
  26. var $idx_free_pos = 48; //id ????????
  27. var $idx_base_pos = 444; //40+20+24*16
  28. var $min_size = 10240; //10M???
  29. var $schema_struct = array('size','free','lru_head','lru_tail','hits','miss');
  30. var $ver = '$Rev: 38548 $';
  31. var $name = 'secache';
  32. function secache(){
  33. $this->workat(HOME_DIR.'/cache/cachedata');
  34. $statfile = HOME_DIR.'/cache/cachedata.stat.php';
  35. if(file_exists($statfile)){
  36. $this->_stat_rs = fopen($statfile,'rb+');
  37. $contents = '';
  38. while (!feof($this->_stat_rs)) {
  39. $contents .= fread($this->_stat_rs, 4096);
  40. }
  41. $this->_vary_list = unserialize($contents);
  42. }else{
  43. $this->_stat_rs = fopen($statfile,'wb+');
  44. $this->_vary_list = array();
  45. }
  46. }
  47. function setModified($key){
  48. $now = time();
  49. if(is_array($key)){
  50. foreach($key as $k){
  51. $this->_vary_list[strtoupper($k)] = $now;
  52. }
  53. }else{
  54. $this->_vary_list[strtoupper($key)] = $now;
  55. }
  56. fseek($this->_stat_rs,0);
  57. ftruncate($this->_stat_rs,0);
  58. return fputs($this->_stat_rs,serialize($this->_vary_list));
  59. }
  60. function workat($file){
  61. $this->_file = $file.'.php';
  62. $this->_bsize_list = array(
  63. 512=>10,
  64. 3<<10=>10,
  65. 8<<10=>10,
  66. 20<<10=>4,
  67. 30<<10=>2,
  68. 50<<10=>2,
  69. 80<<10=>2,
  70. 96<<10=>2,
  71. 128<<10=>2,
  72. 224<<10=>2,
  73. 256<<10=>2,
  74. 512<<10=>1,
  75. 1024<<10=>1,
  76. );
  77. $this->_node_struct = array(
  78. 'next'=>array(0,'V'),
  79. 'prev'=>array(4,'V'),
  80. 'data'=>array(8,'V'),
  81. 'size'=>array(12,'V'),
  82. 'lru_right'=>array(16,'V'),
  83. 'lru_left'=>array(20,'V'),
  84. 'key'=>array(24,'H*'),
  85. );
  86. if(!file_exists($this->_file)){
  87. $this->create();
  88. }else{
  89. $this->_rs = fopen($this->_file,'rb+') or $this->trigger_error('Can\'t open the cachefile: '.realpath($this->_file),E_USER_ERROR);
  90. $this->_seek($this->header_padding);
  91. $info = unpack('V1max_size/a*ver',fread($this->_rs,$this->info_size));
  92. if($info['ver']!=$this->ver){
  93. $this->_format(true);
  94. }else{
  95. $this->max_size = $info['max_size'];
  96. }
  97. }
  98. $this->idx_node_base = $this->data_base_pos+$this->max_size;
  99. $this->_block_size_list = array_keys($this->_bsize_list);
  100. sort($this->_block_size_list);
  101. return true;
  102. }
  103. function create(){
  104. $this->_rs = fopen($this->_file,'wb+') or $this->trigger_error('Can\'t open the cachefile: '.realpath($this->_file),E_USER_ERROR);;
  105. if(!is_writable($this->_file) || !is_readable($this->_file)){
  106. chmod($this->_file,0666);
  107. }
  108. fseek($this->_rs,0);
  109. fputs($this->_rs,'<'.'?php exit()?'.'>');
  110. return $this->_format();
  111. }
  112. function _puts($offset,$data){
  113. if($offset < $this->max_size*1.5){
  114. $this->_seek($offset);
  115. return fputs($this->_rs,$data);
  116. }else{
  117. $this->trigger_error('Offset over quota:'.$offset,E_USER_ERROR);
  118. }
  119. }
  120. function _seek($offset){
  121. return fseek($this->_rs,$offset);
  122. }
  123. function clear(){
  124. return $this->_format(true);
  125. }
  126. function fetch($key,&$return){
  127. if($this->lock(false)){
  128. $locked = true;
  129. }
  130. if($this->search($key,$offset)){
  131. $info = $this->_get_node($offset);
  132. $schema_id = $this->_get_size_schema_id($info['size']);
  133. if($schema_id===false){
  134. if($locked) $this->unlock();
  135. return false;
  136. }
  137. $this->_seek($info['data']);
  138. $data = fread($this->_rs,$info['size']);
  139. $return = unserialize($data);
  140. if($return===false){
  141. if($locked) $this->unlock();
  142. return false;
  143. }
  144. if($locked){
  145. $this->_lru_push($schema_id,$info['offset']);
  146. $this->_set_schema($schema_id,'hits',$this->_get_schema($schema_id,'hits')+1);
  147. return $this->unlock();
  148. }else{
  149. return true;
  150. }
  151. }else{
  152. if($locked) $this->unlock();
  153. return false;
  154. }
  155. }
  156. /**
  157. * lock
  158. * ??flock????????????????
  159. *
  160. * @param mixed $is_block ????
  161. * @access public
  162. * @return void
  163. */
  164. function lock($is_block,$whatever=false){
  165. ignore_user_abort(true);
  166. return flock($this->_rs, $is_block?LOCK_EX:LOCK_EX+LOCK_NB);
  167. }
  168. /**
  169. * unlock
  170. * ??flock????????????????
  171. *
  172. * @access public
  173. * @return void
  174. */
  175. function unlock(){
  176. ignore_user_abort(false);
  177. return flock($this->_rs, LOCK_UN);
  178. }
  179. function delete($key,$pos=false){
  180. if($pos || $this->search($key,$pos)){
  181. if($info = $this->_get_node($pos)){
  182. //??data??
  183. if($info['prev']){
  184. $this->_set_node($info['prev'],'next',$info['next']);
  185. $this->_set_node($info['next'],'prev',$info['prev']);
  186. }else{ //?????
  187. $this->_set_node($info['next'],'prev',0);
  188. $this->_set_node_root($key,$info['next']);
  189. }
  190. $this->_free_dspace($info['size'],$info['data']);
  191. $this->_lru_delete($info);
  192. $this->_free_node($pos);
  193. return $info['prev'];
  194. }
  195. }
  196. return false;
  197. }
  198. function store($key,$value){
  199. if($this->lock(true)){
  200. //save data
  201. $data = serialize($value);
  202. $size = strlen($data);
  203. //get list_idx
  204. $has_key = $this->search($key,$list_idx_offset);
  205. $schema_id = $this->_get_size_schema_id($size);
  206. if($schema_id===false){
  207. $this->unlock();
  208. return false;
  209. }
  210. if($has_key){
  211. $hdseq = $list_idx_offset;
  212. $info = $this->_get_node($hdseq);
  213. if($schema_id == $this->_get_size_schema_id($info['size'])){
  214. $dataoffset = $info['data'];
  215. }else{
  216. //????lru
  217. $this->_lru_delete($info);
  218. if(!($dataoffset = $this->_dalloc($schema_id))){
  219. $this->unlock();
  220. return false;
  221. }
  222. $this->_free_dspace($info['size'],$info['data']);
  223. $this->_set_node($hdseq,'lru_left',0);
  224. $this->_set_node($hdseq,'lru_right',0);
  225. }
  226. $this->_set_node($hdseq,'size',$size);
  227. $this->_set_node($hdseq,'data',$dataoffset);
  228. }else{
  229. if(!($dataoffset = $this->_dalloc($schema_id))){
  230. $this->unlock();
  231. return false;
  232. }
  233. $hdseq = $this->_alloc_idx(array(
  234. 'next'=>0,
  235. 'prev'=>$list_idx_offset,
  236. 'data'=>$dataoffset,
  237. 'size'=>$size,
  238. 'lru_right'=>0,
  239. 'lru_left'=>0,
  240. 'key'=>$key,
  241. ));
  242. if($list_idx_offset>0){
  243. $this->_set_node($list_idx_offset,'next',$hdseq);
  244. }else{
  245. $this->_set_node_root($key,$hdseq);
  246. }
  247. }
  248. if($dataoffset>$this->max_size){
  249. $this->trigger_error('alloc datasize:'.$dataoffset,E_USER_WARNING);
  250. return false;
  251. }
  252. $this->_puts($dataoffset,$data);
  253. $this->_set_schema($schema_id,'miss',$this->_get_schema($schema_id,'miss')+1);
  254. $this->_lru_push($schema_id,$hdseq);
  255. $this->unlock();
  256. return true;
  257. }else{
  258. $this->trigger_error("Couldn't lock the file !",E_USER_WARNING);
  259. return false;
  260. }
  261. }
  262. /**
  263. * search
  264. * ?????key
  265. * ???????$pos=???? ??true
  266. * ?? $pos=???? ??false
  267. *
  268. * @param mixed $key
  269. * @access public
  270. * @return void
  271. */
  272. function search($key,&$pos){
  273. return $this->_get_pos_by_key($this->_get_node_root($key),$key,$pos);
  274. }
  275. function _get_size_schema_id($size){
  276. foreach($this->_block_size_list as $k=>$block_size){
  277. if($size <= $block_size){
  278. return $k;
  279. }
  280. }
  281. return false;
  282. }
  283. function _parse_str_size($str_size,$default){
  284. if(preg_match('/^([0-9]+)\s*([gmk]|)$/i',$str_size,$match)){
  285. switch(strtolower($match[2])){
  286. case 'g':
  287. if($match[1]>1){
  288. $this->trigger_error('Max cache size 1G',E_USER_ERROR);
  289. }
  290. $size = $match[1]<<30;
  291. break;
  292. case 'm':
  293. $size = $match[1]<<20;
  294. break;
  295. case 'k':
  296. $size = $match[1]<<10;
  297. break;
  298. default:
  299. $size = $match[1];
  300. }
  301. if($size<=0){
  302. $this->trigger_error('Error cache size '.$this->max_size,E_USER_ERROR);
  303. return false;
  304. }elseif($size<10485760){
  305. return 10485760;
  306. }else{
  307. return $size;
  308. }
  309. }else{
  310. return $default;
  311. }
  312. }
  313. function _format($truncate=false){
  314. if($this->lock(true,true)){
  315. if($truncate){
  316. $this->_seek(0);
  317. ftruncate($this->_rs,$this->idx_node_base);
  318. }
  319. $this->max_size = $this->_parse_str_size(SECACHE_SIZE,15728640); //default:15m
  320. $this->_puts($this->header_padding,pack('V1a*',$this->max_size,$this->ver));
  321. ksort($this->_bsize_list);
  322. $ds_offset = $this->data_base_pos;
  323. $i=0;
  324. foreach($this->_bsize_list as $size=>$count){
  325. //??????????free???
  326. $count *= min(3,floor($this->max_size/10485760));
  327. $next_free_node = 0;
  328. for($j=0;$j<$count;$j++){
  329. $this->_puts($ds_offset,pack('V',$next_free_node));
  330. $next_free_node = $ds_offset;
  331. $ds_offset+=intval($size);
  332. }
  333. $code = pack(str_repeat('V1',count($this->schema_struct)),$size,$next_free_node,0,0,0,0);
  334. $this->_puts(60+$i*$this->schema_item_size,$code);
  335. $i++;
  336. }
  337. $this->_set_dcur_pos($ds_offset);
  338. $this->_puts($this->idx_base_pos,str_repeat("\0",262144));
  339. $this->_puts($this->idx_seq_pos,pack('V',1));
  340. $this->unlock();
  341. return true;
  342. }else{
  343. $this->trigger_error("Couldn't lock the file !",E_USER_ERROR);
  344. return false;
  345. }
  346. }
  347. function _get_node_root($key){
  348. $this->_seek(hexdec(substr($key,0,4))*4+$this->idx_base_pos);
  349. $a= fread($this->_rs,4);
  350. list(,$offset) = unpack('V',$a);
  351. return $offset;
  352. }
  353. function _set_node_root($key,$value){
  354. return $this->_puts(hexdec(substr($key,0,4))*4+$this->idx_base_pos,pack('V',$value));
  355. }
  356. function _set_node($pos,$key,$value){
  357. if(!$pos){
  358. return false;
  359. }
  360. if(isset($this->_node_struct[$key])){
  361. return $this->_puts($pos*$this->idx_node_size+$this->idx_node_base+$this->_node_struct[$key][0],pack($this->_node_struct[$key][1],$value));
  362. }else{
  363. return false;
  364. }
  365. }
  366. function _get_pos_by_key($offset,$key,&$pos){
  367. if(!$offset){
  368. $pos = 0;
  369. return false;
  370. }
  371. $info = $this->_get_node($offset);
  372. if($info['key']==$key){
  373. $pos = $info['offset'];
  374. return true;
  375. }elseif($info['next'] && $info['next']!=$offset){
  376. return $this->_get_pos_by_key($info['next'],$key,$pos);
  377. }else{
  378. $pos = $offset;
  379. return false;
  380. }
  381. }
  382. function _lru_delete($info){
  383. if($info['lru_right']){
  384. $this->_set_node($info['lru_right'],'lru_left',$info['lru_left']);
  385. }else{
  386. $this->_set_schema($this->_get_size_schema_id($info['size']),'lru_tail',$info['lru_left']);
  387. }
  388. if($info['lru_left']){
  389. $this->_set_node($info['lru_left'],'lru_right',$info['lru_right']);
  390. }else{
  391. $this->_set_schema($this->_get_size_schema_id($info['size']),'lru_head',$info['lru_right']);
  392. }
  393. return true;
  394. }
  395. function _lru_push($schema_id,$offset){
  396. $lru_head = $this->_get_schema($schema_id,'lru_head');
  397. $lru_tail = $this->_get_schema($schema_id,'lru_tail');
  398. if((!$offset) || ($lru_head==$offset))return;
  399. $info = $this->_get_node($offset);
  400. $this->_set_node($info['lru_right'],'lru_left',$info['lru_left']);
  401. $this->_set_node($info['lru_left'],'lru_right',$info['lru_right']);
  402. $this->_set_node($offset,'lru_right',$lru_head);
  403. $this->_set_node($offset,'lru_left',0);
  404. $this->_set_node($lru_head,'lru_left',$offset);
  405. $this->_set_schema($schema_id,'lru_head',$offset);
  406. if($lru_tail==0){
  407. $this->_set_schema($schema_id,'lru_tail',$offset);
  408. }elseif($lru_tail==$offset && $info['lru_left']){
  409. $this->_set_schema($schema_id,'lru_tail',$info['lru_left']);
  410. }
  411. return true;
  412. }
  413. function _get_node($offset){
  414. $this->_seek($offset*$this->idx_node_size + $this->idx_node_base);
  415. $info = unpack('V1next/V1prev/V1data/V1size/V1lru_right/V1lru_left/H*key',fread($this->_rs,$this->idx_node_size));
  416. $info['offset'] = $offset;
  417. return $info;
  418. }
  419. function _lru_pop($schema_id){
  420. if($node = $this->_get_schema($schema_id,'lru_tail')){
  421. $info = $this->_get_node($node);
  422. if(!$info['data']){
  423. return false;
  424. }
  425. $this->delete($info['key'],$info['offset']);
  426. if(!$this->_get_schema($schema_id,'free')){
  427. $this->trigger_error('pop lru,But nothing free...',E_USER_ERROR);
  428. }
  429. return $info;
  430. }else{
  431. return false;
  432. }
  433. }
  434. function _dalloc($schema_id,$lru_freed=false){
  435. if($free = $this->_get_schema($schema_id,'free')){ //??lru????
  436. $this->_seek($free);
  437. list(,$next) = unpack('V',fread($this->_rs,4));
  438. $this->_set_schema($schema_id,'free',$next);
  439. return $free;
  440. }elseif($lru_freed){
  441. $this->trigger_error('Bat lru poped freesize',E_USER_ERROR);
  442. return false;
  443. }else{
  444. $ds_offset = $this->_get_dcur_pos();
  445. $size = $this->_get_schema($schema_id,'size');
  446. if($size+$ds_offset > $this->max_size){
  447. if($info = $this->_lru_pop($schema_id)){
  448. return $this->_dalloc($schema_id,$info);
  449. }else{
  450. $this->trigger_error('Can\'t alloc dataspace',E_USER_ERROR);
  451. return false;
  452. }
  453. }else{
  454. $this->_set_dcur_pos($ds_offset+$size);
  455. return $ds_offset;
  456. }
  457. }
  458. }
  459. function _get_dcur_pos(){
  460. $this->_seek($this->dfile_cur_pos);
  461. list(,$ds_offset) = unpack('V',fread($this->_rs,4));
  462. return $ds_offset;
  463. }
  464. function _set_dcur_pos($pos){
  465. return $this->_puts($this->dfile_cur_pos,pack('V',$pos));
  466. }
  467. function _free_dspace($size,$pos){
  468. if($pos>$this->max_size){
  469. $this->trigger_error('free dspace over quota:'.$pos,E_USER_ERROR);
  470. return false;
  471. }
  472. $schema_id = $this->_get_size_schema_id($size);
  473. if($free = $this->_get_schema($schema_id,'free')){
  474. $this->_puts($free,pack('V1',$pos));
  475. }else{
  476. $this->_set_schema($schema_id,'free',$pos);
  477. }
  478. $this->_puts($pos,pack('V1',0));
  479. }
  480. function _dfollow($pos,&$c){
  481. $c++;
  482. $this->_seek($pos);
  483. list(,$next) = unpack('V1',fread($this->_rs,4));
  484. if($next){
  485. return $this->_dfollow($next,$c);
  486. }else{
  487. return $pos;
  488. }
  489. }
  490. function _free_node($pos){
  491. $this->_seek($this->idx_free_pos);
  492. list(,$prev_free_node) = unpack('V',fread($this->_rs,4));
  493. $this->_puts($pos*$this->idx_node_size+$this->idx_node_base,pack('V',$prev_free_node).str_repeat("\0",$this->idx_node_size-4));
  494. return $this->_puts($this->idx_free_pos,pack('V',$pos));
  495. }
  496. function _alloc_idx($data){
  497. $this->_seek($this->idx_free_pos);
  498. list(,$list_pos) = unpack('V',fread($this->_rs,4));
  499. if($list_pos){
  500. $this->_seek($list_pos*$this->idx_node_size+$this->idx_node_base);
  501. list(,$prev_free_node) = unpack('V',fread($this->_rs,4));
  502. $this->_puts($this->idx_free_pos,pack('V',$prev_free_node));
  503. }else{
  504. $this->_seek($this->idx_seq_pos);
  505. list(,$list_pos) = unpack('V',fread($this->_rs,4));
  506. $this->_puts($this->idx_seq_pos,pack('V',$list_pos+1));
  507. }
  508. return $this->_create_node($list_pos,$data);
  509. }
  510. function _create_node($pos,$data){
  511. $this->_puts($pos*$this->idx_node_size + $this->idx_node_base
  512. ,pack('V1V1V1V1V1V1H*',$data['next'],$data['prev'],$data['data'],$data['size'],$data['lru_right'],$data['lru_left'],$data['key']));
  513. return $pos;
  514. }
  515. function _set_schema($schema_id,$key,$value){
  516. $info = array_flip($this->schema_struct);
  517. return $this->_puts(60+$schema_id*$this->schema_item_size + $info[$key]*4,pack('V',$value));
  518. }
  519. function _get_schema($id,$key){
  520. $info = array_flip($this->schema_struct);
  521. $this->_seek(60+$id*$this->schema_item_size);
  522. unpack('V1'.implode('/V1',$this->schema_struct),fread($this->_rs,$this->schema_item_size));
  523. $this->_seek(60+$id*$this->schema_item_size + $info[$key]*4);
  524. list(,$value) =unpack('V',fread($this->_rs,4));
  525. return $value;
  526. }
  527. function _all_schemas(){
  528. $schema = array();
  529. for($i=0;$i<16;$i++){
  530. $this->_seek(60+$i*$this->schema_item_size);
  531. $info = unpack('V1'.implode('/V1',$this->schema_struct),fread($this->_rs,$this->schema_item_size));
  532. if($info['size']){
  533. $info['id'] = $i;
  534. $schema[$i] = $info;
  535. }else{
  536. return $schema;
  537. }
  538. }
  539. }
  540. function schemaStatus(){
  541. $return = array();
  542. foreach($this->_all_schemas() as $k=>$schemaItem){
  543. if($schemaItem['free']){
  544. $this->_dfollow($schemaItem['free'],$schemaItem['freecount']);
  545. }
  546. $return[] = $schemaItem;
  547. }
  548. return $return;
  549. }
  550. function status(&$curBytes,&$totalBytes){
  551. $totalBytes = $curBytes = 0;
  552. $hits = $miss = 0;
  553. $schemaStatus = $this->schemaStatus();
  554. $totalBytes = $this->max_size;
  555. $freeBytes = $this->max_size - $this->_get_dcur_pos();
  556. foreach($schemaStatus as $schema){
  557. $freeBytes+=$schema['freecount']*$schema['size'];
  558. $miss += $schema['miss'];
  559. $hits += $schema['hits'];
  560. }
  561. $curBytes = $totalBytes-$freeBytes;
  562. $return[] = array('name'=>__('????'),'value'=>$hits);
  563. $return[] = array('name'=>__('?????'),'value'=>$miss);
  564. return $return;
  565. }
  566. function trigger_error($errstr,$errno){
  567. if($errno==E_USER_ERROR){
  568. if(!$this->_in_fatal_error){
  569. $this->_in_fatal_error = true;
  570. $this->_format(true);
  571. }
  572. header('HTTP/1.1 500 Internal Server Error',true,500);
  573. if(function_exists('debug_print_backtrace')){
  574. echo '<h1>'.$errstr.'</h1><hr />';
  575. echo '<pre>';
  576. debug_print_backtrace();
  577. echo '</pre>';
  578. exit;
  579. }else{
  580. trigger_error($errstr,$errno);
  581. }
  582. }else{
  583. trigger_error($errstr,$errno);
  584. }
  585. }
  586. }