从今天开始.我会一有时间就对PHPCMS 整站的代码做一个详细的分析.目的很简单.让大家都能在phpchina 里面得到进步.
谢谢.;;代码讲解分析全部是本人.按照本人的知识水平来讲解.如果有说得不对的.请指正.也欢迎指正.大家一起进步.谢谢
希望大家支持哦;;
首先我对 PHPCMS 的 头程序文件开始讲解 : include/common.inc.php; ;这个文件是程序启动的核心文件.
PHP代码:
<?php
/**
代码讲解分析:;逆雪寒.;2007;-;12;-;20;
*/
$mtime;=;explode(';',;microtime());;;
$phpcms_starttime;=;$mtime[1];+;$mtime[0];
/**
计算脚本开始运行的时间。很常见的写法。最后结算还在程序运行的终点位置。讲到了在给各位说。
*/
unset($LANG,;$_REQUEST,;$HTTP_ENV_VARS,;$HTTP_POST_VARS,;$HTTP_GET_VARS,;$HTTP_POST_FILES,;$HTTP_COOKIE_VARS);
/**
把;;$HTTP_ENV_VARS;$HTTP_POST_VARS;全局变量设置为;NULL;;因为php4.1.0以上默认以;$_POST;来替代。unset后防止程序运行在低版本会出现安全等问题。比如变量注入
*/
set_magic_quotes_runtime(0);;
//地球人都知道。关了字符窜入库自动转意;比如;;my;name;is;on'x;;;;;转成;;my;name;is;on;'x;;为了最大的程序性能所以我们关掉吧~哈哈
define('IN_PHPCMS',;TRUE);;
/**
程序入口标记。为什么需要这个东西呢。就好比你进一个小区,保安大哥哥肯定要你签字或是挂个鸟牌证明你是从正门进来的。
防止你翻墙进入去偷东西搞破坏。这个入口标记也有这个功能。;在这里定义了一个常量;IN_PHPCMS;为;TRUE;,然后我在其他程序文件里面检查这个标记。如果不存在或不为真,
那么就基本可以肯定你这个家伙是个小偷来的。;人可能翻墙进入。但程序怎么翻墙呢。只要在php.ini文件里面激活;allow_url_fopen选项,include();就可以包含;URL;地址了。你想下如果你有个;放密码的PHP文件。如果给人家include;了以后。你怕不怕。
*/
define('PHPCMS_ROOT',;str_replace("",;'/',;substr(dirname(__FILE__),;0,;-8)));
/**
为了程序产品的跨平台。自动获取程序的安装目录路径;,定义为;PHPCMS_ROOT;常量。方便以后程序使用。建议大家都这样做哦
*/
require;PHPCMS_ROOT.'/include/global.func.php'
/**
包含;全局函数;global.func.php;文件。里面放了些程序全局都有需要用的函数.大家看到了吧。;;常量;PHPCMS_ROOT;已经发挥作用。;这样程序给人家发布到了;二级目录下。也不需要人家手动改整站根目录路径了。
自己好好理解下。。。。。
*/
$search_arr;=;array("/;union;/i","/;select;/i","/;update;/i","/;outfile;/i","/;or;/i");
/**
("/;union;/i");;这个是正则的写法?不懂正则的自己百度找教程来学。;这里不详细说了
*/
$replace_arr;=;array(';union;',';select;',';update;',';outfile;',';or;');
/**
看这个意思很明了。;union;:连接两条SQL语句。;outfile;:;主要用来导出数据库资料到其他介质上。
干啥字要定义这两个变量呢?;在;global.func.php;文件(前面已经加载)里面有个函数;strip_sql();各位可以看下。;
function;strip_sql($string)
{
global;$search_arr,$replace_arr;
return;is_array($string);?;array_map('strip_sql',;$string);:;preg_replace($search_arr,;$replace_arr,;$string);
}
函数里面定义了这两个变量为;global全局变量。;那么函数里就可以直接使用了。;;先讲解下这个函数吧。;顾名思义这个函数是过滤字符窜里面的SQL语句使得关键的SQL语句单词失效。
主要过滤那些SQL语句呢。;主要是这几个关键字:;union;;select;;update;outfile;;or;等。因为这几个SQL字是极度容易在$_GET传输中给截注.
这个函数写得实在精妙。;;is_array;来判断;$string;是否为数组。如果;是;;就;array_map();函数来递归过滤;$string数组里面的每一个单元.;每个单元是一个字符窜吧?
当递归调用;strip_sql();以后;is_array()判断;$string;肯定为假了,因为$string已经不是数组而是字符窜。所以;执行;preg_replace()函数。这个函数很好理解了。就是替换了。
*/
$_POST;=;strip_sql($_POST);
$_GET;=;strip_sql($_GET);
$_COOKIE;=;strip_sql($_COOKIE);
/**
使用strip_sql()函数来过滤;$_POST;;$_GET;;;$_COOKIE;
一般不是开源的站。很少过SQL关键字过滤。不过这个也是冒很大风险的。
*/
unset($search_arr,;$replace_arr);
/**
unset;使用过但以后不需要的变量。这个是很好的习惯。第一不会浪费内存。如果变量存了大量的数据字节,而后你的程序是一直不需要用的。那么
就会很浪费内存。拉底程序性能;.;哈哈。真很书面。反正是好习惯我们都要学习的拉。
*/
$magic_quotes_gpc;=;get_magic_quotes_gpc();;//;get_magic_quotes_gpc()检测gpc是否系统自动转意。;gpc;是什么呢?;GET;POST;COOKIE;来来去去就这几个东西罗。会返回;真或假
if(!$magic_quotes_gpc)
{
$_POST;=;new_addslashes($_POST);
$_GET;=;new_addslashes($_GET);
}
/**
判断一下系统是否打开了自动对gpc进行转意这个选择。如果是的话,就不需要我们自动转意了。如果不是那么还是要老百姓的手段。自己动手丰衣足食。
来看下也是在global.func.php;文件里面定义的这个函数:;其实是一个封装好的php的;addslashes();函数的函数。PHP都自己有了为什么还要自己封装成函数呢?
理由很简单。为了以后的扩展更改容易罗。如果我们一开始就全部用;addslashes();这个函数来对;';进行转意的话。那么以后随着程序的发展。我可能想多过滤个;^;*;(;)之类的
那如何是好呢?所以为了以后孩子的成长。我们还是最好封起来吧。;记住:以后有可能会边的东西。最好都封装成模块。函数;。类。;这样程序的灵活度就上去了。
function;new_addslashes($string)
{
if(!is_array($string));return;addslashes($string);
foreach($string;as;$key;=>;$val);$string[$key];=;new_addslashes($val);
return;$string;
}
这个函数也是写得贼好。也是同时考虑过滤;字符窜或数组,也是使用了;传归。看下就应该明白了吧。这个不用说了。我们要学下这个思路这个方法方式哦。这样才能进步。
哈哈。我们要懂模仿。
*/
@extract($_POST,;EXTR_OVERWRITE);
@extract($_GET,;EXTR_OVERWRITE);
/**
嘿。;extract;前面加个;@鸡蛋做什么呢??抑制错误的。还不懂的话。自己百度了。
为什么用extract()函数呢.;;平时我们程序;是不是要常使用;$_POST;;$_GET来获取传递的变量呀。是不是感觉贼麻烦呀。
比如;$_POST['xx'];;这样接受是挺好。但写多了很麻烦是吧。我是感觉麻烦。我现在想直接就;$xx就可以获取传递过来的东西。那怎么办呢。
就用了;extract()函数来实现这么一个技巧。;这个技巧在discuz;论坛上也有应用。
*/
unset($_POST,;$_GET);
/**
unset();好处不用说了吧。;释放;$_POST;$_GET;数组;,因为已经不需要他们了。
*/
?>
明天放假了.今天在写点罗.放假没空写了.要陪老婆,大家看了有什么不明白的.可以跟帖问.我懂的我会回答.谢谢
继续::
2007-12-21
吃完中午开始分析了点代码.时间不多.
PHP代码:
/**
代码讲解分析:;逆雪寒.;2007;-;12;-;21;
*/
require;PHPCMS_ROOT.'/config.inc.php'
/**
加栽整站的配置参数文件。一般的程序都会有这个文件。做什么的呢?比如一些数据库连接地址。用户名,密码等。需要用到的参数都定义在这个文件里面。这样以后配置变了。我们只要改动下这个文件里面的
变量值就好。是不是很方便呢。呵呵.;;在这里说下;require();这个加载函数。;require;和;include;都是用来加载其他PHP文件用的。但他们是有区别的。;require;函数:是"预解释"函数。就是程序一加载,就执行了require函数。而include;;呢。是个过程加载函数。我们可以在逻辑里比如: if;里面使用include;来动态的加载其他程序片段。而require;就不行。*/
require;PHPCMS_ROOT.'/languages/'.$CONFIG['language'].'/phpcms.lang.php'
/**
顾名思义: 这个就是加载语言包了。PHP的国际化目前做得最多的。就是直接用PHP文件来实现。 在;phpcms.lang.php;文件里面定义程序中要用到的中文信息。然后在程序一开始就加载。那里程序里面
就可以使用这个文件里面的变量和一切。那么就简单了。模板上就不需要直接写中文信息了。直接用这个文件里面定义的变量等来替换。从而实现国际化。over!!!最好自己打开这个语言文件再加上自己思考下。就知道。原来如此简单。
*/
define('PHPCMS_PATH',;$CONFIG['rootpath']);
define('PHPCMS_CACHEDIR',;$CONFIG['cachedir']);
/**
$CONFIG['rootpath'];;这个就是全局配置文件 config.inc.php;文件里面数据库信息。等全部配置信息。在这里把他们定义为 常量。 为什么需要定义为常量呢。因为作者感觉这样写爽罗。呵呵。其实因为后面
用到这两个变量多。所以干脆定义为常量。方便使用。再多说一个技巧: $CONFIG['rootpath'];;其实也可以写成 $CONFIG[rootpath];;但是最好不要这样。为什么呢。因为PHP引擎会先判断;;rootpath;是不是常量。如果不是才会认定;; $CONFIG[rootpath];;是数组。 这样性能上就差了一点点了。 再多说一个技巧: 为什么程序多数都用 '' 单引号呢而不用;"";双引号呢。因为这样效率好,;"";双引号。
php引擎还会先检查里面是否有变量,如果有就解释。而;'';单引号不会做这一步的检查。而直接就当成字符窜了。所以效率上也会有一点点影响哦。
*/
$CONFIG['enablephplog'];?;set_error_handler('phpcms_error');:;error_reporting(E_ERROR;|;E_WARNING;|;E_PARSE);
/**
$CONFIG['enablephplog'];;是否开启错误日志设置。这个设置在全局配置文件里面.config.inc.php;。 这里使用了 三目运算符 偶最喜欢用了。一些简短的逻辑判断。可以使用 ?;:;; 来实现比较简洁
set_error_handler();这个函数就大有来头了。php4里面的典型自定义程序出错后行为的一个函数。十分好用。怎么用呢?;set_error_handler(函数) 的参数也是一个函数。这个函数。反映了程序出错后行为的。
phpcms_error;函数存在 global.func.php;全局函数里面。
function;phpcms_error($errno,;$errmsg,;$filename,;$linenum,;$vars)
{
$filename;=;str_replace(PHPCMS_ROOT,;'.',;$filename);
;;;;$filename;=;str_replace("",;'/',;$filename);;;//; 把win平台的; 换成 /兼容常见系统的路径
;;;;if(!defined('E_STRICT'));define('E_STRICT',;2048);
$dt;=;date('Y-m-d;H:i:s');
$errortype;=;array;(
E_ERROR;;;=>;'Error',
E_WARNING;=>;'Warning',
E_PARSE;;;=>;'Parsing;Error',
E_NOTICE;;=>;'Notice',
E_CORE_ERROR;;;;;;=>;'Core;Error',
E_CORE_WARNING;;;;=>;'Core;Warning',
E_COMPILE_ERROR;;;=>;'Compile;Error',
E_COMPILE_WARNING;=>;'Compile;Warning',
E_USER_ERROR;;;;;;=>;'User;Error',
E_USER_WARNING;;;;=>;'User;Warning',
E_USER_NOTICE;;;;;=>;'User;Notice',
E_STRICT;;=>;'Runtime;Notice'
;;;;;;);
$user_errors;=;array(E_USER_ERROR,;E_USER_WARNING,;E_USER_NOTICE);
$err;=;"<errorentry>n";
$err;.=;"t<datetime>";.;$dt;.;"</datetime>n";
$err;.=;"t<errornum>";.;$errno;.;"</errornum>n";
$err;.=;"t<errortype>";.;$errortype[$errno];.;"</errortype>n";
$err;.=;"t<errormsg>";.;$errmsg;.;"</errormsg>n";
$err;.=;"t<scriptname>";.;$filename;.;"</scriptname>n";
$err;.=;"t<scriptlinenum>";.;$linenum;.;"</scriptlinenum>n";
if;(in_array($errno,;$user_errors))
{
;;;;$err;.=;"t<vartrace>";.;wddx_serialize_value($vars,;"Variables");.;"</vartrace>n";
}
$err;.=;"</errorentry>nn";
echo;$err;
error_log($err,;3,;PHPCMS_ROOT.'/data/php_error_log.xml');
chmod(PHPCMS_ROOT.'/data/php_error_log.xml',;0777);
}
就是这个鸟蛋。 现在我们慢慢来干掉他。呵呵 ;;这个自定义出错信息函数默认带有四个参数。 第一个参数 $errno;是程序出错的等级。 第二参数是程序出错的界面信息。第三是出现错误的程序文件名。
第四是 第几行出现错误。第五个参数。要不要都行是当前变量状态的快照.看吧。我们有这些信息后。想定义怎么样的错误信息给客户看都很容易了是吧?但现在我们是要生成错误日志呢?这里phpcms;作者是动态生成一个XML文件来做错误日志的。不错不错.;;他使用了;in_array();函数来实现(因为比较简单,自己理解下) 只记录 E_USER_ERROR,;E_USER_WARNING,;E_USER_NOTICE;这三个级别的错误日志信息。n;是文本换行符 t是制表符.;这里他使用了一个比较漂亮而不常用的函数;wddx_serialize_value;();wddx;其实也是一种 xml;。 wddx_serialize_value() 这个函数就是把一般变量以XML格式输出。这样我们就不用自己模拟写xml了。方便吧。呵呵;第一个参数就是: 要格式输出的变量,第二个参数是输出的xml的介绍信息.;下面就是;error_log();函数。这个函数十分有用了。就是生成错误日志XML文件。不需要我们fopen;了。方便吧。它还有很多功能。详细的看手册。chmod;设置日志文件的权限是 可读可写可执行。 在php5中。我习惯使用;extends Exception 来定义自己的出错信息。所以很少用;set_error_handle().;如果没开启日志功能。那么 error_reporting(E_ERROR;|;E_WARNING;|;E_PARSE) 就运行了。把一般出错信息先出过来。
*/
if($CONFIG['sessionsavepath']);session_save_path($CONFIG['sessionsavepath']);
/**
定义session;的存储路径,session;其实 也是cookie;不过;session;是实现在服务器端的。安全但负载重点。这样做的好处?效率很好。如果你在虚拟主机的话。大家的session;cookie;都放在了php.ini里面设置的默认地方。文件夹臃肿就会慢罗。是吧。第二就是安全罗。 记得一定要定义在;session_start()函数之前
*/
session_start();
if(function_exists('date_default_timezone_set'));date_default_timezone_set($CONFIG['timezone']);
/**
php5开始有时区的概念了。记得就行
*/
header('Content-type:;text/html;;charset='.$CONFIG['charset']);
/**
设置页面编码.; php编码有: 页面编码。数据库编码。文件内码。如果三码相同就一般不会出现乱码. 文件内码是什么呢?每个文件都有自己的内部编码。一般都用UTF8比较爽。怎么改变文件内码?你用DW也行 UE 也行。随便。 数据库编码那肯定是要指定的了。mysql5开始也有字符集模式这个最好也设置这样可以兼容更多平台。
页面编码:<meta;http-equiv="Content-Type";c;/> 这句就是。一般的HTML头文件都有。那;还需要header('Content-type:;text/html;;charset='.$CONFIG['charset']);吗? 其实需要的。因为有些自己写的提示层呀。或是文件里没指定页面编码的。就很容易出现乱码那么我们就防范于未然。;header;一个编码过去。那就OK了。多好。
*/
if(getenv('HTTP_CLIENT_IP');&&;strcasecmp(getenv('HTTP_CLIENT_IP'),;'unknown'))
{
$PHP_IP;=;getenv('HTTP_CLIENT_IP');
}
elseif(getenv('HTTP_X_FORWARDED_FOR');&&;strcasecmp(getenv('HTTP_X_FORWARDED_FOR'),;'unknown'))
{
$PHP_IP;=;getenv('HTTP_X_FORWARDED_FOR');
}
elseif(getenv('REMOTE_ADDR');&&;strcasecmp(getenv('REMOTE_ADDR'),;'unknown'))
{
$PHP_IP;=;getenv('REMOTE_ADDR');
}
elseif(isset($_SERVER['REMOTE_ADDR']);&&;$_SERVER['REMOTE_ADDR'];&&;strcasecmp($_SERVER['REMOTE_ADDR'],;'unknown'))
{
$PHP_IP;=;$_SERVER['REMOTE_ADDR'];
}
preg_match("/[d.]{7,15}/",;$PHP_IP,;$ipmatches);
$PHP_IP;=;$ipmatches[0];?;$ipmatches[0];:;'unknown'
/**
函数 getenv();是获取环境变量。 环境变量: HTTP_CLIENT_IP;是获取客户端的IP 。但有可能人家是通过代理来访问你的程序的呢。那么这时候就要用;环境变量:
HTTP_X_FORWARDED_FOR;;了。 包括 getenv('REMOTE_ADDR') $_SERVER['REMOTE_ADDR'] 都是获取人家 IP的。反正碰罗。碰到那个能获取就大工告成。
*/
$PHP_TIME;=;time();
$PHP_SELF;=;isset($_SERVER['PHP_SELF']);?;$_SERVER['PHP_SELF'];:;(isset($_SERVER['SCRIPT_NAME']);?;$_SERVER['SCRIPT_NAME'];:;$_SERVER['ORIG_PATH_INFO']);
/**
获取当前运行的脚本名:; 刚开始看是不是有点乱呢。 咋没用if;else;呢。;看这样的东西。我们最好从右看到左。这样比较好明白点。$_SERVER['SCRIPT_NAME'];$_SERVER['PHP_SELF'];;$_SERVER['ORIG_PATH_INFO'];这三个服务器全局变量都是获取 当前脚本名的。主要看服务器当前环境了。那个存在的就获取那个。
isset();函数 十分有用。 测试一个变量是否已经定义。 注: $a=;NULL;;;isset($a);这样会返回false的哦。 注意 isset;和empty;两个函数的用法。用得不好会出大问题的。自己看手册。
*/
$PHP_QUERYSTRING;=;$_SERVER['QUERY_STRING'];
$PHP_DOMAIN;=;$_SERVER['SERVER_NAME'];
$PHP_REFERER;=;isset($_SERVER['HTTP_REFERER']);?;$_SERVER['HTTP_REFERER'];:;''
$PHP_SCHEME;=;$_SERVER['SERVER_PORT'];==;'443';?;'https://';:;'http://' //测试服务器是否启动了ssl;连接如果是的话。就用https://安全连接来进行通行
$PHP_PORT;=;$_SERVER['SERVER_PORT'];==;'80';?;'';:;':'.$_SERVER['SERVER_PORT'];
$PHP_SITEURL;=;$PHP_SCHEME.$PHP_DOMAIN.$PHP_PORT.PHPCMS_PATH
$PHP_URL;=;$PHP_SCHEME.$PHP_DOMAIN.$PHP_PORT.$PHP_SELF.($PHP_QUERYSTRING;?;'?'.$PHP_QUERYSTRING;:;'');
/**
获取当前脚本的URL
*/
$db_file;=;$db_class;=;'db_'.$CONFIG['database'];
if(!defined('IN_ADMIN')) //如果不是在后台。 常量 IN_ADMIN;是后台标志
{
if($CONFIG['dbiscache']);$db_file;.=;'_cache'
if($CONFIG['phpcache'];==;'2') //;如果在config.inc.php;里面开启了缓存
{
;;$cachefileid;=;md5($PHP_SELF.'?'.$PHP_QUERYSTRING);;//把脚本名和后面的get信息 md5加密,以此来生成下面的缓存目录和缓存文件
;;$cachefiledir;=;PHPCMS_ROOT.'/data/phpcache/'.substr($cachefileid,;0,;2).'/' //缓存目录
;;$cachefile;=;$cachefiledir.$cachefileid.'.html' //缓存文件: xxx.html;格式
;;if(file_exists($cachefile);&&;($PHP_TIME;<;@filemtime($cachefile);+;$CONFIG['phpcacheexpires']))
;;{;;;//如果缓存文件存在和缓存没有过期效,那么就返回缓存文件名
;;;require;$cachefile
;;;exit;
;;}
}
if($PHP_QUERYSTRING;&&;preg_match("/^(.*).(htm|html|shtm|shtml)$/",;$PHP_QUERYSTRING,;$urlvar));//获取传递过来的变量。有什么用的呢?请看下面解释
{
;;parse_str(str_replace(array('/',;'-',;';'),;array('&',;'=',;''),;$urlvar[1]));
}
}
/**
上面这部分相对复杂了点。但没关系。慢慢讲解. 首先缓存只针对前台.所以我们一开始就判断.这个脚本是运行在前台的而不是在后台;!defined('IN_ADMIN');来判断.
然后呢.再看客户配置;config.inc.php文件是否开启了缓存. ==2;就是开启了. .接着开始用一系列的规则来找出缓寸的文件名和目录:;以;脚本名:xx.php和后续传递的参数;?xx=ee&bb=jj;他两的字符窜的MD5 .以这个md5窜来定义出了缓存目录.和缓存文件 .接着再判断这个缓存文件是否存在和是否没过缓存有效期.如果没有就返回这个缓存文件的名字.
然后到主菜了. 最后一个if逻辑是做什么的呢? 不知道大家有没见过 这样的网址:http://www.beihai.com/dd.php/xx-23/cc-22.html; 他们其实都算是伪静态.优化URL用的.咋看起来还很象静态.爽.;但你可能想.这样的地址.我们写PHP程序的.怎么获取get;变量呢? 最后if;就是解答这个问题的. 先剥离url来获取 ;传递的字符窜.然后 str_replace;来把;'/';;'-';;替换成标准的;;'&';'=';;好象:; http://www.beihai.com/dd.php&xx=23&cc=22 ;看这样你应该看明白了吧.然后用;parse_str();函数来把xx;变 $xx=23;;;cc 变 $cc=22;;;;php真是什么都给你想到了.强.看明白了吧.OK.过了.
*/
恩.终于把common.inc.php 这个文件大概讲解完了. 这个文件里面包含了很多东西.都是些挺不错的思想.大家应该好好学习.这样我们写出来的PHP程序会更加强壮.
,偶现在晚上都在边陪老婆边看电影边弄linux 的C,还是学习 阶段;;所以时间有点紧.白天在公司挤点时间出来分析代码罗.
对于phpcms 我也是第一次接触.以前没装过也没用过.现在也没详细用过.所以我看到代码讲什么我就讲什么.没具体讲PHPCMS的应用等.希望理解.
如果我分析代码分析得不合理.请指出.功能进步学习.谢谢
PHP代码:
require;PHPCMS_ROOT.'/include/'.$db_file.'.class.php' // 包含数据库操作类,下章详说
require;PHPCMS_ROOT.'/include/tag.func.php' //遇到再说
require;PHPCMS_ROOT.'/include/extension.inc.php' //遇到再说
$db;=;new;$db_class // 实例化数据库类
$db->connect($CONFIG['dbhost'],;$CONFIG['dbuser'],;$CONFIG['dbpw'],;$CONFIG['dbname'],;$CONFIG['pconnect']);;//连接数据库@_@
$db->iscache;=;$CONFIG['dbiscache']; //是否开启SQL缓存
$db->expires;=;$CONFIG['dbexpires']; //缓存时间
if(!cache_read('table.php'))
{
require_once;PHPCMS_ROOT.'/include/cache.func.php'
;;;;cache_all();;//生成所有缓存
}
/**
cache_read();函数 读缓存文件函数存在;global.func.php 里面.上菜先:
function;cache_read($file,;$mode;=;'i')
{
$cachefile;=;PHPCMS_CACHEDIR.$file;
if(!file_exists($cachefile));return;array();
return;$mode;==;'i';?;include;$cachefile;:;file_get_contents($cachefile);
}
就这么简单.文本缓存,在一些大的开源的PHP项目中经常见到.主要是为了减轻数据库的负荷的.;比如在程序启动文件里面,就把一些后台配置的常用信息缓存到php文件里面.然后在以后的程序就可以直接使用而不用每次都访问数据库了.但对经常要更新的信息.最好不要用文本缓存这形式,因为PHP文件内置的文件锁flock()不是很好用.大系统中多用户同时写访问的时候有可能会把缓存文件破坏.大系统建议使用;memcached; mysql5.1;分区 mysql;主从 来实现负载均衡 @=@ 废话太多了. 这个函数很简单.自己看下就明白了.如果缓存和模式变量;$mode;是否为;i;是就include;不是就 把文件以字符窜形式读到内存中.
如果;cache_read()找不到缓存文件'table.php'就会返回false,那么就 加栽;cache.func.php; 文件.它里面是些创建缓存的一些函数.;然后呢执行;cache_all()函数生成所有的常用信息缓存.
关于phpcms;的缓存更详细包括生成原理.打算在弄完启动文件common.inc.php;后再开篇写个详细的.
*/
$CACHE;=;cache_read('common.php');;
/**
加载 common.php 缓存文件里面的变量(数据) 这样我们不用从数据库读了每次.是吧
common.php;文件里面是什么来的呢?上菜:
<?php
return;array;(
;;'module';=>;
;;array;(
;;;;'phpcms';=>;
;;;;array;(
;;;;;;'module';=>;'phpcms',
;;;;;;'name';=>;'phpcms',
;;;;;;'iscore';=>;'1',
;;;;;;'iscopy';=>;'0',
;;;;;;'isshare';=>;'0',
;;;;;;'moduledir';=>;'',
;;;;;;'linkurl';=>;'',
;;;;),
;;;;'member';=>;
;;;;array;(
;;;;;;'module';=>;'member',
;;;;;;'name';=>;'会员',
;;;;;;'iscore';=>;'1',
;;;;;;'iscopy';=>;'0',
;;;;;;'isshare';=>;'0',
;;;;;;'moduledir';=>;'member',
;;;;;;'linkurl';=>;'/phpcms/member/',
;;;;),
;;;;'article';=>;
;;;;array;(
;;;;;;'module';=>;'article',
;;;;;;'name';=>;'文章',
;;;;;;'iscore';=>;'0',
;;;;;;'iscopy';=>;'1',
;;;;;;'isshare';=>;'0',
;;;;;;'moduledir';=>;'article',
;;;;;;'linkurl';=>;'',
;;;;)
?>
看到了吧.这个就是全部从数据库里面生成的文本缓存信息.我们不用每次都连接数据库读数据库.而只要访问里面的数组就可以得到一些配置信息.
这个就是文本缓存的作用了,至于怎么会生成这个文本缓存文件的.我会另外开一篇来介绍。
*/
$MODULE;=;$CACHE['module']; //缓存中的数据
$CHANNEL;=;$CACHE['channel'];
$PHPCMS;=;$CACHE['phpcms'];
$FIELD;=;$CACHE['field'];
unset($CACHE,;$ipmatches,;$CONFIG['timezone'],;$CONFIG['cachedir'],;$CONFIG['dbhost'],;$CONFIG['dbuser'],;$CONFIG['dbpw'],;$CONFIG['pconnect'],;$CONFIG['dbiscache'],;$CONFIG['dbexpires']);
/**
unset;掉不需要用的变量.
*/
if($PHPCMS['enablebanip'];&&;ip_banned($PHP_IP));showmessage($LANG['administrator_banned_this_IP']);
/**
$PHPCMS['enablebanip'] 是什么.不用说应该知道了吧.这个就是后台里面设置是否开启过滤IP访问的功能.(因为我没用过phpcms,我是按照代码猜的,不对的请指出)从这里就看出了文本缓存也有他的作用的。 ip_banned()函数是什么呢.上菜再说:
function;ip_banned($ip)
{
global;$PHP_TIME; //前面定义过的.当前的时间
$ipbanneds;=;cache_read('banip.php');
if(!is_array($ipbanneds));return;FALSE;
foreach($ipbanneds;as;$v)
{
;;if($v['overtime'];<;$PHP_TIME);return;FALSE;
;;if($ip;==;$v['ip'];||;preg_match("/^".str_replace('.',;'[.]',;$v['ip'])."$/",;$ip));return;TRUE;
}
}
里面也用到了;cache_read() 这个函数,还是读banip.php;这个文件.banip.php这个文件里面存着你在后台甚至的要过滤的IP列表.
里面的逻辑比较简单.自己消化下了.不明白跟帖问
showmessage();函数是提示出错信息封装好的一个函数. 国家化的;$LANG['administrator_banned_this_IP']这个看到了吧.这个就是读语言包里面的.这样我们就可以出好多个语言版本的程序拉.
*/
$TEMP;=;$MOD;=;$CHA;=;$CATEGORY;=;$CAT;=;array();
$ftp;=;$enableftp;=;$tags;=;$html;=;0
/**
初始化变量.这个是好习惯我们要模仿.
*/
if(!isset($mod))
{
$mod;=;'phpcms'//phpcms;是默认加载的模块
}
elseif($mod;!=;'phpcms')
{
isset($MODULE[$mod]);or;exit($LANG['module_not_exists']); // 从缓存中读加载的模块是否开启
/**
这个写法,我十分喜欢,平时也用.;;;xx;&&;dd;;;xx;and;dd;;;与运算要同时两边都为真整个公式才为真,就是利用这个原理. ;;xx;||;dd;;;xx;or;dd;;或运算只要一个条件满足就不会执行下一个条件而继续执行下去. 这样写是不是很酷.
*/
$MOD;=;cache_read($mod.'_setting.php');;//开始加载这个模块的一些常用配置数值。;phpcms;对应的每个模块都有一个缓存配置文件。@@;怪不得速度那么快
@include;PHPCMS_ROOT.'/languages/'.(defined('IN_ADMIN');?;$CONFIG['adminlanguage'].'/'.$mod.'_admin.lang.php';:;$CONFIG['language'].'/'.$mod.'.lang.php');
/**
加载想对应的模块语言包.
*/
}
if(!isset($forward));$forward;=;$PHP_REFERER//记录前一个URL地址。估计以后下面程序有需要用这个变量
$dosubmit;=;isset($dosubmit);?;1;:;0//记录是否有表单提交过.也是以后有用
$channelid;=;isset($channelid);?;intval($channelid);:;0//记录当前频道的id;;如果$channelid;没有;isset;那么就为;0.;intval();十分有用。数字和数字的比较加减速度会快很多。记得哦
$skindir;=;PHPCMS_PATH.'templates/'.$CONFIG['defaulttemplate'].'/skins/'.$CONFIG['defaultskin'];;//加载默认phpcms皮肤
if($PHPCMS['enablegzip'];&&;function_exists('ob_gzhandler'))
{
($CONFIG['phpcache'];||;defined('SHOWJS'));?;ob_start();:;ob_start('ob_gzhandler');
}
else
{
$PHPCMS['enablegzip'];=;0
ob_start();
}
/**
$PHPCMS['enablegzip'];;;这个变量就是存在于;phpcms_setting.php;文件里。上面已经说过了。每个模块都有相对应的模块配置缓存文件(是从数据库copy过来的信息);这个变量标致;是否开启;压缩传输。
压缩传输,听名字就知道。就是把数据按照一定的算法压缩小罗。然后再传送到客户端。这样就可以在有限的带宽中传输更大的数据拉。当然速度快了不少。压缩的数据到了你的浏览器,它就自动解压缩,老版本的一些浏览器不支持解压缩哦。不过现在还有谁用很久的浏览器呢。用法很简单的:看上面就知道:
首先判断下,看客户老大们是否在后台选择了这个模块的压缩传输(如果是的话。自然的已经加载到了相对应的文本缓存文件里面拉);标致:$PHPCMS['enablegzip'];;和;判断;回调函数;ob_gzhandler;是否开启,;;ob_gzhandler;其实不算是个函数。看手册说明。;就这么简单。它只是一个专门给;ob_start();做回调使用的一个参数函数。详细请看下手册。别偷懒哦,在程序开头ob_start('ob_gzhandler')就算是开始压缩传输了;判断完了;如果为真。就继续下面的代码:
($CONFIG['phpcache'];||;defined('SHOWJS'));?;ob_start();:;ob_start('ob_gzhandler');
看代码phpcms;是这样的:;如果用户在后台开启了压缩传输。而用户又开启了;页面缓存。那么就默认不使用压缩传输了。我也不知道为什么这样设计。我测试了下。后台开启压缩传输。又同时又使用页面缓存。没发现有什么问题。@@;
如果没开启压缩传输,那么我们就ob_start();;使用session;之前必须要;ob_start();;;而且在ob_start();之前不能有任何的;头文件发送和输出。比如:echo;header等要不会出错的哦。
*/
$_userid;=;0
$_username;=;''
$_groupid;=;3
$_arrgroupid;=;array();
$phpcms_auth;=;getcookie('auth');
/**
$_userid,$_username,$_groupid;;这几个记录用户信息的变量初始化,不初始化危险就太大了。@@;如果给人家$_GET一个;_userid;变量过来。那么就会把我们这个变量覆盖。但是我们如果给这几个变量一个值,
那么按照就近原则。就算你GET个变量过来。你也一样改不了我原来的变量值。大家好好自己想下。就会明白了。
getcookie();这个自定义函数在;global.func.php文件里定义的。上菜:
function;getcookie($var)
{
global;$CONFIG;
$var;=;$CONFIG['cookiepre'].$var;
return;isset($_COOKIE[$var]);?;$_COOKIE[$var];:;FALSE;
}
这个函数用来提取我们设置的cookie;值.;$CONFIG['cookiepre'];;在;config.inc.php;文件里面设置,cookie;名的前缀.;;函数很简单。一看就明白不说了。
*/
if($phpcms_auth)
{
$phpcms_auth_key;=;md5($PHPCMS['authkey'].$_SERVER['HTTP_USER_AGENT']);
list($_userid,;$_password,;$_answer);=;$phpcms_auth;?;explode("t",;phpcms_auth($phpcms_auth,;'DECODE'));:;array(0,;'',;'');
/**
;;list();=;array();;用户大家自己试下。;意会下
;;phpcms_auth();;是加密和解密;函数,;;因为cookie;是存在于客户端。十分危险呀。;你看连用户的密码也存在cookie;不加密能行吗。但是呢加密后又要能解密。因为用户名和用户密码我们往下操作要
;;获取的。;这个函数存在于;global.func.php;文件里面。大家想了解这个算法的自己去看下吧。挺简单的。;其实就是围绕着;;$phpcms_auth_key;;这个变量来加密解密和discuz;的cookie;机制差不。
;;$phpcms_auth_key;=;md5($PHPCMS['authkey'].$_SERVER['HTTP_USER_AGENT']);;;看$PHPCMS['authkey'],估计后台有个;cookie;加密值让你填,然后以这个值和;$_SERVER['HTTP_USER_AGENT'](系统信息)
*/
$_userid;=;intval($_userid);
if($_userid;<;0);$_userid;=;0//读出的cookie;的用户id;如果是;小于0;
if($_userid);//如果;cookie;保存的这个uid;存在,那么开始按照这个ID来查数据库用户表;来取出用户信息
{
;;$memberinfo;=;$db->get_one("SELECT;username,password,groupid,arrgroupid,email,chargetype,begindate,enddate,money,point,credit,newmessages;FROM;".TABLE_MEMBER.";WHERE;userid=$_userid;LIMIT;0,1");
;;/**
;;phpcms;封装好的数据库类,下篇开讲这个大家就大概看行了。;;大家看下;select;;sql语句。;也可以学习下。;首先最好不要使用;select;*;from;xx;;的;*;形式,除非你想获取所有字段的记录。只罗列你要的字段。这样在数据量大的查询中。速度明显上去。;;;;常量:;TABLE_MEMBER;;定义了表名。这样做有什么好处呢?想都知道了,为了以后变更表名方便而定义为常量。这个东西那里来的。估计在一个文件里面定义好的。遇到了再讲吧懒得找了。
;;*/
;;if($memberinfo;&&;$memberinfo['password'];==;$_password);;//用查询出来的密码和;cookie;中存在的密码想对比.为了在效率:;在比较前;先判断查询是否成功先。很多phper往往忽略。
;;{
;;;if($memberinfo['groupid'];==;2);;//如果用户属于的组的ID;为;2;;那么这个用户是被管理员禁止访问的了。
;;;{
mkcookie('auth',;'');;//;清除cookie;
;;;;showmessage($LANG['userid_banned_by_administrator']);;//提示出错菜单
;;;}
;;;@extract($memberinfo,;EXTR_PREFIX_ALL,;'');;//又来这招,应该明白了吧各位老大:把字段;变成;我们能直接使用的变量
;;;unset($memberinfo,;$_password,;$_answer);
;;;$_arrgroupid;=;$_arrgroupid;?;array_filter(explode(',',;$_arrgroupid));:;array();;;//把;字段为;arrgroupid;;值为;FALSE;过滤掉。array_filter()不带回调参数的用法,请看手册。
;;}
;;else
;;{
;;;mkcookie('auth',;'');
;;}
;;/**
经过上面的读cookie;和查数据库用户信息后。当确定这个用户信息是合法以后。就会自动登陆了。比如phpchina论坛。当你登陆后没注销。下次访问的时候还是登陆状态。就是这个原理。记得模仿哦
;;这里详细解释下;mkcookie;()函数;;上菜:
;;function;mkcookie($var,;$value;=;'',;$time;=;0)
;;{
;;;global;$CONFIG,$PHP_TIME;
;;;$time;=;$time;>;0;?;$time;:;(empty($value);?;$PHP_TIME;-;3600;:;0);
;;;$s;=;$_SERVER['SERVER_PORT'];==;'443';?;1;:;0;
;;;$var;=;$CONFIG['cookiepre'].$var;
;;;return;setcookie($var,;$value,;$time,;$CONFIG['cookiepath'],;$CONFIG['cookiedomain'],;$s);
;;}
;;$time;;为cookie;的存活时间:;;如果为;0;;就是关闭浏览器;cookie;就自动失效;,;;$PHP_TIME;在前面定义了:当前时间。;;;$PHP_TIME;-3600;;减去3600秒。就是一个小时前的意思,那肯定是设置cookie;失效的意思了。
;;$s;;变量是;获取;是否开启SSL安全传输的标致。;cookie;有一个参数是ssl传输的。如果服务器已经opensll;了那么我们肯定不能浪费这么好的安全资源了。
;;$var;cookie名的前缀,主要防止混淆。
;;$CONFIG['cookiedomain'];;这个家伙在;config.inc.php里面已经配置的了。定义为:;'/';;;意思就是说;在当前域;的所有目录的PHP程序都能访问这个COOKIE;,还有限制目录访问COOKIE;的弄法。具体请看;setcookie;();函数手册上说明。
;;*/
;;
}
}
unset($db_class,;$db_file,;$phpcms_auth,;$phpcms_auth_key,;$memberinfo);
下章我就分析 PHPCMS 的数据库操作类文件和 PHPCMS的文本缓存机制