• 技术文章 >后端开发 >php教程

    yii框架源码分析(二)

    2016-08-08 09:33:27原创716
    转载请注明:TheViper http://www.cnblogs.com/TheViper/

    上一篇主要分析了Yii::createWebApplication ( $config )->run ();的createWebApplication ( $config )部分,这篇分析后面的。

    run()也是不在CWebApplication里面,在CApplication 里。

     1 php  2abstractclass CApplication extends CModule {  3private$_id;  4private$_basePath;  5abstractpublicfunction processRequest();  6publicfunction __construct($config = null) {  7if (is_string ( $config ))  8$config = require ($config);  9 Yii::setApplication ( $this );//保存整个app实例10if (isset ( $config ['basePath'] )) { 11$this->setBasePath ( $config ['basePath'] ); 12unset ( $config ['basePath'] ); 13 } else14$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 } 27protectedfunction registerCoreComponents() { 28$components = array ( 29 'request' => array ( 30 'class' => 'CHttpRequest' 31 ), 32 'urlManager' => array ( 33 'class' => 'CUrlManager' 34 ) 35 ); 3637$this->setComponents ( $components );//父类实现38 } 39publicfunction run() { 40$this->processRequest (); 41 } 42publicfunction getId() { 43if ($this->_id !== null) 44return$this->_id; 45else46return$this->_id = sprintf ( '%x', crc32 ( $this->getBasePath () . $this->name ) ); 47 } 48publicfunction setId($id) { 49$this->_id = $id; 50 } 51publicfunction getBasePath() { 52return$this->_basePath; 53 } 54publicfunction setBasePath($path) { 55if (($this->_basePath = realpath ( $path )) === false || ! is_dir ( $this->_basePath )) 56return; 57 } 58publicfunction getDb() { 59return$this->getComponent ( 'db' );//父类实现60 } 61publicfunction getUrlManager() { 62return$this->getComponent ( 'urlManager' ); 63 } 64publicfunction getController() { 65returnnull; 66 } 67publicfunction getBaseUrl($absolute = false) { 68return$this->getRequest ()->getBaseUrl ( $absolute ); 69 } 70 }

    run()又用了CWebApplication里面的processRequest()。薛强大哥(yii作者),架构要不要这样啊.裁剪后当然觉得这样的调用很没意思。

    后面的主要在CWebApplication里了。

     1 php  2class CWebApplication extends CApplication {  3public$controllerNamespace;  4private$_controllerPath;  5private$_viewPath;  6private$_systemViewPath;  7private$_controller;  8public$controllerMap=array();  9publicfunction processRequest() {//开始执行请求 10 //获取urlManager组件,解析请求,得到controller/action这种格式的string, 11 //并且将隐藏参数与请求的参数一一对应,匹配起来,写入$_REQUEST中12$route = $this->getUrlManager ()->parseUrl ($this->getRequest()); 13$this->runController ( $route ); 14 } 15publicfunction getRequest() {//获取request组件16return$this->getComponent ( 'request' ); 17 } 18protectedfunction registerCoreComponents() {//注册核心组件19 parent::registerCoreComponents (); 20 } 21//执行contronller22publicfunction runController($route) { 23if (($ca = $this->createController ( $route )) !== null) { 24list ( $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 33publicfunction createController($route, $owner = null) { 34if ($owner === null) 35$owner = $this; 36if (($route = trim ( $route, '//m.sbmmt.com/m/' )) === '') 37$route = $owner->defaultController; 3839$route .= '//m.sbmmt.com/m/'; 40while ( ($pos = strpos ( $route, '//m.sbmmt.com/m/' )) !== false ) { 41$id = substr ( $route, 0, $pos ); 42if (! preg_match ( '/^\w+$/', $id )) 43returnnull; 44$id = strtolower ( $id ); 45$route = ( string ) substr ( $route, $pos + 1 ); 46if (! isset ( $basePath )) // first segment47 { 48$basePath = $owner->getControllerPath (); 49$controllerID = ''; 50 } else { 51$controllerID .= '//m.sbmmt.com/m/'; 52 } 53$className = ucfirst ( $id ) . 'Controller'; 54$classFile = $basePath . DIRECTORY_SEPARATOR . $className . '.php'; 5556if (is_file ( $classFile )) { 57if (! class_exists ( $className, false )) 58require ($classFile); 59if (class_exists ( $className, false ) && is_subclass_of ( $className, 'CController' )) { 60$id [0] = strtolower ( $id [0] ); 61returnarray ( 62new$className ( $controllerID . $id, $owner === $this ? null : $owner ), 63$this->parseActionParams ( $route ) 64 ); 65 } 66returnnull; 67 } 68$controllerID .= $id; 69$basePath .= DIRECTORY_SEPARATOR . $id; 70 } 71 } 72protectedfunction parseActionParams($pathInfo) { 73if (($pos = strpos ( $pathInfo, '//m.sbmmt.com/m/' )) !== false) { 74$manager = $this->getUrlManager ();//再次获取urlManager,在上面第一次调用中已经导入。75$manager->parsePathInfo ( ( string ) substr ( $pathInfo, $pos + 1 ) ); 76$actionID = substr ( $pathInfo, 0, $pos ); 77return$manager->caseSensitive ? $actionID : strtolower ( $actionID ); 78 } else79return$pathInfo; 80 } 81publicfunction getControllerPath() { 82if ($this->_controllerPath !== null) 83return$this->_controllerPath; 84else85return$this->_controllerPath = $this->getBasePath () . DIRECTORY_SEPARATOR . 'controllers'; 86 } 87//两个钩子,子类去实现88publicfunction beforeControllerAction($controller, $action) { 89returntrue; 90 } 91publicfunction afterControllerAction($controller, $action) { 92 } 93protectedfunction init() { 94 parent::init (); 95 } 96 }

    对于$this->getUrlManager (),YiiBase里面有'CUrlManager' => 'CUrlManager.php'这个映射,说明是实例化了CUrlManager这个类。

     1 php  2class CUrlManager {  3const GET_FORMAT = 'get';  4public$rules = array ();  5public$urlSuffix = '';  6public$caseSensitive = true;  7public$urlRuleClass = 'CUrlRule';  8private$_urlFormat = self::GET_FORMAT;  9private$_rules = array (); 10private$_baseUrl; 11protectedfunction processRules() { 12//遍历自定义的请求匹配规则13foreach ( $this->rules as$pattern => $route ) { 14//对每一个规则创建CUrlRule实例15$this->_rules [] = $this->createUrlRule ( $route, $pattern ); 16 } 17 } 18protectedfunction createUrlRule($route, $pattern) { 19if (is_array ( $route ) && isset ( $route ['class'] )) 20return$route; 21else { 22//import第二个参数表示是否立即包含类文件。 如果为flase,则类文件仅在被使用时包含。 这个参数仅当使用一个类的路径 别名 时才会用到23$urlRuleClass = Yii::import ( $this->urlRuleClass, true ); 24//创建CUrlRule实例25returnnew$urlRuleClass ( $route, $pattern ); 26 } 27 } 28//类似于__construct()29publicfunction init() { 30$this->processRules (); 31 } 32publicfunction parseUrl($request) { 33//获取请求34$rawPathInfo = $request->getPathInfo (); 35$pathInfo = $this->removeUrlSuffix ( $rawPathInfo, $this->urlSuffix ); 36foreach ( $this->_rules as$i => $rule ) { 37if (($r = $rule->parseUrl ( $this, $pathInfo, $rawPathInfo )) !== false) { 38return$r; 39 } 40 } 41return$pathInfo; 42 } 43//解析请求,将请求参数写入$_REQUEST44publicfunction parsePathInfo($pathInfo) { 45if ($pathInfo === '') 46return; 47$segs = explode ( '//m.sbmmt.com/m/', $pathInfo . '//m.sbmmt.com/m/' ); 48$n = count ( $segs ); 49for($i = 0; $i < $n - 1; $i += 2) { 50$key = $segs [$i]; 51if ($key === '') 52continue; 53$value = $segs [$i + 1]; 54if (($pos = strpos ( $key, '[' )) !== false && ($m = preg_match_all ( '/\[(.*?)\]/', $key, $matches )) > 0) { 55$name = substr ( $key, 0, $pos ); 56for($j = $m - 1; $j >= 0; -- $j) { 57if ($matches [1] [$j] === '') 58$value = array ( 59$value60 ); 61else62$value = array ( 63$matches [1] [$j] => $value64 ); 65 } 66if (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 75publicfunction removeUrlSuffix($pathInfo, $urlSuffix) { 76if ($urlSuffix !== '' && substr ( $pathInfo, - strlen ( $urlSuffix ) ) === $urlSuffix) 77returnsubstr ( $pathInfo, 0, - strlen ( $urlSuffix ) ); 78else79return$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//' => 'reply/load_comment_reply',  5 'b/' => array (  6 'video/broadcast',  7 'urlSuffix' => '.html'  8 ),  9 'c/' => 'video/list_more_video', 10 'u/reg' => 'user/reg', 11 'v/upload' => 'video/upload_video', 12 'login' => 'user/to_login', 13 'show_chanel/' => 'show/chanel' , 14 'show/' => 'show/show', 15 ) 16 ) 

    $this->_rules [] = $this->createUrlRule ( $route, $pattern );对每一个规则创建CUrlRule实例,并保存。

     1 php  2class CUrlRule {  3public$urlSuffix;  4public$defaultParams = array ();  5public$route;  6public$routePattern;  7public$pattern;  8public$template;  9public$params = array (); 10//根据自定义规则构建匹配参数的正则表达式。11publicfunction __construct($route, $pattern) { 12if (is_array ( $route )) { 13foreach ( array ( 14 'urlSuffix', 15 'caseSensitive', 16 'defaultParams', 17 ) as$name ) { 18if (isset ( $route [$name] )) 19$this->$name = $route [$name]; 20 } 21if (isset ( $route ['pattern'] )) 22$pattern = $route ['pattern']; 23$route = $route [0]; 24 } 25$this->route = trim ( $route, '//m.sbmmt.com/m/' ); 2627$tr2 ['//m.sbmmt.com/m/'] = $tr ['//m.sbmmt.com/m/'] = '\\/'; 28$tr ['.'] = '\\.'; 2930$this->hasHostInfo = ! strncasecmp ( $pattern, 'http://', 7 ) || ! strncasecmp ( $pattern, 'https://', 8 ); 3132if (preg_match_all ( '/<(\w+):?(.*?)?>/', $pattern, $matches )) { 33$tokens = array_combine ( $matches [1], $matches [2] ); 34foreach ( $tokensas$name => $value ) { 35if ($value === '') 36$value = '[^\/]+'; 37$tr ["<$name>"] = "(?P<$name>$value)"; 38//取出自定义规则中隐藏的参数,保存39if (isset ( $this->references [$name] )) 40$tr2 ["<$name>"] = $tr ["<$name>"]; 41else42$this->params [$name] = $value; 43 } 44 } 45$p = rtrim ( $pattern, '*' ); 46$this->append = $p !== $pattern; 47$p = trim ( $p, '//m.sbmmt.com/m/' ); 48$this->template = preg_replace ( '/<(\w+):?.*?>/', '<$1>', $p ); 49$this->pattern = '/^' . strtr ( $this->template, $tr ) . '\/'; 50//合成匹配的正则表达式51if ($this->append) 52$this->pattern .= '/u'; 53else54$this->pattern .= '$/u'; 55 } 56//根据正则表达式和请求,将隐藏参数与请求参数一一匹配,保存$_REQUEST57publicfunction parseUrl($manager, $pathInfo, $rawPathInfo) { 58if ($this->urlSuffix !== null) { 59$pathInfo = $manager->removeUrlSuffix ( $rawPathInfo, $this->urlSuffix ); 60 } 61$pathInfo .= '//m.sbmmt.com/m/'; 62if (preg_match ( $this->pattern, $pathInfo, $matches )) { 63foreach ( $this->defaultParams as$name => $value ) { 64if (! isset ( $_GET [$name] )) 65$_REQUEST [$name] = $_GET [$name] = $value; 66 } 67$tr = array (); 68foreach ( $matchesas$key => $value ) { 69if (isset ( $this->references [$key] )) 70$tr [$this->references [$key]] = $value; 71elseif (isset ( $this->params [$key] )) 72$_REQUEST [$key] = $_GET [$key] = $value; 73 } 74if ($pathInfo !== $matches [0]) //如果还有另外的请求参数75$manager->parsePathInfo ( ltrim ( substr ( $pathInfo, strlen ( $matches [0] ) ), '//m.sbmmt.com/m/' ) ); 76return$this->route; 77 } else78returnfalse; 79 } 80 }

    CUrlRule中的parseUrl($manager, $pathInfo, $rawPathInfo)这里暂时还没用到。

    然后回到CWebApplication中的¥route=$this->getUrlManager ()->parseUrl ($this->getRequest());。后面parseUrl开始解析请求了。

    转到CUrlManager的parseUrl($request),遍历创建CUrlRule过程中保存的匹配规则,再在CUrlRule里面用CUrlRule的parseUrl($manager, $pathInfo, $rawPathInfo)将隐藏参数与请求参数一一匹配,保存$_REQUEST.

    比如:

    'b/' => array (
      'video/broadcast',
      'urlSuffix' => '.html'
    )

    我的请求是b/1.html,就会被解析成video/broadcast?id=1.而最终的$route就是这个。

    最后附上,裁剪的yii http://files.cnblogs.com/TheViper/framework.zip

    下一篇

    以上就介绍了yii框架源码分析(二),包括了yii框架源码方面的内容,希望对PHP教程有兴趣的朋友有所帮助。

    php入门到就业线上直播课:查看学习

    声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn核实处理。

    前端(VUE)零基础到就业课程:点击学习

    清晰的学习路线+老师随时辅导答疑

    自己动手写 PHP MVC 框架:点击学习

    快速了解MVC架构、了解框架底层运行原理

    上一篇:开发动态网站 选择PHP、ASP还是ASP.NET 下一篇:自己动手写 PHP MVC 框架(40节精讲/巨细/新人进阶必看)

    相关文章推荐

    • ❤️‍🔥共22门课程,总价3725元,会员免费学• ❤️‍🔥接口自动化测试不想写代码?• 继续收藏一些PHP常用函数第1/2页_PHP教程• ThinkPHP控制器里javascript代码不能执行的解决方法_PHP• 基于PHP的简单采集数据入库程序_PHP• PHP中读取文件的8种方法和代码实例_PHP• ThinkPHP Mobile使用方法简明教程_PHP
    1/1

    PHP中文网