php+yii框架,yii框架源码分析(一)

我就是我 2023-01-17 10:56 349阅读 0赞

yii框架源码分析(一)

本文将对yii中的mvc,路由器,filter,组件机制等最主要的部分进行自己的一点浅析,力求说明自己做一个php mvc不是那么的遥不可及,其实是很简单的。

源码基于yii 1.13,为了方便说明,我对其进行了大量的裁剪,不过还是让他保有上面的那些最重要的功能。裁剪下来,其实没有几个文件了,而且每个文件代码最多100多行,避免因为代码太多而懒得看。

20141116172749\_5781.png

所谓的mvc都是让所有请求从一个地方进去,通过对请求,配置的解析,分发到对应的类方法中。

首先当然是入口文件,index.php.

1 php2$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 php2define ( “VIEWS_DIR”, “$app/protected/views/“);3define ( “CONTROLLERS_DIR”, “$app/protected/controllers/“);4require(dirname(__FILE__).’/YiiBase.php’);5class Yii extendsYiiBase6{7 }

原来yii是个空的类啊,去看YiiBase.

1 php2defined ( ‘YII_PATH’ ) or define ( ‘YII_PATH’, dirname ( __FILE__) );3classYiiBase {4publicstatic$classMap = array();5publicstatic$enableIncludePath = true;6privatestatic$_aliases = array(7 ‘system’ =>YII_PATH8 ); //alias => path9privatestatic$_imports = array (); //alias => class name or directory10privatestatic$_includePaths; //list of include paths11privatestatic$_app;12privatestatic$_logger;13publicstaticfunction createWebApplication($config = null) {14return self::createApplication ( ‘CWebApplication’, $config);15}16publicstaticfunction createApplication($class, $config = null) {17returnnew$class ( $config);18}19publicstaticfunctionapp() {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] );36else37 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 imported45return self::$_imports [$alias];4647if (class_exists ( $alias, false ) || interface_exists ( $alias, false))48return self::$_imports [$alias] = $alias;49if (($pos = strrpos ( $alias, ‘.’ )) === false) //a simple class name50{51//try to autoload the class with an autoloader if $forceInclude is true52if ($forceInclude && (Yii::autoload ( $alias, true ) || class_exists ( $alias, true)))53 self::$_imports [$alias] = $alias;54return$alias;55}5657$className = ( string ) substr ( $alias, $pos + 1);58$isClass = $className !== ‘*‘;5960if ($isClass && (class_exists ( $className, false ) || interface_exists ( $className, false)))61return self::$_imports [$alias] = $className;6263if (($path = self::getPathOfAlias ( $alias )) !== false) {64if ($isClass) {65if ($forceInclude) {66if (is_file ( $path . ‘.php’))67require ($path . ‘.php’);68else69thrownew 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}‘ => $alias71) ) );72 self::$_imports [$alias] = $className;73 } else74 self::$classMap [$className] = $path . ‘.php’;75return$className;76 } else//a directory77{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}8384array_unshift ( self::$_includePaths, $path);8586if (self::$enableIncludePath && set_include_path ( ‘.’ . PATH_SEPARATOR . implode ( PATH_SEPARATOR, self::$_includePaths ) ) === false)87 self::$enableIncludePath = false;8889return 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 php2returnarray(3 ‘basePath’ => dirname ( __FILE__ ) . DIRECTORY_SEPARATOR . ‘..’, 45 ‘import’ => array(6 ‘application.util.*‘ 7 ), 89 ‘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 php2class CWebApplication extendsCApplication {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, $actionID33publicfunction 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}81publicfunctiongetControllerPath() {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}93protectedfunctioninit() {94 parent::init ();95}96 }

没有构造方法,构造方法在父类CApplication里面。

1 php2abstractclass CApplication extendsCModule {3private$_id;4private$_basePath;5abstractpublicfunctionprocessRequest();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}27protectedfunctionregisterCoreComponents() {28$components = array(29 ‘request’ => array(30 ‘class’ => ‘CHttpRequest’ 31 ), 32 ‘urlManager’ => array(33 ‘class’ => ‘CUrlManager’ 34)35);3637$this->setComponents ( $components );//父类实现38}39publicfunctionrun() {40$this->processRequest ();41}42publicfunctiongetId() {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}51publicfunctiongetBasePath() {52return$this->_basePath;53}54publicfunction setBasePath($path) {55if (($this->_basePath = realpath ( $path )) === false || ! is_dir ( $this->_basePath ))56return;57}58publicfunctiongetDb() {59return$this->getComponent ( ‘db’ );//父类实现60}61publicfunctiongetUrlManager() {62return$this->getComponent ( ‘urlManager’);63}64publicfunctiongetController() {65returnnull;66}67publicfunction getBaseUrl($absolute = false) {68return$this->getRequest ()->getBaseUrl ( $absolute);69}70 }

__construct里面注释写的很详细了,值得注意的是registerCoreComponents ()。前面说了那么多,那么Yii::createWebApplication ( $config )到底是做什么的。

其实最终目的对这个裁剪过的yii而言就是注册核心组件。就这么简单.

setComponents ( $components )在父类CModule里面.

1 php2abstractclass CModule extendsCComponent {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}79protectedfunctionpreloadComponents() {80foreach ( $this->preload as$id)81$this->getComponent ( $id);82}83//又是两个钩子84protectedfunctionpreinit() {85}86protectedfunctioninit() {87}88 }

看到所谓的注册组件就是写入数组保存,getComponent()的时候就是用前面讲到的YiiBase里面的createComponent($config)返回组件实例。就这么简单。

而CApplication里面的什么getDb(),getUrlManager()也是在调用getComponent()。

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

下一篇

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

本文原创发布php中文网,转载请注明出处,感谢您的尊重!

发表评论

表情:
评论列表 (有 0 条评论,349人围观)

还没有评论,来说两句吧...

相关阅读