phpcms 源码分析
来源:互联网 发布:json数组解析 编辑:程序博客网 时间:2024/05/30 22:49
目录结构
| -- api 接口文件目录| -- caches 缓存文件目录 | -- configs 系统配置文件目录 (配置目录放在缓存目录里感觉不太合理) | -- cache_* 缓存相关目录| -- phpcms phpcms 框架主目录 | -- languages 框架语言包目录 | -- libs 框架主类库、主函数库目录 | -- model 框架数据库模型目录 | -- modules 框架模块目录 | -- templates 框架系统模板目录| -- phpsso_server phpsso 主目录| -- statics 静态资源相关 | -- css css 目录 | -- images 图片目录 | -- js js 目录| -- uploadfile 网站附件目录| -- admin.php 后台登陆入口| -- index.php 程序主入口| -- crossdomain.xml flash 跨域传输文件| -- robots.txt 搜索引擎蜘蛛限制配置文件| -- favicon.ico 系统 icon 图标| -- js.html JS站群跨域| -- plugin.php 插件相关| -- api.php 外部 api 调用地址
生命周期
index.php 入口文件
define('PHPCMS_PATH', dirname(__FILE__).DIRECTORY_SEPARATOR);include PHPCMS_PATH.'/phpcms/base.php';pc_base::creat_app();
我们看到入口文件定义了一个常量 PHPCMS_PATH
为当前路径, 同时加载 phpcms
目录下的 base.php
文件,随后又调用 pc_base::create_app()
。 非常类似 Yii,创建一个运行的 app,也是常见的一种入口文件模式。
base.php 核心文件
接下来我们来看一下比较重要的一个文件 base.php
, 位于 phpcms/base.php
。
文件阅读主要分为两个部分
第一部分定义了一堆的路径常量,这在框架的初始化过程中也很常见。 剩下的主要是一个 pc_base
的类定义,这个是我们重点要关注的类。
先来看一下第一部分,常量定义。
首先第一行我们注意到 define('IN_PHPCMS', true);
定义了一个 IN_PHPCMS 的常量,其实目的是确保从入口文件进入的,你会在模块中经常见到如下定义:
defined('IN_PHPCMS') or exit('No permission resources.');
如果我们通过 URL 直接访问这个目录下的文件,由于没有定义 IN_PHPCMS 常量,便无法访问。在一些老的代码里我们经常会看到这样的用法,比如 CI、Discuz。其实我们采用程序与入口分离即可,把入口文件以及静态资源单独放一个 www 作为根目录,而把框架核心放到一个其他目录,分配好权限即可。也不用每个文件头部这么蛋疼的写这么一句了。
接着往下看,就是一些路径,时间,字符集等的一些常量设定了。
注意到如下这句:
//输出页面字符集header('Content-type: text/html; charset='.CHARSET);
这里默认输出的就是 text/html
类型。 不过5.4 以上就不用手动写这句了,会默认发送 utf-8。
期间会调用 pc_base
的一些静态方法。比如
//加载公用函数库pc_base::load_sys_func('global');pc_base::load_sys_func('extention');pc_base::auto_load_func();//加载配置pc_base::load_config('system','errorlog')
我们来一个一个整理。
pc_base::load_sys_func
pc_base::load_sys_func
这个方法主要用于加载系统函数, 位于 phpcms/libs/functions
下。 命名规则是xx.func.php
load_sys_func
实际上是一个代理方法,本质调用的是 pc_base 类的 _load_func
私有方法,会使用静态变量数组来缓存结果,key 则是 md5 过后的 path 值。
pc_base::auto_load_func
我们在来看一下 pc_base::auto_load_func
看名字像是会自动加载一些函数类库,类似 composer
中的classmap
, 看下具体实现
private static function _auto_load_func($path = '') { if (empty($path)) $path = 'libs'.DIRECTORY_SEPARATOR.'functions'.DIRECTORY_SEPARATOR.'autoload'; $path .= DIRECTORY_SEPARATOR.'*.func.php'; $auto_funcs = glob(PC_PATH.DIRECTORY_SEPARATOR.$path); if(!empty($auto_funcs) && is_array($auto_funcs)) { foreach($auto_funcs as $func_path) { include $func_path; } } }
原来是会自动加载 phpcms/libs/functions/autoload
目录下的所有类,使用 glob
函数获取所有文件后遍历加载。
至此,我们知道一开始会载入一个全局的函数库(global.func.php)和一个自定义的函数库(extension.func.php),然后自动加载 autoload
目录下的类库。 加载公用函数库部分结束。
pc_base::load_config
我们接着看一下加载配置文件的函数 pc_base::load_config('文件','key')
, 我们查找一下caches/configs/system.php
发现这个文件中返回的就是一个二维数组。在很多框架中,config 的配置都是类似的实现,比如 laravel 中 Config::get('database.default')
就代表查找配置目录中 database.php 文件中的 default 对应的配置。所以 pc_base::load_config('system', 'errorlog')
的意思就是获取caches/configs/system.php
中的 errorlog
键值。
来看一下 pc_base::load_config
的实现, 也是用一个静态变量数组来存储。
逻辑大概如下:
- 如果静态变量中已经有对应的 key 值,直接返回。
- 设定读取的配置目录 位于
caches/configs
下。 - 如果没有指定 key ,那么会返回整个文件数组。
- 可以指定额外的 default 值, 当获取的 key 对应的值不存在的时候会返回 default 值。
- 把对应的键值写入到静态变量中方便下一次调用。
纵观几个方法,无一不是通过这种静态变量的形式缓存的,同时也把载入路径写死在函数中,或者通过参数传递,已经不是 DRY 的做法了。这可能是具有一定代表意义的时代性的写法,也影响了不少人,可以从当时流行的其他代码中窥见。
其实在现在来看,PHP 5.3 以后带来的命名空间特性,以及 composer 的流行,已经不需要再这样手动载入了,这些方法本质都只是载入对应的类库文件,composer
完全可以满足,而只需要一行 require 'vendor/autoload.php';
。
我们总结一下 pc_base
的一些常见静态方法
pc_base::load_sys_class
加载系统类,位于 phpcms/libs/classes
pc_base::load_app_class
加载模块中的类,位于 modules/模块目录/classes
pc_base::load_model
加载模型类,位于 phpcms/model
pc_base::load_sys_func
加载系统函数,位于 phpcms/libs/functions
pc_base::auto_load_func
自动载入系统函数库,位于 phpcms/libs/functions/autoload
pc_base::load_app_func
加载模块函数,位于 modules/模块目录/functions
pc_base::load_config
加载配置文件,位于 caches/configs/xxx.php
pc_base::create_app
我们回到 index.php
的最后一行的方法 pc_base::create_app()
,这也是刚刚没有分析的一个方法,我们来看一下定义。
/** * 初始化应用程序 */public static function creat_app() { return self::load_sys_class('application');}
可以看到本质是调用 load_sys_class
方法,通过前面的分析我们知道其实是会去加载phpcms/libs/classes/application.class.php
文件,这里在提一点,如果是载入类,支持使用 MY_xxxx
的形式来扩展自定义的类。关键部分如下:
if (file_exists(PC_PATH.$path.DIRECTORY_SEPARATOR.$classname.'.class.php')) { include PC_PATH.$path.DIRECTORY_SEPARATOR.$classname.'.class.php'; $name = $classname; //my_path 方法会判断是否存在一个以 MY_ 开头的同名类文件 if ($my_path = self::my_path(PC_PATH.$path.DIRECTORY_SEPARATOR.$classname.'.class.php')) { include $my_path; $name = 'MY_'.$classname; } if ($initialize) { $classes[$key] = new $name; } else { $classes[$key] = true; } return $classes[$key];} else { return false;}
假设我们访问的是 modules/content/index.php
,这也是默认的首页路由,我们可以通过创建一个MY_index.php
来覆盖原有的 index.php
,这也是 PHPCMS 的一种扩展机制。
因此,pc_base::create_app()
本质就是实例化 phpcms/libs/classes/application.class.php
application.class.php
现在我们来看一下 application.class.php
/** * 构造函数 */public function __construct() { $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();}
实例化的时候注入了一个 param
类 ,猜想是解析地址成 c、m、a 之类的,然后调用 init
初始化。
params.class.php
我们来看一下 params
类
比较关键的几个地方:
$this->route_config = pc_base::load_config('route', SITE_URL) ? pc_base::load_config('route', SITE_URL) : pc_base::load_config('route', 'default');
主要是根据当前的主机地址来获取 route_config
如果没有的话采用 default
默认路由配置。 配置文件位于caches/config/route.php
return array( 'default'=>array('m'=>'content', 'c'=>'index', 'a'=>'init'),);
这也就意味着默认情况下会访问 modules/content/index.php
的 init 方法。
而route_* 系列方法则会把当前 URL 参数的 m、c、a 通过安全处理后返回对应的值。
我们回到 application.class.php
的构造函数,可以看到通过调用 route_*
系列,可以把对应的module,controller 和 action 绑定到 ROUTE_*
常量中。
init
终于到了最后的 init
方法了
/** * 调用件事 */private function init() { $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 action'); } else { call_user_func(array($controller, ROUTE_A)); } } else { exit('Action does not exist.'); }}
首先吐槽下,调用件事
这个注释,这不是我写错了,而是源码中确实存在的,也是醉了,原谅我语死早。
第一行调用 load_controller
获取控制器实例,这个控制器位于一个具体模块下也就是 m=?
中 m 对应的值,同时也支持 MY_xx
的形式覆盖。注意到这里会判断以_开头的方法,会认为是受保护的方法,不允许访问,命名的时候需要注意。
最后调用 call_user_func()
至此框架流程就算是结束了。是一个比较典型的传统 MVC 的框架形式,而现代化的那些框架则抽象了 HTTP 的整个流程,比如 symfony
的 http component
,带来了更多的可能性以及扩展性,值得我们学习跟上脚步。
至于其他的重头戏都在 modules
目录中,由于和业务相关就不重点分析了。了解了这整一个流程,在做相关的开发相信就会更得心应手。
--EOF--
- phpcms 源码分析
- phpcms分析
- phpcms template函数分析
- PHPCMS数据表分析---v9版本
- phpcms头部代码详细分析
- phpcms头部代码详细分析
- phpcms头部代码详细分析
- phpcms整站代码分析
- phpcms model.class.php分析
- 【phpcms-v9】phpcms-v9中model.class.php文件分析
- 【phpcms-v9】phpcms-v9中缓存COOKIE分析
- 【phpcms-v9】phpcms-v9中系统管理员登陆页面控制器文件分析:phpcms/modules/admin/index.php
- 【phpcms-v9】phpcms-v9应用程序创建类phpcms/libs/classes/application.class.php文件分析
- 【phpcms-v9】phpcms-v9中url路由规则文件分析:phpcms/libs/classes/param.class.php
- 【phpcms-v9】phpcms-v9数据源调用的控制器文件分析phpcms/modules/dbsource/call.php
- phpcms
- phpcms
- PHPCMS
- DM8127 彩转黑的实现
- iOS证书配置,打包上传的一些小结
- Missing include: urn:docbkx:stylesheet
- Asp.net用回车键提交
- #include“stdafx.h”详解
- phpcms 源码分析
- 注解apt工具示例程序
- 使用appdelegate的变量做全局变量的方法
- coe_xfr_sql_profile.sql
- python2.7练习 写一个简单的文本编辑器
- C++ Primer第四版习题--3.10
- MyBatis (2)配置
- OpenCV 获取摄像头并显示摄像头视频
- Another app is currently holding the yum lock; The other application is: yum-updatesd-he