colaphp 学习笔记(四)实战
上一篇展示了colaphp的性能测试,这一篇开始具体的代码分析。
1.修改路由实现controllers目录创建子目录
目的是实现接口控制器和页面控制器按文件夹分离,比如:http://my.letv.com/api/follow/add?cid=1&pid=5566
(1)在创建的应用项目目录下index.php定义APP_DIR
APP_DIR
define(
'APP_DIR'
, dirname(
__FILE__
));
1 public function setDispatchInfo($dispatchInfo = null) 2 { 3 if (null === $dispatchInfo) { 4 $router = $this->getRouter(); 5 // add urls to router from config 6 if (isset(self::$_config['_urls'])) $router->add(self::$_config['_urls'], false); 7 $pathInfo = $this->getPathInfo(); 8 // add support directory 9 $pathInfos = explode("/", trim($pathInfo,'/'), 2); 10 if(is_dir(APP_DIR.DS.self::config('_controllersHome').DS.current($pathInfos))){ 11 $dispatchInfo = $router->match(next($pathInfos)); 12 $dispatchInfo['directory'] = reset($pathInfos); 13 }else{ 14 $dispatchInfo = $router->match($pathInfo); 15 } 16 } 17 $this->_dispatchInfo = $dispatchInfo; 18 return $this; 19 }
(3)同时修改dispatch方法
dispatch
1 public function dispatch() 2 { 3 if (!$this->getDispatchInfo()) { 4 throw new Cola_Exception_Dispatch('No dispatch info found'); 5 } 6 extract($this->_dispatchInfo); 7 if (isset($file)) { 8 if (!file_exists($file)) { 9 throw new Cola_Exception_Dispatch("Can't find dispatch file:{$file}"); 10 } 11 require_once $file; 12 } 13 if (isset($controller)) { 14 if(!empty($directory)){ 15 if (!self::loadClass($controller, self::config('_controllersHome').DS.$directory)) { 16 throw new Cola_Exception_Dispatch("Can't load controller:{$controller}"); 17 } 18 }elseif (!self::loadClass($controller, self::config('_controllersHome'))) { 19 throw new Cola_Exception_Dispatch("Can't load controller:{$controller}"); 20 } 21 $cls = new $controller(); 22 } 23 if (isset($action)) { 24 $func = isset($cls) ? array($cls, $action) : $action; 25 if (!is_callable($func, true)) { 26 throw new Cola_Exception_Dispatch("Can't dispatch action:{$action}"); 27 } 28 call_user_func($func); 29 } 30 }
2.ColaPHP运行流程浅析
我们把下载下来的ColaPHP-1.2-ga.zip解压并上传到项目的目录时,会有两个文件夹,cola文件夹是cola的核心文件,demo文件夹是一个示例项目。
(1)当我们请求http://my.letv.com时会请求demo文件夹下的index.php,index.php也是所有动态请求的入口文件。
index.php做了什么呢?php运行错误开启,级别设置,时区设置,加载cola核心Cola.php文件,实例化cola,启动运行,分配请求。
index.php
1 <?php 2 error_reporting(E_ALL); 3 ini_set('display_errors', 'on'); 4 date_default_timezone_set('Asia/Shanghai'); 5 define('APP_DIR', dirname(__FILE__)); 6 require '../Cola/Cola.php'; 7 $cola = Cola::getInstance(); 8 9 //$benchmark = new Cola_Com_Benchmark(); 10 $cola->boot()->dispatch(); 11 //echo "<br />cost:", $benchmark->cost(), 's';
(2)我们来看看cola对象的构建函数:
定义cola核心的Router Model View Controller Com等文件路径,实例化Cola的Config对象赋值给本身的config元素,将核心文件的配置路径返回给本身的_congfig元素。接下来执行本身的registerAutoload方法,一个自动加载类的方法。
__construct
1 protected function __construct() 2 { 3 $config['_class'] = array( 4 'Cola_Router' => COLA_DIR . '/Router.php', 5 'Cola_Model' => COLA_DIR . '/Model.php', 6 'Cola_View' => COLA_DIR . '/View.php', 7 'Cola_Controller' => COLA_DIR . '/Controller.php', 8 'Cola_Com' => COLA_DIR . '/Com.php', 9 'Cola_Com_Widget' => COLA_DIR . '/Com/Widget.php', 10 'Cola_Exception' => COLA_DIR . '/Exception.php' 11 ); 12 self::$config = new Cola_Config($config); 13 self::$_config = &self::$config->reference(); 14 Cola::registerAutoload();
(3)接下cola对象调取了本身的boot方法:
boot
方法做了两件事情,加载应用demo目录下的config.inc.php文件,之后执行了config对象的merge方法,将cola核心文件路径配
置和我们自定的配置比如数据库,缓存,项目的models、controllers、views文件夹名称等配置合并,返回对象本身。
boot
1 public static function boot($config = 'config.inc.php') 2 { 3 if (is_string($config)) { 4 include $config; 5 } 6 if (!is_array($config)) { 7 throw new Exception('Boot config must be an array, if you use config file, the variable should be named $config'); 8 } 9 self::$config->merge($config); 10 return self::$_instance; 11 }
(4)最后执行cola对象的dispatch方法:
dispatch
方法,又做了什么呢?首先执行cola对象的getDispatchInfo,这个方法最终会根据请求地址获取到请求的controllers的目录,名
称,action名称,并赋值给cola对象的dispatchInfo元素,我们打印dispatchInfo会输出类似以下信息:
array(3)
{ ["controller"]=> string(15) "IndexController" ["action"]=>
string(10) "testAction" ["directory"]=> string(3) "api" },对此执行extract函数,键名用于变量名,键值用于变量值。如果有controller加载controller文件,最后执行call_user_func函数执行此controller类的action方法。
dispatch
1 public function dispatch() 2 { 3 if (!$this->getDispatchInfo()) { 4 throw new Cola_Exception_Dispatch('No dispatch info found'); 5 } 6 extract($this->_dispatchInfo); 7 if (isset($file)) { 8 if (!file_exists($file)) { 9 throw new Cola_Exception_Dispatch("Can't find dispatch file:{$file}"); 10 } 11 require_once $file; 12 } 13 if (isset($controller)) { 14 if(!empty($directory)){ 15 if (!self::loadClass($controller, self::config('_controllersHome').DS.$directory)) { 16 throw new Cola_Exception_Dispatch("Can't load controller:{$controller}"); 17 } 18 }elseif (!self::loadClass($controller, self::config('_controllersHome'))) { 19 throw new Cola_Exception_Dispatch("Can't load controller:{$controller}"); 20 } 21 $cls = new $controller(); 22 } 23 if (isset($action)) { 24 $func = isset($cls) ? array($cls, $action) : $action; 25 if (!is_callable($func, true)) { 26 throw new Cola_Exception_Dispatch("Can't dispatch action:{$action}"); 27 } 28 call_user_func($func); 29 } 30 }
(5)最后我们来分析cola.php的自动加载处理:
loadClass
方法有两个参数,类名和路径,如果我们今后开发过程中想要加载某个类,直接执行这个方法,传两个参数的值。这是其一,其二,若$className为空
值,loadClass会加载cola对象的元素_config内的文件,都是colaphp运行的核心文件,在Cola目录下。
loadClass
1 public static function loadClass($className, $dir = '') 2 { 3 if (class_exists($className, false) || interface_exists($className, false)) { 4 return true; 5 } 6 if (isset(self::$_config['_class'][$className])) { 7 include self::$_config['_class'][$className]; 8 return true; 9 } 10 if (empty($dir)) { 11 $dir = ('Cola' == substr($className, 0, 4)) ? substr(COLA_DIR, 0, -4) : ''; 12 } else { 13 $dir = rtrim($dir,'\\/') . DIRECTORY_SEPARATOR; 14 } 15 $file = str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php'; 16 $classFile = $dir . $file; 17 if (file_exists($classFile)) { 18 include $classFile; 19 } 20 return (class_exists($className, false) || interface_exists($className, false)); 21 }