转载请注明:TheViper http://www.cnblogs.com/TheViper/ 本文将对yii中的mvc,路由器,filter,组件机制等最主要的部分进行自己的一点浅析,力求说明自己做一个php mvc不是那么的遥不可及,其实是很简单的。 源码基于yii 1.13,为了方便说明,我对其进行了大量的裁剪,不过还是让他保有上面的那些最重要的功能。裁剪下来,其实没有几个文件了,而且每个文件代码最多100多行,避免因为代码太多而懒得看。 所谓的mvc都是让所有请求从一个地方进去,通过对请求,配置的解析,分发到对应的类方法中。 首先当然是入口文件,index.php. 1 <?php 2 $app = "app"; 3 $yii = dirname ( __FILE__ ) . ‘/framework/yii.php‘; 4 $config = dirname ( __FILE__ ) . ‘/app/protected/config/main.php‘;//载入配置 5 require_once ($yii); 6 Yii::createWebApplication ( $config )->run (); 引入yii.php 1 <?php 2 define ( "VIEWS_DIR", "$app/protected/views/" ); 3 define ( "CONTROLLERS_DIR", "$app/protected/controllers/" ); 4 require(dirname(__FILE__).‘/YiiBase.php‘); 5 class Yii extends YiiBase 6 { 7 } 原来yii是个空的类啊,去看YiiBase. 1 <?php 2 defined ( ‘YII_PATH‘ ) or define ( ‘YII_PATH‘, dirname ( __FILE__ ) ); 3 class YiiBase { 4 public static $classMap = array (); 5 public static $enableIncludePath = true; 6 private static $_aliases = array ( 7 ‘system‘ => YII_PATH 8 ); // alias => path 9 private static $_imports = array (); // alias => class name or directory 10 private static $_includePaths; // list of include paths 11 private static $_app; 12 private static $_logger; 13 public static function createWebApplication($config = null) { 14 return self::createApplication ( ‘CWebApplication‘, $config ); 15 } 16 public static function createApplication($class, $config = null) { 17 return new $class ( $config ); 18 } 19 public static function app() { 20 return self::$_app; 21 } 22 //别名路径 23 public static function getPathOfAlias($alias) { 24 if (isset ( self::$_aliases [$alias] )) 25 return self::$_aliases [$alias]; 26 elseif (($pos = strpos ( $alias, ‘.‘ )) !== false) { 27 $rootAlias = substr ( $alias, 0, $pos ); 28 if (isset ( self::$_aliases [$rootAlias] )) 29 return self::$_aliases [$alias] = rtrim ( self::$_aliases [$rootAlias] . DIRECTORY_SEPARATOR . str_replace ( ‘.‘, DIRECTORY_SEPARATOR, substr ( $alias, $pos + 1 ) ), ‘*‘ . DIRECTORY_SEPARATOR ); 30 } 31 return false; 32 } 33 public static function setPathOfAlias($alias, $path) { 34 if (empty ( $path )) 35 unset ( self::$_aliases [$alias] ); 36 else 37 self::$_aliases [$alias] = rtrim ( $path, ‘\\/‘ ); 38 } 39 public static function setApplication($app) { 40 if (self::$_app === null || $app === null) 41 self::$_app = $app; 42 } 43 public static function import($alias, $forceInclude = false) { 44 if (isset ( self::$_imports [$alias] )) // previously imported 45 return self::$_imports [$alias]; 46 47 if (class_exists ( $alias, false ) || interface_exists ( $alias, false )) 48 return self::$_imports [$alias] = $alias; 49 if (($pos = strrpos ( $alias, ‘.‘ )) === false) // a simple class name 50 { 51 // try to autoload the class with an autoloader if $forceInclude is true 52 if ($forceInclude && (Yii::autoload ( $alias, true ) || class_exists ( $alias, true ))) 53 self::$_imports [$alias] = $alias; 54 return $alias; 55 } 56 57 $className = ( string ) substr ( $alias, $pos + 1 ); 58 $isClass = $className !== ‘*‘; 59 60 if ($isClass && (class_exists ( $className, false ) || interface_exists ( $className, false ))) 61 return self::$_imports [$alias] = $className; 62 63 if (($path = self::getPathOfAlias ( $alias )) !== false) { 64 if ($isClass) { 65 if ($forceInclude) { 66 if (is_file ( $path . ‘.php‘ )) 67 require ($path . ‘.php‘); 68 else 69 throw new CException ( Yii::t ( ‘yii‘, ‘Alias "{alias}" is invalid. Make sure it points to an existing PHP file and the file is readable.‘, array ( 70 ‘{alias}‘ => $alias 71 ) ) ); 72 self::$_imports [$alias] = $className; 73 } else 74 self::$classMap [$className] = $path . ‘.php‘; 75 return $className; 76 } else // a directory 77 { 78 if (self::$_includePaths === null) { 79 self::$_includePaths = array_unique ( explode ( PATH_SEPARATOR, get_include_path () ) ); 80 if (($pos = array_search ( ‘.‘, self::$_includePaths, true )) !== false) 81 unset ( self::$_includePaths [$pos] ); 82 } 83 84 array_unshift ( self::$_includePaths, $path ); 85 86 if (self::$enableIncludePath && set_include_path ( ‘.‘ . PATH_SEPARATOR . implode ( PATH_SEPARATOR, self::$_includePaths ) ) === false) 87 self::$enableIncludePath = false; 88 89 return self::$_imports [$alias] = $path; 90 } 91 } 92 } 93 //创建组件实例 94 public static function createComponent($config) { 95 if (is_string ( $config )) { 96 $type = $config; 97 $config = array (); 98 } elseif (isset ( $config [‘class‘] )) { 99 $type = $config [‘class‘]; 100 unset ( $config [‘class‘] ); 101 } 102 if (! class_exists ( $type, false )) { 103 $type = Yii::import ( $type, true ); 104 } 105 if (($n = func_num_args ()) > 1) { 106 $args = func_get_args (); 107 if ($n === 2) 108 $object = new $type ( $args [1] ); 109 elseif ($n === 3) 110 $object = new $type ( $args [1], $args [2] ); 111 elseif ($n === 4) 112 $object = new $type ( $args [1], $args [2], $args [3] ); 113 else { 114 unset ( $args [0] ); 115 $class = new ReflectionClass ( $type ); 116 // Note: ReflectionClass::newInstanceArgs() is available for PHP 5.1.3+ 117 // $object=$class->newInstanceArgs($args); 118 $object = call_user_func_array ( array ( 119 $class, 120 ‘newInstance‘ 121 ), $args ); 122 } 123 } else 124 $object = new $type (); 125 foreach ( $config as $key => $value ) 126 $object->$key = $value; 127 128 return $object; 129 } 130 //按需加载相应的php 131 public static function autoload($className) { 132 include self::$_coreClasses [$className]; 133 } 134 private static $_coreClasses = array ( 135 ‘CApplication‘ => ‘/base/CApplication.php‘, 136 ‘CModule‘ => ‘/base/CModule.php‘, 137 ‘CWebApplication‘ => ‘/base/CWebApplication.php‘, 138 ‘CUrlManager‘ => ‘CUrlManager.php‘, 139 ‘CComponent‘ => ‘/base/CComponent.php‘ 140 , ‘CUrlRule‘ => ‘CUrlRule.php‘, 141 ‘CController‘ => ‘CController.php‘, 142 ‘CInlineAction‘ => ‘/actions/CInlineAction.php‘, 143 ‘CAction‘ => ‘/actions/CAction.php‘, 144 ‘CFilterChain‘ => ‘/filters/CFilterChain.php‘, 145 ‘CFilter‘ => ‘/filters/CFilter.php‘, 146 ‘CList‘ => ‘/collections/CList.php‘, 147 ‘CHttpRequest‘ => ‘CHttpRequest.php‘, 148 ‘CDb‘ => ‘CDb.php‘, 149 ‘CInlineFilter‘ => ‘filters/CInlineFilter.php‘ 150 ); 151 } 152 153 spl_autoload_register ( array ( 154 ‘YiiBase‘, 155 ‘autoload‘ 156 ) ); 看似很多,其实就三个地方注意下就可以了 1.spl_autoload_register,用这个就可以实现传说中的按需加载相应的php了,坑爹啊。 2.createComponent($config)这个方法是yii组件调用的核心。在配置中注册的所有组件都是通过它获取组件类的实例的。比如配置: 1 <?php 2 return array ( 3 ‘basePath‘ => dirname ( __FILE__ ) . DIRECTORY_SEPARATOR . ‘..‘, 4 5 ‘import‘ => array ( 6 ‘application.util.*‘ 7 ), 8 9 ‘components‘ => array ( 10 ‘db‘ => array ( 11 ‘class‘ => ‘CDb‘, 12 ‘driver‘ => ‘mysql‘, 13 ‘hostname‘ => ‘localhost‘, 14 ‘username‘ => ‘root‘, 15 ‘password‘ => ‘‘, 16 ‘database‘ => ‘youtube‘ 17 ), 18 ‘urlManager‘ => array ( 19 ‘urlFormat‘ => ‘path‘, 20 ‘rules‘ => array ( 21 ‘comment_reply/<a:\d+>/<ci:\d+>‘ => ‘reply/load_comment_reply‘, 22 ‘b/<id:\d+>‘ => array ( 23 ‘video/broadcast‘, 24 ‘urlSuffix‘ => ‘.html‘ 25 ), 26 ‘c/<list_start:\d+>‘ => ‘video/list_more_video‘, 27 ‘u/reg‘ => ‘user/reg‘, 28 ‘v/upload‘ => ‘video/upload_video‘, 29 ‘login‘ => ‘user/to_login‘, 30 ‘show_chanel/<chanel_id:\d+>‘ => ‘show/chanel‘ , 31 ‘show/<show_id:\d+>‘ => ‘show/show‘, 32 ) 33 ) 34 ) 35 ); 36 ?> 这个文件就返回了个map,里面components中的db,urlManager便是我注册的系统中的组件,里面的array便是组件的参数. 从源码中看到$type = $config [‘class‘];$object = new $type;就创建了注册的类实例了。 3.import($alias, $forceInclude = false)。作用:导入一个类或一个目录。导入一个类就像包含相应的类文件。 主要区别是导入一个类比较轻巧, 它仅在类文件首次引用时包含。这个也是yii的一个核心优化点。 这个在上面createComponent($config)中有用到, if (!class_exists ( $type, false )) { $type = Yii::import ( $type, true ); } 如果$type类没有定义,就去导入。有些组件,核心在yii中多次create调用,这样就保证了仅在类文件首次引用时导入。 下面分析index.php中Yii::createWebApplication ( $config )的调用过程。 这个调用是去了CWebApplication。 1 <?php 2 class CWebApplication extends CApplication { 3 public $controllerNamespace; 4 private $_controllerPath; 5 private $_viewPath; 6 private $_systemViewPath; 7 private $_controller; 8 public $controllerMap=array(); 9 public function processRequest() {//开始执行请求 10 //获取urlManager组件,解析请求,得到controller/action这种格式的string, 11 //并且将隐藏参数与请求的参数一一对应,匹配起来,写入$_REQUEST中 12 $route = $this->getUrlManager ()->parseUrl ($this->getRequest()); 13 $this->runController ( $route ); 14 } 15 public function getRequest() {//获取request组件 16 return $this->getComponent ( ‘request‘ ); 17 } 18 protected function registerCoreComponents() {//注册核心组件 19 parent::registerCoreComponents (); 20 } 21 //执行contronller 22 public function runController($route) { 23 if (($ca = $this->createController ( $route )) !== null) { 24 list ( $controller, $actionID ) = $ca; 25 $oldController = $this->_controller; 26 $this->_controller = $controller; 27 $controller->init ();//钩子,在执行action方法前调用,子类去实现 28 $controller->run ( $actionID );//开始转入controller类中action方法的执行 29 $this->_controller = $oldController; 30 } 31 } 32 //创建controller类实例,从controller/action这种格式的string中解析出$controller, $actionID 33 public function createController($route, $owner = null) { 34 if ($owner === null) 35 $owner = $this; 36 if (($route = trim ( $route, ‘/‘ )) === ‘‘) 37 $route = $owner->defaultController; 38 39 $route .= ‘/‘; 40 while ( ($pos = strpos ( $route, ‘/‘ )) !== false ) { 41 $id = substr ( $route, 0, $pos ); 42 if (! preg_match ( ‘/^\w+$/‘, $id )) 43 return null; 44 $id = strtolower ( $id ); 45 $route = ( string ) substr ( $route, $pos + 1 ); 46 if (! isset ( $basePath )) // first segment 47 { 48 $basePath = $owner->getControllerPath (); 49 $controllerID = ‘‘; 50 } else { 51 $controllerID .= ‘/‘; 52 } 53 $className = ucfirst ( $id ) . ‘Controller‘; 54 $classFile = $basePath . DIRECTORY_SEPARATOR . $className . ‘.php‘; 55 56 if (is_file ( $classFile )) { 57 if (! class_exists ( $className, false )) 58 require ($classFile); 59 if (class_exists ( $className, false ) && is_subclass_of ( $className, ‘CController‘ )) { 60 $id [0] = strtolower ( $id [0] ); 61 return array ( 62 new $className ( $controllerID . $id, $owner === $this ? null : $owner ), 63 $this->parseActionParams ( $route ) 64 ); 65 } 66 return null; 67 } 68 $controllerID .= $id; 69 $basePath .= DIRECTORY_SEPARATOR . $id; 70 } 71 } 72 protected function parseActionParams($pathInfo) { 73 if (($pos = strpos ( $pathInfo, ‘/‘ )) !== false) { 74 $manager = $this->getUrlManager ();//再次获取urlManager,在上面第一次调用中已经导入。 75 $manager->parsePathInfo ( ( string ) substr ( $pathInfo, $pos + 1 ) ); 76 $actionID = substr ( $pathInfo, 0, $pos ); 77 return $manager->caseSensitive ? $actionID : strtolower ( $actionID ); 78 } else 79 return $pathInfo; 80 } 81 public function getControllerPath() { 82 if ($this->_controllerPath !== null) 83 return $this->_controllerPath; 84 else 85 return $this->_controllerPath = $this->getBasePath () . DIRECTORY_SEPARATOR . ‘controllers‘; 86 } 87 //两个钩子,子类去实现 88 public function beforeControllerAction($controller, $action) { 89 return true; 90 } 91 public function afterControllerAction($controller, $action) { 92 } 93 protected function init() { 94 parent::init (); 95 } 96 } 没有构造方法,构造方法在父类CApplication里面。 1 <?php 2 abstract class CApplication extends CModule { 3 private $_id; 4 private $_basePath; 5 abstract public function processRequest(); 6 public function __construct($config = null) { 7 if (is_string ( $config )) 8 $config = require ($config); 9 Yii::setApplication ( $this );//保存整个app实例 10 if (isset ( $config [‘basePath‘] )) { 11 $this->setBasePath ( $config [‘basePath‘] ); 12 unset ( $config [‘basePath‘] ); 13 } else 14 $this->setBasePath ( ‘protected‘ ); 15 //设置别名,后面就可以用application表示basePath了 16 Yii::setPathOfAlias ( ‘application‘, $this->getBasePath () ); 17 //钩子,模块 预 初始化时执行,子类实现。不过这时,配置还没有写入框架 18 $this->preinit (); 19 $this->registerCoreComponents (); 20 //父类实现 21 $this->configure ( $config ); 22 //加载静态应用组件 23 $this->preloadComponents (); 24 //这才开始初始化模块 25 $this->init (); 26 } 27 protected function registerCoreComponents() { 28 $components = array ( 29 ‘request‘ => array ( 30 ‘class‘ => ‘CHttpRequest‘ 31 ), 32 ‘urlManager‘ => array ( 33 ‘class‘ => ‘CUrlManager‘ 34 ) 35 ); 36 37 $this->setComponents ( $components );//父类实现 38 } 39 public function run() { 40 $this->processRequest (); 41 } 42 public function getId() { 43 if ($this->_id !== null) 44 return $this->_id; 45 else 46 return $this->_id = sprintf ( ‘%x‘, crc32 ( $this->getBasePath () . $this->name ) ); 47 } 48 public function setId($id) { 49 $this->_id = 上一篇 主要分析了Yii::createWebApplication ( $config )->run ();的createWebApplication ( $config )部分,这篇分析后面的。 run()也是不在CWebApplication里面,在CApplication 里。 1 <?php 2 abstract class CApplication extends CModule { 3 private $_id; 4 private $_basePath; 5 abstract public function processRequest(); 6 public function __construct($config = null) { 7 if (is_string ( $config )) 8 $config = require ($config); 9 Yii::setApplication ( $this );//保存整个app实例 10 if (isset ( $config [‘basePath‘] )) { 11 $this->setBasePath ( $config [‘basePath‘] ); 12 unset ( $config [‘basePath‘] ); 13 } else 14 $this->setBasePath ( ‘protected‘ ); 15 //设置别名,后面就可以用application表示basePath了 16 Yii::setPathOfAlias ( ‘application‘, $this->getBasePath () ); 17 //钩子,模块 预 初始化时执行,子类实现。不过这时,配置还没有写入框架 18 $this->preinit (); 19 $this->registerCoreComponents (); 20 //父类实现 21 $this->configure ( $config ); 22 //加载静态应用组件 23 $this->preloadComponents (); 24 //这才开始初始化模块 25 $this->init (); 26 } 27 protected function registerCoreComponents() { 28 $components = array ( 29 ‘request‘ => array ( 30 ‘class‘ => ‘CHttpRequest‘ 31 ), 32 ‘urlManager‘ => array ( 33 ‘class‘ => ‘CUrlManager‘ 34 ) 35 ); 36 37 $this->setComponents ( $components );//父类实现 38 } 39 public function run() { 40 $this->processRequest (); 41 } 42 public function getId() { 43 if ($this->_id !== null) 44 return $this->_id; 45 else 46 return $this->_id = sprintf ( ‘%x‘, crc32 ( $this->getBasePath () . $this->name ) ); 47 } 48 public function setId($id) { 49 $this->_id = $id; 50 } 51 public function getBasePath() { 52 return $this->_basePath; 53 } 54 public function setBasePath($path) { 55 if (($this->_basePath = realpath ( $path )) === false || ! is_dir ( $this->_basePath )) 56 return; 57 } 58 public function getDb() { 59 return $this->getComponent ( ‘db‘ );//父类实现 60 } 61 public function getUrlManager() { 62 return $this->getComponent ( ‘urlManager‘ ); 63 } 64 public function getController() { 65 return null; 66 } 67 public function getBaseUrl($absolute = false) { 68 return $this->getRequest ()->getBaseUrl ( $absolute ); 69 } 70 } run()又用了CWebApplication里面的processRequest()。 薛强大哥 (yii作者),架构要不要这样啊.裁剪后当然觉得这样的调用很没意思。 后面的主要在CWebApplication里了。 1 <?php 2 class CWebApplication extends CApplication { 3 public $controllerNamespace; 4 private $_controllerPath; 5 private $_viewPath; 6 private $_systemViewPath; 7 private $_controller; 8 public $controllerMap=array(); 9 public function processRequest() {//开始执行请求 10 //获取urlManager组件,解析请求,得到controller/action这种格式的string, 11 //并且将隐藏参数与请求的参数一一对应,匹配起来,写入$_REQUEST中 12 $route = $this->getUrlManager ()->parseUrl ($this->getRequest()); 13 $this->runController ( $route ); 14 } 15 public function getRequest() {//获取request组件 16 return $this->getComponent ( ‘request‘ ); 17 } 18 protected function registerCoreComponents() {//注册核心组件 19 parent::registerCoreComponents (); 20 } 21 //执行contronller 22 public function runController($route) { 23 if (($ca = $this->createController ( $route )) !== null) { 24 list ( $controller, $actionID ) = $ca; 25 $oldController = $this->_controller; 26 $this->_controller = $controller; 27 $controller->init ();//钩子,在执行action方法前调用,子类去实现 28 $controller->run ( $actionID );//开始转入controller类中action方法的执行 29 $this->_controller = $oldController; 30 } 31 } 32 //创建controller类实例,从controller/action这种格式的string中解析出$controller, $actionID 33 public function createController($route, $owner = null) { 34 if ($owner === null) 35 $owner = $this; 36 if (($route = trim ( $route, ‘/‘ )) === ‘‘) 37 $route = $owner->defaultController; 38 39 $route .= ‘/‘; 40 while ( ($pos = strpos ( $route, ‘/‘ )) !== false ) { 41 $id = substr ( $route, 0, $pos ); 42 if (! preg_match ( ‘/^\w+$/‘, $id )) 43 return null; 44 $id = strtolower ( $id ); 45 $route = ( string ) substr ( $route, $pos + 1 ); 46 if (! isset ( $basePath )) // first segment 47 { 48 $basePath = $owner->getControllerPath (); 49 $controllerID = ‘‘; 50 } else { 51 $controllerID .= ‘/‘; 52 } 53 $className = ucfirst ( $id ) . ‘Controller‘; 54 $classFile = $basePath . DIRECTORY_SEPARATOR . $className . ‘.php‘; 55 56 if (is_file ( $classFile )) { 57 if (! class_exists ( $className, false )) 58 require ($classFile); 59 if (class_exists ( $className, false ) && is_subclass_of ( $className, ‘CController‘ )) { 60 $id [0] = strtolower ( $id [0] ); 61 return array ( 62 new $className ( $controllerID . $id, $owner === $this ? null : $owner ), 63 $this->parseActionParams ( $route ) 64 ); 65 } 66 return null; 67 } 68 $controllerID .= $id; 69 $basePath .= DIRECTORY_SEPARATOR . $id; 70 } 71 } 72 protected function parseActionParams($pathInfo) { 73 if (($pos = strpos ( $pathInfo, ‘/‘ )) !== false) { 74 $manager = $this->getUrlManager ();//再次获取urlManager,在上面第一次调用中已经导入。 75 $manager->parsePathInfo ( ( string ) substr ( $pathInfo, $pos + 1 ) ); 76 $actionID = substr ( $pathInfo, 0, $pos ); 77 return $manager->caseSensitive ? $actionID : strtolower ( $actionID ); 78 } else 79 return $pathInfo; 80 } 81 public function getControllerPath() { 82 if ($this->_controllerPath !== null) 83 return $this->_controllerPath; 84 else 85 return $this->_controllerPath = $this->getBasePath () . DIRECTORY_SEPARATOR . ‘controllers‘; 86 } 87 //两个钩子,子类去实现 88 public function beforeControllerAction($controller, $action) { 89 return true; 90 } 91 public function afterControllerAction($controller, $action) { 92 } 93 protected function init() { 94 parent::init (); 95 } 96 } 对于$this->getUrlManager (),YiiBase里面有’CUrlManager’ => ‘CUrlManager.php’这个映射,说明是实例化了CUrlManager这个类。 1 <?php 2 class CUrlManager { 3 const GET_FORMAT = ‘get‘; 4 public $rules = array (); 5 public $urlSuffix = ‘‘; 6 public $caseSensitive = true; 7 public $urlRuleClass = ‘CUrlRule‘; 8 private $_urlFormat = self::GET_FORMAT; 9 private $_rules = array (); 10 private $_baseUrl; 11 protected function processRules() { 12 //遍历自定义的请求匹配规则 13 foreach ( $this->rules as $pattern => $route ) { 14 //对每一个规则创建CUrlRule实例 15 $this->_rules [] = $this->createUrlRule ( $route, $pattern ); 16 } 17 } 18 protected function createUrlRule($route, $pattern) { 19 if (is_array ( $route ) && isset ( $route [‘class‘] )) 20 return $route; 21 else { 22 //import第二个参数表示是否立即包含类文件。 如果为flase,则类文件仅在被使用时包含。 这个参数仅当使用一个类的路径 别名 时才会用到 23 $urlRuleClass = Yii::import ( $this->urlRuleClass, true ); 24 //创建CUrlRule实例 25 return new $urlRuleClass ( $route, $pattern ); 26 } 27 } 28 //类似于__construct() 29 public function init() { 30 $this->processRules (); 31 } 32 public function parseUrl($request) { 33 //获取请求 34 $rawPathInfo = $request->getPathInfo (); 35 $pathInfo = $this->removeUrlSuffix ( $rawPathInfo, $this->urlSuffix ); 36 foreach ( $this->_rules as $i => $rule ) { 37 if (($r = $rule->parseUrl ( $this, $pathInfo, $rawPathInfo )) !== false) { 38 return $r; 39 } 40 } 41 return $pathInfo; 42 } 43 //解析请求,将请求参数写入$_REQUEST 44 public function parsePathInfo($pathInfo) { 45 if ($pathInfo === ‘‘) 46 return; 47 $segs = explode ( ‘/‘, $pathInfo . ‘/‘ ); 48 $n = count ( $segs ); 49 for($i = 0; $i < $n - 1; $i += 2) { 50 $key = $segs [$i]; 51 if ($key === ‘‘) 52 continue; 53 $value = $segs [$i + 1]; 54 if (($pos = strpos ( $key, ‘[‘ )) !== false && ($m = preg_match_all ( ‘/\[(.*?)\]/‘, $key, $matches )) > 0) { 55 $name = substr ( $key, 0, $pos ); 56 for($j = $m - 1; $j >= 0; -- $j) { 57 if ($matches [1] [$j] === ‘‘) 58 $value = array ( 59 $value 60 ); 61 else 62 $value = array ( 63 $matches [1] [$j] => $value 64 ); 65 } 66 if (isset ( $_GET [$name] ) && is_array ( $_GET [$name] )) 67 $value = CMap::mergeArray ( $_GET [$name], $value ); 68 $_REQUEST [$name] = $_GET [$name] = $value; 69 } else { 70 $_REQUEST [$key] = $_GET [$key] = $value; 71 } 72 } 73 } 74 //去除请求后缀,如video/broadcast.html=>video/broadcast 75 public function removeUrlSuffix($pathInfo, $urlSuffix) { 76 if ($urlSuffix !== ‘‘ && substr ( $pathInfo, - strlen ( $urlSuffix ) ) === $urlSuffix) 77 return substr ( $pathInfo, 0, - strlen ( $urlSuffix ) ); 78 else 79 return $pathInfo; 80 } 81 } yii在创建组件的时候,在调用了CModule的getComponent($id, $createIfNull = true)里面调用了$component->init ();。 所以这里进入init(),然后processRules()遍历自定义的请求匹配规则。如 1 ‘urlManager‘ => array ( 2 ‘urlFormat‘ => ‘path‘, 3 ‘rules‘ => array ( 4 ‘comment_reply/<a:\d+>/<ci:\d+>‘ => ‘reply/load_comment_reply‘, 5 ‘b/<id:\d+>‘ => array ( 6 ‘video/broadcast‘, 7 ‘urlSuffix‘ => ‘.html‘ 8 ), 9 ‘c/<list_start:\d+>‘ => ‘video/list_more_video‘, 10 ‘u/reg‘ => ‘user/reg‘, 11 ‘v/upload‘ => ‘video/upload_video‘, 12 ‘login‘ => ‘user/to_login‘, 13 ‘show_chanel/<chanel_id:\d+>‘ => ‘show/chanel‘ , 14 ‘show/<show_id:\d+>‘ => ‘show/show‘, 15 ) 16 ) $this->_rules [] = $this->createUrlRule ( $route, $pattern );对每一个规则创建CUrlRule实例,并保存。 1 <?php 2 class CUrlRule { 3 public $urlSuffix; 4 public $defaultParams = array (); 5 public $route; 6 public $routePattern; 7 public $pattern; 8 public $template; 9 public $params = array (); 10 //根据自定义规则构建匹配参数的正则表达式。 11 public function __construct($route, $pattern) { 12 if (is_array ( $route )) { 13 foreach ( array ( 14 ‘urlSuffix‘, 15 ‘caseSensitive‘, 16 ‘defaultParams‘, 17 ) as $name ) { 18 if (isset ( $route [$name] )) 19 $this->$name = $route [$name]; 20 } 21 if (isset ( $route [‘pattern‘] )) 22 $pattern = $route [‘pattern‘]; 23 $route = $route [0]; 24 } 25 $this->route = trim ( $route, ‘/‘ ); 26 27 $tr2 [‘/‘] = $tr [‘/‘] = ‘\\/‘; 28 $tr [‘.‘] = ‘\\.‘; 29 30 $this->hasHostInfo = ! strncasecmp ( $pattern, ‘http://‘, 7 ) || ! strncasecmp ( $pattern, ‘https://‘, 8 ); 31 32 if (preg_match_all ( ‘/<(\w+):?(.*?)?>/‘, $pattern, $matches )) { 33 $tokens = array_combine ( $matches [1], $matches [2] ); 34 foreach ( $tokens as $name => $value ) { 35 if ($value === ‘‘) 36 $value = ‘[^\/]+‘; 37 $tr ["<$name>"] = "(?P<$name>$value)"; 38 //取出自定义规则中隐藏的参数,保存 39 if (isset ( $this->references [$name] )) 40 $tr2 ["<$name>"] = $tr ["<$name>"]; 41 else 42 $this->params [$name] = $value; 43 } 44 } 45 $p = rtrim ( $pattern, ‘*‘ ); 46 $this->append = $p !== $pattern; 47 $p = trim ( $p, ‘/‘ ); 48 $this->template = preg_replace ( ‘/<(\w+):?.*?>/‘, ‘<$1>‘, $p ); 49 $this->pattern = ‘/^‘ . strtr ( $this->template, $tr ) . ‘\/‘; 50 //合成匹配的正则表达式 51 if ($this->append) 52 $this->pattern .= ‘/u‘; 53 else 54 $this->pattern .= ‘$/u‘; 55 } 56 //根据正则表达式和请求,将隐藏参数与请求参数一一匹配,保存$_REQUEST 57 public function parseUrl($manager, $pathInfo, $rawPathInfo) { 58 if ($this->urlSuffix !== null) { 59 $pathInfo = $manager->removeUrlSuffix ( $rawPathInfo, $this->urlSuffix ); 60 } 61 $pathInfo .= ‘/‘; 62 if (preg_match ( $this->pattern, $pathInfo, $matches )) { 63 foreach ( $this->defaultParams as $name => $value ) { 64 if (! isset ( $_GET [$name] )) 65 $_REQUEST [$name] = $_GET [$name] = $value; 66 } 67 $tr = array (); 68 foreach ( $matches as $key => $value ) { 69 if (isset ( $this->references [$key] )) 70 $tr [$this->references [$key]] = $value; 71 elseif (isset ( $this->params [$key] )) 72 $_REQUEST [$key] = $_GET [$key] = $value; 73 } 74 if ($pathInfo !== $matches [0]) //如果还有另外的请求参数 75 $manager->parsePathInfo ( ltrim ( substr ( $pathInfo, strlen ( $matches [0] ) ), ‘/‘ ) ); 76 return $this-> Posted in: 开发语言
时间: 2024-10-31 22:17:35