转载请注明: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: 开发语言