zoukankan      html  css  js  c++  java
  • yii源码分析I、II

    转载请注明: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: 开发语言  
  • 相关阅读:
    发布一个扩展Repeater的模板控件,带自动分页功能
    webservice 测试窗体只能用于来自本地计算机的请求
    FCKeditor编辑器中设置默认文本行高和字体大小
    程序员的个人性格
    程序设计模式的有趣解释-追MM
    集锦一
    UML简介(原创)
    一位IT从业人员的心路历程
    一个初级测试工程师的工作总结
    "与熊共舞"(转载)
  • 原文地址:https://www.cnblogs.com/sunscheung/p/4864805.html
Copyright © 2011-2022 走看看