phpcms 源码分析五:文件缓存实现
这次是逆雪寒的文件缓存实现代码分析:
1 /* 2 [/php] 3 4 PHPCMS的文本缓存实现: 5 6 [php] 7 <?php 8 9 /* 10 这个文件里面全是有关生成文本缓存的函数。文本缓存是个好东西。一般的项目,我们用不着内存缓存 : memcached ,文本搞定。 11 原理是这样的: 我们在后台是不是可以设置很多有关网站的参数。而这些参数很多都是固定的。就不变化的。都存到咱的数据库上。而我们程序那里呢 12 每次都要访问数据库读出参数来进行我们程序中的操作。首先数据库查询是个很耗硬盘IO资源的一个东西,所以文本缓存刚好能减轻数据库那边的承重。 13 我们在程序开始就把数据库里面的配置都转化为数组 等 放到 php文件里面。这样我们可以直接访问php文件而不用每次都访问数据库了。 14 php文本缓存其实成了我们程序和数据库的一个中间件。 所以我们自己写自己的文本缓存的时候其实要实现的很简单: 15 读数据库 -> 写到PHP文件 -> 程序中include ;来吧。开始文本缓存学习 16 */ 17 18 defined('IN_PHPCMS') or exit('Access Denied'); 19 20 // 生成所有缓存的总操作函数 21 function cache_all() 22 { 23 // 生成所有的数据库表名,表名是根据数据库里面当前的表名而生成。请看这个函数的详细分析 24 cache_table(); 25 26 // 包含表常量 27 require_once PHPCMS_CACHEDIR.'table.php'; 28 29 cache_common(); 30 31 cache_member_group(); 32 33 $modules = cache_module(); 34 35 $channelids = cache_channel(0); 36 37 $keyids = array_merge($modules, $channelids); 38 39 foreach($keyids as $keyid) 40 { 41 $catids = cache_categorys($keyid); 42 43 if(is_array($catids)) 44 { 45 foreach($catids as $catid) 46 { 47 cache_category($catid); 48 } 49 } 50 } 51 52 cache_type(0); 53 54 return TRUE; 55 } 56 57 function cache_common() 58 { 59 global $db; 60 61 // 查询所有能用的模块 62 $query = $db->query("SELECT module,name,iscore,iscopy,isshare,moduledir,moduledomain FROM " 63 .TABLE_MODULE." WHERE disabled=0"); 64 65 while($r = $db->fetch_array($query)) 66 { 67 $r['linkurl'] = ''; 68 69 // 如果模块存在目录的就取它目录地址 70 if($r['module'] != 'phpcms' && $r['iscopy'] == 0) { 71 $r['linkurl'] = linkurl($r['moduledomain'] 72 ? dir_path($r['moduledomain']) 73 : $r['moduledir'].'/'); 74 } 75 76 unset($r['moduledomain']); 77 78 $key = $r['module']; 79 80 $data[$key] = $r; 81 } 82 83 // 存到缓存数组,等一下一起把 $CACHE 数组写到文本里去 84 $CACHE['module'] = $data; 85 86 $data = array(); 87 88 // 罗列能用的频道列表 89 $query = $db->query("SELECT channelid, module, channelname, channeldir, channeldomain, 90 channelpic, introduce, style, islink, linkurl, cat_html_urlruleid, 91 item_html_urlruleid, special_html_urlruleid, cat_php_urlruleid, 92 item_php_urlruleid, special_php_urlruleid FROM ".TABLE_CHANNEL 93 ." WHERE disabled=0 ORDER by listorder"); 94 95 while($r = $db->fetch_array($query)) 96 { 97 $r['linkurl'] = linkurl($r['linkurl']); 98 99 $key = $r['channelid']; 100 101 $data[$key] = $r; 102 } 103 104 // 存到缓存数组 105 $CACHE['channel'] = $data; 106 107 $data = array(); 108 109 // 查询 phpcms这个模块的设置信息,大家可以看下数据库这个表内容。setting 字段里面的信息是经过serialize 函数串行化的 110 $r = $db->get_one("SELECT setting FROM ".TABLE_MODULE." WHERE module='phpcms'"); 111 112 /* 113 * 所以取出的内容要unserialize 反串行.我是挺喜欢使用serialize 函数的。 114 * 他可以实现把一个数组存到数据库或把一个对象存到数据库。或是拿来GET传递都行。 115 * 太强了。大家可以试用下。可能你项目某个地方需要用到哦。 116 */ 117 $CACHE['phpcms'] = unserialize($r['setting']); 118 119 $fields = array(); 120 121 // 下载模块的信息,请自己看下这个表的数据就明白 122 $result = $db->query("SELECT * FROM ".TABLE_FIELD." ORDER BY fieldid"); 123 124 while($r = $db->fetch_array($result)) 125 { 126 $tablename = $r['tablename']; 127 128 $fields[$tablename] .= ','.$r['name']; 129 } 130 131 $CACHE['field'] = $fields; 132 133 // 开始把$CACHE 数组写到 common.php 这个文本缓存里。大家可以自己去打开这个文件看下内容。一切了然 134 cache_write('common.php', $CACHE); 135 136 return $CACHE; 137 } 138 139 // 更新文本缓存。最好在后台操作使用。因为PHP的文件flock 文件锁在某些平台使用不是很好。会出现多用户同写一个文件从而破坏缓存文件 140 function cache_update($action = '') 141 { 142 global $db; 143 144 $data=array(); 145 146 switch($action) 147 { 148 case 'keylink': 149 $query=$db->query("SELECT linktext,linkurl FROM ".TABLE_KEYLINK." where passed=1"); 150 151 while($r=$db->fetch_array($query)) 152 { 153 $data[]=$r; 154 } 155 break; 156 157 case 'reword': 158 $query = $db->query("SELECT word,replacement FROM ".TABLE_REWORD." where passed=1"); 159 160 while($r = $db->fetch_array($query)) 161 { 162 $data[]=$r; 163 } 164 break; 165 166 default: 167 $actions = array('keylink','reword'); 168 169 array_map('cache_update', $actions); 170 171 return TRUE; 172 } 173 174 cache_write('cache_'.$action.'.php', $data); 175 176 return $data; 177 } 178 179 function cache_table() 180 { 181 global $db,$CONFIG; 182 183 /* 184 显示数据库里面的所有表名 185 */ 186 $query = $db->query("SHOW TABLES FROM `".$CONFIG['dbname']."`"); 187 188 189 while($r = $db->fetch_row($query)) 190 { 191 $table = $r[0]; 192 193 // 寻找表前缀等于 $CONFIG['tablepre'] (在config.inc.php里设置) @@表前缀还有这个作用 嘿嘿 194 if(preg_match("/^".$CONFIG['tablepre']."/i", $table)) 195 { 196 $tablename = str_replace($CONFIG['tablepre'], 'table_', $table); 197 198 // $data['table_xx'] = xx; 形式 只能意会下了 199 $data[$tablename] = $table; 200 } 201 } 202 203 // $db->free_result() 这个类方法其实是调用了函数:mysql_free_result() 函数 204 // 主要是为了清除数据库大量的查询而占用的内存。还是有必要的哦 205 $db->free_result($query); 206 207 // 常量 PHPCMS_CACHEDIR 在 common.inc.php 里面定义的。大家不记得了去看看吧。 208 // 是存放phpcms 缓存目录的路径,这里意思是:如果缓存目录不存在 209 if(!is_dir(PHPCMS_CACHEDIR)) 210 { 211 // 如果缓存目录不存在那么就创建 212 dir_create(PHPCMS_CACHEDIR); 213 214 // 创建编译后的PHP模板目录,有关phpcms模板引擎编写。在下一章合适就开讲 215 dir_create($CONFIG['templatescachedir']); 216 /* 217 dir_create() 函数为创建 目录函数。PHPCMS自己封装的,刚看了下。phpcms 挺强。 218 这个函数还可以通过ftp 来创建目录。这样就可以解决一些 开启了安全模式下的服务器对于创建目录等出现的问题 219 因为涉及到PHP FTP 知识。所以打算讲解到下面再说。 220 */ 221 } 222 223 /* 224 cache_write() 函数在global.func.php里面定义的。是把 已经从数据库取出来的数组信息写到 PHP文本上去。 225 @@文本缓存关键的一步 废话少说上菜: 226 */ 227 cache_write('table.php', $data , 'constant'); //很多朋友说找不到phpcms 表常量在那里定义的。就是在这里。 228 229 function cache_write($file, $string, $type = 'array') 230 { 231 // 检测 $string 内容是字符串的呢还是数组的,是数组的那就继续 .. 232 if(is_array($string)) 233 { 234 $type = strtolower($type); 235 236 // 然后再判断这个函数的模式标志 ,是否为数组模式,默认为数组模式 237 if($type == 'array') 238 { 239 /*这个太关键了。因为我们把数据库的信息写到文本上去的时候。是以符合PHP语法的格式写进去的。为什么呢?@@ 240 * 十分废话,因为如果不是以PHP格式写到文件里面去,那么这个PHP文件怎么能给我们include 进程序运行调用呢? 241 * 呵呵。 知道这一点就真的明白文本缓存的实现了。忒简单。 这里使用了个小技巧:使用了 var_export() 函数 242 * 这个函数会返回一个变量的字符串形式。这个函数太有帮助了。如果没有这个函数,我们还要自己想办法实现呢。 243 * 自己写一次文本缓存就明白了。会碰到这个问题的。 '\n' 这个是文本文件的换行。初学者 别把<br> 和 '\n' 搞混罗。 244 * 一个是html 的 一个是文本文件的。 245 */ 246 $string = "<?php\n return ".var_export($string,TRUE).";\n?>"; 247 } 248 249 // 以内容形式 250 elseif($type == 'constant') 251 { 252 $data=''; 253 254 foreach($string as $key => $value) $data .= "define('".strtoupper($key)."','".addslashes($value)."');\n"; 255 256 // 如果以内容形式的话。就不是写数组到文本里面了。而是把内容都定义成常量。 257 $string = "<?php\n".$data."\n?>"; 258 } 259 } 260 261 // file_put_contents()函数 是PHP5才支持的 效率最好。建议使用 262 $strlen = file_put_contents(PHPCMS_CACHEDIR.$file, $string); 263 264 // 设置目录 为可读可写可执行 265 chmod(PHPCMS_CACHEDIR.$file, 0777); 266 267 // 返回写到文本的字节数 268 return $strlen; 269 } 270 271 // 再说多一个读 缓存文件的操作函数 :上菜 272 function cache_read($file, $mode = 'i') 273 { 274 $cachefile = PHPCMS_CACHEDIR.$file; 275 276 if(!file_exists($cachefile)) { 277 return array(); 278 } 279 280 return $mode == 'i' ? include $cachefile : file_get_contents($cachefile); 281 } 282 283 // 读缓存其实就是 include php 缓存文件。 讲完走人 284 285 return $data; 286 } 287 /* 288 phpcms 的所有数据库表名 都用根据数据库当前的表名来用常量来进行定义。我认为这样设计不是很好。 289 不够灵活:比如如果我们更改数据库的一个表名的话。那么会出现找不到表的错误信息。 290 而且想要修复还很麻烦。就是说不能随便更改表名了。不推荐大家这样写。我们可以把表名都定义在一个PHP文件里面。 291 这样我们以后要改某个表名,就很方便了。 292 */ 293 294 function cache_module($module = '') 295 { 296 global $db; 297 if($module) 298 { 299 // 模块具体信息 300 $r = $db->get_one("SELECT setting,module,name,iscopy,moduledir,moduledomain FROM " 301 .TABLE_MODULE." WHERE module='$module'"); 302 303 if($r['setting']) 304 { 305 // 讲过了反串行。因为里面信息是串行化后再存到数据库的 306 $setting = unserialize($r['setting']); 307 } 308 309 $setting['name'] = $r['name']; 310 311 $setting['moduledir'] = $r['moduledir']; 312 313 $setting['moduledomain'] = $r['moduledomain']; 314 315 $setting['linkurl'] = ''; 316 317 if($r['module'] != 'phpcms' && $r['iscopy'] == 0) 318 { 319 $setting['linkurl'] = linkurl($r['moduledomain'] 320 ? dir_path($r['moduledomain']) 321 : $r['moduledir'].'/'); 322 323 cache_categorys($module); 324 } 325 326 unset($r['moduledomain']); 327 328 cache_write($module.'_setting.php', $setting); 329 330 return $setting; 331 } 332 333 else 334 { 335 $query = $db->query("SELECT module FROM ".TABLE_MODULE 336 ." WHERE disabled=0 ORDER by moduleid"); 337 338 while($r = $db->fetch_array($query)) 339 { 340 cache_module($r['module']); 341 342 $modules[] = $r['module']; 343 } 344 345 return $modules; 346 } 347 } 348 349 function cache_channel($channelid = 0) 350 { 351 global $db; 352 353 if($channelid) 354 { 355 $data = $db->get_one("SELECT * FROM ".TABLE_CHANNEL 356 ." WHERE channelid=$channelid"); 357 358 if($data && !$data['islink']) 359 { 360 if($data['setting']) 361 { 362 $setting = unserialize($data['setting']); 363 unset($data['setting']); 364 $data = is_array($setting) ? array_merge($data, $setting) : $data; 365 } 366 367 $data['linkurl'] = linkurl($data['linkurl']); 368 369 cache_write('channel_'.$channelid.'.php', $data); 370 371 cache_categorys($channelid); 372 373 return $data; 374 } 375 } 376 377 else 378 { 379 $query = $db->query("SELECT channelid FROM ".TABLE_CHANNEL 380 ." WHERE islink=0 AND disabled=0 ORDER by channelid"); 381 382 while($r = $db->fetch_array($query)) 383 { 384 cache_channel($r['channelid']); 385 386 $channelids[] = $r['channelid']; 387 } 388 389 return $channelids; 390 } 391 } 392 393 function cache_categorys($keyid) 394 { 395 global $db, $PHPCMS, $CHANNEL; 396 397 $urlpre = ''; 398 399 if(is_numeric($keyid)) 400 { 401 $keyid = intval($keyid); 402 403 $module = $CHANNEL[$keyid]['module']; 404 405 $sql = " channelid=$keyid "; 406 } 407 408 else 409 { 410 $sql = " module='$keyid' "; 411 } 412 413 $catids = $data = array(); 414 415 $query = $db->query("SELECT module,channelid,catid,catname,style,introduce,catpic,islink,catdir, 416 linkurl,parentid,arrparentid,parentdir,child,arrchildid,items,itemordertype, 417 itemtarget,ismenu,islist,ishtml,htmldir,prefix,urlruleid,item_prefix,item_html_urlruleid, 418 item_php_urlruleid FROM ".TABLE_CATEGORY." WHERE $sql ORDER by listorder,catid"); 419 420 while($r = $db->fetch_array($query)) 421 { 422 $r['linkurl'] = str_replace($PHPCMS['index'].'.'.$PHPCMS['fileext'], '', $r['linkurl']); 423 424 $r['linkurl'] = $urlpre 425 ? preg_replace("|^".$urlpre."|", '', $r['linkurl']) 426 : linkurl($r['linkurl']); 427 428 $catid = $r['catid']; 429 430 $data[$catid] = $r; 431 432 $catids[] = $catid; 433 } 434 435 // 写缓存罗。 436 if($data) { 437 cache_write('categorys_'.$keyid.'.php', $data); 438 } 439 440 return $catids; 441 } 442 443 function cache_category($catid) 444 { 445 global $db,$PHPCMS; 446 447 if(!$catid) { 448 return FALSE; 449 } 450 451 $data = $db->get_one("SELECT * FROM ".TABLE_CATEGORY 452 ." WHERE catid=$catid"); 453 454 $setting = unserialize($data['setting']); 455 456 unset($data['setting']); 457 458 $data = is_array($setting) ? array_merge($data, $setting) : $data; 459 460 $data['linkurl'] = linkurl(str_replace($PHPCMS['index'].'.'.$PHPCMS['fileext'], '', $data['linkurl'])); 461 462 cache_write('category_'.$catid.'.php', $data); 463 464 return $data; 465 } 466 467 function cache_type($keyid=0) 468 { 469 global $db; 470 471 if($keyid) 472 { 473 $result = $db->query("SELECT * FROM ".TABLE_TYPE 474 ." WHERE keyid='$keyid'"); 475 $data = array(); 476 477 while($r = $db->fetch_array($result)) 478 { 479 $r['introduce'] = $r['introduce'] 480 ? $r['introduce'] 481 : ' '; 482 483 $data[$r['typeid']] = $r; 484 } 485 486 if($data) 487 { 488 cache_write('type_'.$keyid.'.php', $data); 489 } 490 491 return $data; 492 } 493 else 494 { 495 $modules = array(); 496 497 $query = $db->query("SELECT module FROM ".TABLE_MODULE 498 ." WHERE disabled=0 ORDER by moduleid"); 499 500 while($r = $db->fetch_array($query)) 501 { 502 $modules[] = $r['module']; 503 } 504 505 $channelids = array(); 506 507 $query = $db->query("SELECT channelid FROM ".TABLE_CHANNEL 508 ." WHERE islink=0 AND disabled=0 ORDER by channelid"); 509 510 while($r = $db->fetch_array($query)) 511 { 512 $channelids[] = $r['channelid']; 513 } 514 515 $modulechannels = array_merge($modules, $channelids); 516 517 foreach($modulechannels as $m) 518 { 519 $result = $db->query("SELECT * FROM ".TABLE_TYPE." WHERE keyid='$m'"); 520 521 $TYPE = array(); 522 523 while($r = $db->fetch_array($result)) 524 { 525 $r['introduce'] = $r['introduce']? $r['introduce']:' '; 526 527 $TYPE[$r['typeid']] = $r; 528 } 529 530 cache_write('type_'.$m.'.php',$TYPE); 531 } 532 533 return $modulechannels; 534 } 535 } 536 537 function cache_member_group() 538 { 539 global $db; 540 541 // 用户组信息 542 $query = $db->query("SELECT * FROM ".TABLE_MEMBER_GROUP." ORDER BY groupid"); 543 544 while($r = $db->fetch_array($query)) 545 { 546 $groupid = $r['groupid']; 547 cache_write('member_group_'.$groupid.'.php', $r); 548 $data[$groupid] = $r; 549 } 550 551 // 明白了吧。写缓存罗 552 cache_write('member_group.php', $data); 553 554 return $data; 555 } 556 557 function cache_banip() 558 { 559 global $db, $PHP_TIME; 560 561 $result = $db->query("SELECT ip,overtime FROM ".TABLE_BANIP 562 ." WHERE ifban=1 and overtime>=$PHP_TIME order by id desc "); 563 564 while($r = $db->fetch_array($result)) 565 { 566 $data[] = array('ip'=>$r['ip'], 567 'overtime'=>$r['overtime']); 568 } 569 570 $db->free_result($result); 571 572 cache_write('banip.php', $data); 573 574 return $data; 575 } 576