您好,欢迎访问一九零五行业门户网

yii框架源码分析(一)

yii框架源码分析(一)
本文将对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';//载入配置5require_once ($yii); 6 yii::createwebapplication ( $config )->run ();
引入yii.php
1 php 2define ( views_dir, $app/protected/views/ ); 3define ( controllers_dir, $app/protected/controllers/ ); 4require(dirname(__file__).'/yiibase.php'); 5class yii extends yiibase 6{ 7 }
原来yii是个空的类啊,去看yiibase.
1 php 2defined ( 'yii_path' ) or define ( 'yii_path', dirname ( __file__ ) ); 3class yiibase { 4publicstatic$classmap = array (); 5publicstatic$enableincludepath = true; 6privatestatic$_aliases = array ( 7 'system' => yii_path 8 ); // alias => path 9privatestatic$_imports = array (); // alias => class name or directory 10privatestatic$_includepaths; // list of include paths 11privatestatic$_app; 12privatestatic$_logger; 13publicstaticfunction createwebapplication($config = null) { 14return self::createapplication ( 'cwebapplication', $config ); 15 } 16publicstaticfunction createapplication($class, $config = null) { 17returnnew$class ( $config ); 18 } 19publicstaticfunction app() { 20return self::$_app; 21 } 22//别名路径 23publicstaticfunction getpathofalias($alias) { 24if (isset ( self::$_aliases [$alias] )) 25return self::$_aliases [$alias]; 26elseif (($pos = strpos ( $alias, '.' )) !== false) { 27$rootalias = substr ( $alias, 0, $pos ); 28if (isset ( self::$_aliases [$rootalias] )) 29return self::$_aliases [$alias] = rtrim ( self::$_aliases [$rootalias] . directory_separator . str_replace ( '.', directory_separator, substr ( $alias, $pos + 1 ) ), '*' . directory_separator ); 30 } 31returnfalse; 32 } 33publicstaticfunction setpathofalias($alias, $path) { 34if (empty ( $path )) 35unset ( self::$_aliases [$alias] ); 36else 37 self::$_aliases [$alias] = rtrim ( $path, '\\/' ); 38 } 39publicstaticfunction setapplication($app) { 40if (self::$_app === null || $app === null) 41 self::$_app = $app; 42 } 43publicstaticfunction import($alias, $forceinclude = false) { 44if (isset ( self::$_imports [$alias] )) // previously imported 45return self::$_imports [$alias]; 46 47if (class_exists ( $alias, false ) || interface_exists ( $alias, false )) 48return self::$_imports [$alias] = $alias; 49if (($pos = strrpos ( $alias, '.' )) === false) // a simple class name 50 { 51// try to autoload the class with an autoloader if $forceinclude is true 52if ($forceinclude && (yii::autoload ( $alias, true ) || class_exists ( $alias, true ))) 53 self::$_imports [$alias] = $alias; 54return$alias; 55 } 56 57$classname = ( string ) substr ( $alias, $pos + 1 ); 58$isclass = $classname !== '*'; 59 60if ($isclass && (class_exists ( $classname, false ) || interface_exists ( $classname, false ))) 61return self::$_imports [$alias] = $classname; 62 63if (($path = self::getpathofalias ( $alias )) !== false) { 64if ($isclass) { 65if ($forceinclude) { 66if (is_file ( $path . '.php' )) 67require ($path . '.php'); 68else 69thrownew 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'; 75return$classname; 76 } else// a directory 77 { 78if (self::$_includepaths === null) { 79 self::$_includepaths = array_unique ( explode ( path_separator, get_include_path () ) ); 80if (($pos = array_search ( '.', self::$_includepaths, true )) !== false) 81unset ( self::$_includepaths [$pos] ); 82 } 83 84array_unshift ( self::$_includepaths, $path ); 85 86if (self::$enableincludepath && set_include_path ( '.' . path_separator . implode ( path_separator, self::$_includepaths ) ) === false) 87 self::$enableincludepath = false; 88 89return self::$_imports [$alias] = $path; 90 } 91 } 92 } 93//创建组件实例 94publicstaticfunction createcomponent($config) { 95if (is_string ( $config )) { 96$type = $config; 97$config = array (); 98 } elseif (isset ( $config ['class'] )) { 99$type = $config ['class']; 100unset ( $config ['class'] ); 101 } 102if (! class_exists ( $type, false )) { 103$type = yii::import ( $type, true ); 104 } 105if (($n = func_num_args ()) > 1) { 106$args = func_get_args (); 107if ($n === 2) 108$object = new$type ( $args [1] ); 109elseif ($n === 3) 110$object = new$type ( $args [1], $args [2] ); 111elseif ($n === 4) 112$object = new$type ( $args [1], $args [2], $args [3] ); 113else { 114unset ( $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 } else124$object = new$type (); 125foreach ( $configas$key => $value ) 126$object->$key = $value; 127128return$object; 129 } 130//按需加载相应的php131publicstaticfunction autoload($classname) { 132include self::$_coreclasses [$classname]; 133 } 134privatestatic$_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} 152153 spl_autoload_register ( array ( 154 'yiibase', 155 'autoload' 156 ) );
看似很多,其实就三个地方注意下就可以了
1.spl_autoload_register,用这个就可以实现传说中的按需加载相应的php了,坑爹啊。
2.createcomponent($config)这个方法是yii组件调用的核心。在配置中注册的所有组件都是通过它获取组件类的实例的。比如配置:
1 php 2returnarray ( 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//' => 'reply/load_comment_reply', 22 'b/' => array ( 23 'video/broadcast', 24 'urlsuffix' => '.html' 25 ), 26 'c/' => 'video/list_more_video', 27 'u/reg' => 'user/reg', 28 'v/upload' => 'video/upload_video', 29 'login' => 'user/to_login', 30 'show_chanel/' => 'show/chanel' , 31 'show/' => '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 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, '/' )) === '') 37$route = $owner->defaultcontroller; 3839$route .= '/'; 40while ( ($pos = strpos ( $route, '/' )) !== 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 .= '/'; 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, '/' )) !== 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 }
没有构造方法,构造方法在父类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 }
__construct里面注释写的很详细了,值得注意的是registercorecomponents ()。前面说了那么多,那么yii::createwebapplication ( $config )到底是做什么的。
其实最终目的对这个裁剪过的yii而言就是注册核心组件。就这么简单.
setcomponents ( $components )在父类cmodule里面.
1 php 2abstractclass cmodule extends ccomponent { 3public$preload = array (); 4public$behaviors = array (); 5private$_id; 6private$_parentmodule; 7private$_basepath; 8private$_modulepath; 9private$_params; 10private$_modules = array (); 11private$_moduleconfig = array (); 12private$_components = array (); 13private$_componentconfig = array (); 14//重写是为了方便直接用 application.组件 这种方式直接获取组件15publicfunction __get($name) { 16if ($this->hascomponent ( $name )) 17return$this->getcomponent ( $name ); 18else19return parent::__get ( $name ); 20 } 21publicfunction __isset($name) { 22if ($this->hascomponent ( $name )) 23return$this->getcomponent ( $name ) !== null; 24else25return parent::__isset ( $name ); 26 } 27publicfunction hascomponent($id) { 28returnisset ( $this->_components [$id] ) || isset ( $this->_componentconfig [$id] ); 29 } 30// 31publicfunction getcomponent($id, $createifnull = true) { 32if (isset ( $this->_components [$id] )) 33return$this->_components [$id]; 34elseif (isset ( $this->_componentconfig [$id] ) && $createifnull) { 35$config = $this->_componentconfig [$id]; 36$component = yii::createcomponent ( $config );//yiibase,返回组件实例37$component->init ();//钩子,调用子类重写的init方法 38 //将组件写入数组保存,并返回39return$this->_components [$id] = $component; 40 } 41 } 42publicfunction setcomponent($id, $component, $merge = true) { 43//组件写入数组保存44if (isset ( $this->_componentconfig [$id] ) && $merge) { 4546$this->_componentconfig [$id] = self::mergearray ( $this->_componentconfig [$id], $component ); 47 } else { 4849$this->_componentconfig [$id] = $component; 50 } 51 } 52publicstaticfunction mergearray($a, $b) { 53$args = func_get_args (); 54$res = array_shift ( $args ); 55while ( ! empty ( $args ) ) { 56$next = array_shift ( $args ); 57foreach ( $nextas$k => $v ) { 58if (is_integer ( $k )) 59isset ( $res [$k] ) ? $res [] = $v : $res [$k] = $v; 60elseif (is_array ( $v ) && isset ( $res [$k] ) && is_array ( $res [$k] )) 61$res [$k] = self::mergearray ( $res [$k], $v ); 62else63$res [$k] = $v; 64 } 65 } 66return$res; 67 } 68publicfunction setcomponents($components, $merge = true) { 69foreach ( $componentsas$id => $component ) 70$this->setcomponent ( $id, $component, $merge ); 71 } 72//子类capplication调用,用来为模块指定配置73publicfunction configure($config) { 74if (is_array ( $config )) { 75foreach ( $configas$key => $value ) 76$this->$key = $value; 77 } 78 } 79protectedfunction preloadcomponents() { 80foreach ( $this->preload as$id ) 81$this->getcomponent ( $id ); 82 } 83//又是两个钩子84protectedfunction preinit() { 85 } 86protectedfunction init() { 87 } 88 }
看到所谓的注册组件就是写入数组保存,getcomponent()的时候就是用前面讲到的yiibase里面的createcomponent($config)返回组件实例。就这么简单。
而capplication里面的什么getdb(),geturlmanager()也是在调用getcomponent()。
最后附上,裁剪的yii http://files.cnblogs.com/theviper/framework.zip
下一篇 
以上就介绍了yii框架源码分析(一),包括了yii框架源码方面的内容,希望对php教程有兴趣的朋友有所帮助。
其它类似信息

推荐信息