PHPCMS单入口源码分析
本人在查阅PHP相关单入口资料时发现文献很少,而且PHPCMS源码的官方注释也十分有限,所以在此简要的写一篇关于PHPCMS单入口源码的分析,此文给出了PHPCMS入口的主要源码附带本人的一些注释。
限于本人的个人水平和疏忽,不能保证文章内的源码注释绝对准确,适合新手朋友学习 交流之用。
话不多说,首先我们需要下载一个PHPCMS的内容管理系统并安装,相关的下载安装教程百度上很多,在此不再赘述了。
安装完成后我们打开安装的PHPCMS工程,打开index.php,这是默认的控制器,也就是唯一的入口了。
该文件内的代码很少,详细如下:
/**
* index.php PHPCMS 入口
*/
define('PHPCMS_PATH', dirname(__FILE__).DIRECTORY_SEPARATOR);
includePHPCMS_PATH.'/phpcms/base.php';
pc_base::creat_app();
该文件定义了一个常量后就引入了一个名为base.php的文件,然后调用该文件中pc_base类的静态方法,创建一个应用。
我们打开安装文件目录下/phpcms/base.php源码附带注释如下: <?php
/**
* base.php PHPCMS框架入口文件
*/
//设定入口为true
define('IN_PHPCMS', true);
// 设定PC_PATH:PHPCMS框架路径
define('PC_PATH', dirname(__FILE__).DIRECTORY_SEPARATOR);
if(!defined('PHPCMS_PATH'))
define('PHPCMS_PATH',PC_PATH.'..'.DIRECTORY_SEPARATOR);
//设定CACHE_PATH:缓存路径
define('CACHE_PATH', PHPCMS_PATH.'caches'.DIRECTORY_SEPARATOR);
//设定SITE_PROTOCOL:主机协议
define('SITE_PROTOCOL', isset($_SERVER['SERVER_PORT'])
&&$_SERVER['SERVER_PORT'] == '443' ? 'https://' : 'http://');
//设定SITE_URL:当前访问的主机名
define('SITE_URL', (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : ''));
//设定HTTP_REFERER:来源
define('HTTP_REFERER', isset($_SERVER['HTTP_REFERER']) ?
$_SERVER['HTTP_REFERER'] : '');
//设定SYS_START_TIME:系统开始时间
define('SYS_START_TIME', microtime());
//加载公用函数库
pc_base::load_sys_func('global');
pc_base::load_sys_func('extention');
pc_base::auto_load_func();
//加载错误日志、报告、句柄
pc_base::load_config('system','errorlog') ?
set_error_handler('my_error_handler') : error_reporting(E_ERROR | E_WARNING | E_PARSE);
//设置本地时差
function_exists('date_default_timezone_set')
&&date_default_timezone_set(pc_base::load_config('system','timezone')); define('CHARSET',pc_base::load_config('system','charset'));
//输出页面字符集
header('Content-type: text/html; charset='.CHARSET);
//返回当前unix时间戳
define('SYS_TIME', time());
//定义网站根路径
define('WEB_PATH',pc_base::load_config('system','web_path'));
//js路径
define('JS_PATH',pc_base::load_config('system','js_path'));
//css路径
define('CSS_PATH',pc_base::load_config('system','css_path'));
//img路径
define('IMG_PATH',pc_base::load_config('system','img_path'));
//动态程序路径
define('APP_PATH',pc_base::load_config('system','app_path'));
//应用静态文件路径
define('PLUGIN_STATICS_PATH',WEB_PATH.'statics/plugin/');
if(pc_base::load_config('system','gzip') &&function_exists('ob_gzhandler')) {
}
classpc_base {
{
$m= empty($m) &&defined('ROUTE_M') ? ROUTE_M :$m; if(empty($m)) return false; return self::_load_class($classname, /** * 初始化应用程序 * creat_app()不仅包含了application.class.php文件,还返回了一个该类的实例 */ public static function creat_app() { } /** * 加载系统类方法 * @param string $classname类名 * @param string $path 扩展地址 * @paramintger $initialize 是否初始化 *///默认path路径为libs/classes public static function load_sys_class($classname, $path= '', $initialize= } /** * 加载应用类方法 * @param string $classname类名 * @param string $m 模块 * @paramintger $initialize 是否初始化 */ public static function load_app_class($classname, $m= '', $initialize= 1) return self::_load_class($classname, $path, $initialize); return self::load_sys_class('application'); ob_start('ob_gzhandler'); ob_start(); } else { 1) {
'modules'.DIRECTORY_SEPARATOR.$m.DIRECTORY_SEPARATOR.'classes',
$initialize);
} /**
{
* 加载数据模型 * @param string $classname类名 */ public static function load_model($classname) { } /** * 加载类文件函数 * @param string $classname类名 * @param string $path 扩展地址 * @paramintger $initialize 是否初始化 */ private static function _load_class($classname, $path= '', $initialize= 1) //如果path为空,指定path路径为libs/classes static$classes = array(); if(empty($path)) $path= 'libs'.DIRECTORY_SEPARATOR.'classes'; $key = md5($path.$classname); if(isset($classes[$key])) { } /** * 如果存在phpcms/$path/$classname.class.php则包含 */ if(!empty($classes[$key])) { } return$classes[$key]; return true; } else { return self::_load_class($classname,'model'); //返回加密后的类具体路径给数组的索引 if(file_exists(PC_PATH.$path.DIRECTORY_SEPARATOR.$classname.'.class.php includePC_PATH.$path.DIRECTORY_SEPARATOR.$classname.'.class.php'; $name = $classname; //如果此路径下有扩展路径(自定义)//覆盖原$path用自定义$path ')) { phpcms/$path/'MY_'.$classname.class.php便引入 if($my_path= include$my_path; //覆盖原类名,用自定义的 $name = 'MY_'.$classname; self::my_path(PC_PATH.$path.DIRECTORY_SEPARATOR.$classname.'.class.php')) {
} } } if($initialize) { } return$classes[$key]; return false; $classes[$key] = new $name; //返回文件名类定义类的实例 $classes[$key] = true; } else { } else { /** * 加载系统的函数库 * @param string $func函数库名 */ public static function load_sys_func($func) { } /** * 自动加载autoload目录下函数库 * @param string $func函数库名 */ public static function auto_load_func($path='') { } /** * 加载应用函数库 * @param string $func函数库名 * @param string $m 模型名 */ public static function load_app_func($func, $m= '') { } /** * 加载插件类库
*@$identification插件标识 $m= empty($m) &&defined('ROUTE_M') ? ROUTE_M :$m; if(empty($m)) return false; return self::_load_func($func, return self::_auto_load_func($path); return self::_load_func($func); //判断模块是否为空,常量是否定义 'modules'.DIRECTORY_SEPARATOR.$m.DIRECTORY_SEPARATOR.'functions');
*/ public static function load_plugin_class($classname, $identification= '' ,$initialize= 1) {
//判断插件标识是否为空,判断是否定义插件id
$identification= empty($identification) &&defined('PLUGIN_ID') ? if(empty($identification)) return false; returnpc_base::load_sys_class($classname, PLUGIN_ID :$identification; //调用load_sys_class函数
'plugin'.DIRECTORY_SEPARATOR.$identification.DIRECTORY_SEPARATOR.'classes', $initialize);
} /** * 加载插件函数库 * @param string $func函数文件名称 * @param string $identification 插件标识 */ public static function load_plugin_func($func,$identification) { static$funcs= array(); $identification= empty($identification) &&defined('PLUGIN_ID') ? if(empty($identification)) return false; PLUGIN_ID :$identification;
$path =
'plugin'.DIRECTORY_SEPARATOR.$identification.DIRECTORY_SEPARATOR.'functions'.DIRECTORY_SEPARATOR.$func.'.func.php';
//将路径加密后作为数组的索引
} /** * 加载插件数据模型 * @param string $classname类名 */ public static function load_plugin_model($classname,$identification) { $key = md5($path); if(isset($funcs[$key])) return true; if(file_exists(PC_PATH.$path)) { } $funcs[$key] = true; return true; includePC_PATH.$path; $funcs[$key] = false; return false; } else {
} $identification= empty($identification) &&defined('PLUGIN_ID') ? $path = return self::_load_class($classname,$path); PLUGIN_ID :$identification; 'plugin'.DIRECTORY_SEPARATOR.$identification.DIRECTORY_SEPARATOR.'model'; /** * 加载指定函数 * @param string $func函数名 * @param string $path 地址 */ private static function _load_func($func, $path= '') { } /** * 加载函数库 * @param string $func函数库名 * @param string $path 数组路径 */ private static function _auto_load_func($path= '') { /** * 如果path为空,则使用默认path中所有.func.php结尾的函数库(默认static$funcs= array(); /* * 如果没有指定位置,默认叠加为phpcms/libs/下的某一位置 */ if(empty($path)) $path= 'libs'.DIRECTORY_SEPARATOR.'functions'; $path.=DIRECTORY_SEPARATOR.$func.'.func.php'; $key = md5($path); if(isset($funcs[$key])) return true; /* * 如果存在文件,则包含,否则func[$key]为false */ if(file_exists(PC_PATH.$path)) { } $funcs[$key] = true; return true; includePC_PATH.$path; $funcs[$key] = false; return false; } else {
libs/functions/autoload/*.func.php) */
} /** if(empty($path)) $path= $path.=DIRECTORY_SEPARATOR.'*.func.php'; /** * 返回path包含的函数库文件,组成数组,遍历数组后包含 */ $auto_funcs=array(); $auto_funcs = glob(PC_PATH.DIRECTORY_SEPARATOR.$path); } foreach($auto_funcs as $func_path) { } include$func_path; 'libs'.DIRECTORY_SEPARATOR.'functions'.DIRECTORY_SEPARATOR.'autoload'; if(!empty($auto_funcs) &&is_array($auto_funcs)) { * 是否有自己的扩展文件 * @param string $filepath路径 */ public static function my_path($filepath) { /** * pathinfo返回关联数组,根据key返回用户自定义文件位置 $path = pathinfo($filepath); /** * 形如: phpcms/modules/content/index.php * 如果存在phpcms/modules/content/MY_index.php则返回此路径 * 主要用于区别系统默认的index,添加个性化需求 */ *
if(file_exists($path['dirname'].DIRECTORY_SEPARATOR.'MY_'.$path['basename'])) { return$path['dirname'].DIRECTORY_SEPARATOR.'MY_'.$path['basename'];
} /** * 加载配置文件 * @param string $file 配置文件 * @param string $key 要获取的配置荐 * @param string $default 默认配置。当获取配置项目失败时该值发生作用。 * @paramboolean $reload 强制重新加载。 */ } else { } return false;
{
} public static function load_config($file, $key= '', $default= '', $reload= false) } /** * 如果key为空返回$file整个文件(数组形式) * 否则返回file中某一个key的值(二维数组) */ static$configs= array(); if(!$reload&&isset($configs[$file])) { } /** * 指定默认path为 CACHE_PATH/configs/+用户file */ $path = CACHE_PATH.'configs'.DIRECTORY_SEPARATOR.$file.'.php'; if(file_exists($path)) { } if(empty($key)) { } return$configs[$file]; return$configs[$file][$key]; return$default; } elseif(isset($configs[$file][$key])) { } else { $configs[$file] = include $path; if(empty($key)) { } return$configs[$file]; return$configs[$file][$key]; return$default; } elseif(isset($configs[$file][$key])) { } else { *
该base类代码较多主要定义了一些常量,写了一些加载配置、函数和类的函数,要注意的是该类中define('IN_PHPCMS', true);定义了一个进入入口的标志,用来保证访问的合法性,防止直接访问工程内的一些重要文件。另外一个要注意的是该类中的以下代码: public static function creat_app() {
} return self::load_sys_class('application');
这就是index.php中creat_app()访问的方法,该函数两次调用了pc_base类的方法,传入的application参数引入了application.class.php并返回了一个该类的实例。 该文件在phpcms/libs/classes/下,我们打开这个文件,源码如下:
<?php
/**
* application.class.php PHPCMS应用程序创建类
*
*/
classapplication {
/** * 初始化应用 */ public function __construct() { } /** * 调用事件 */ private function init() { } /** * 加载控制器-接收一个模块路径($m)和一个控制器对象($filename) * @param string $filename 控制器类 * @param string $m 模块 //加载控制器对象 $controller = $this->load_controller(); //判断该类是否存在相应事件 if(method_exists($controller, ROUTE_A)) { } /** * 检查方法权限 */ if(preg_match('/^[_]/i', ROUTE_A)) { exit('You are visiting the action is to protect the private //调用参数类 $param= pc_base::load_sys_class('param'); define('ROUTE_M', $param->route_m()); define('ROUTE_C', $param->route_c()); define('ROUTE_A', $param->route_a()); //调用应用类事件 $this->init(); action'); } else { } exit('Action does not exist.'); //调用该类的ROUTE_A方法 call_user_func(array($controller, ROUTE_A)); } else {
* @return (obj)$filename - 并引入(访问)指定控制器路径 */ private function load_controller($filename= '', $m= '') { if(empty($filename)) $filename= ROUTE_C; if(empty($m)) $m= ROUTE_M; //确定路径形如: $filepath= phpcms/modules/ROUTE_M/ROUTE_C.php
$filepath=
PC_PATH.'modules'.DIRECTORY_SEPARATOR.$m.DIRECTORY_SEPARATOR.$filename.'.php';
} } //若存在指定路径便引入用于访问 if(file_exists($filepath)) { } $classname= $filename; include$filepath; //检查该路径下是否有扩展路径,如果有则引入 if($mypath= pc_base::my_path($filepath)) { } //检查该类是否定义,如果定义,返回一个新类 if(class_exists($classname)){ } exit('Controller does not exist.'); return new $classname; exit('Controller does not exist.'); }else{ $classname= 'MY_'.$filename; include$mypath; } else {
该类就是应用创建类,前面index.php中creat_app()实际上时调用了此类,该类中首先加载了参数类param.class.php,该类定义了一些路由法则,因为PHPCMS时单入口文件,其访问方式如同/index?m=content&c=index&a=init ,该参数类主要就是定义了一些路由法则的函数,判断该访问类的哪个事件。
后面定义的常量就是调用类参数类的路由方法,$this->init()调用了自己类中的init()方法,其中又调用了load_controller(),较为详细的说明可以参考以上的注释,到此该cms就可以按照用户的路由规则进行访问,为了防止直接访问其它受保护的文件,可以在该文件前面加一句defined('IN_PHPCMS') or exit('No permission resources.');,这样就可以防止恶意的直接访问,整个系统就会按照进入base类一步一步按照路由规则进行web系统的访问。这就是所谓的单入口技术了。
此文只是简要的描述了PHPCMS中单入口的一些源码和要点,只适合读者朋友简单的参考交流,希望能多少能有所帮助。PHPCMS是国内一个优秀的开源项目,想要了解更多PHPCMS的框架信息,还需读者参考更多资料,多多学习和使用才能有更多的认识。