php+yii框架,yii框架源码分析(一)
yii框架源码分析(一)
本文将对yii中的mvc,路由器,filter,组件机制等最主要的部分进行自己的一点浅析,力求说明自己做一个php mvc不是那么的遥不可及,其实是很简单的。
源码基于yii 1.13,为了方便说明,我对其进行了大量的裁剪,不过还是让他保有上面的那些最重要的功能。裁剪下来,其实没有几个文件了,而且每个文件代码最多100多行,避免因为代码太多而懒得看。
所谓的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中文网,转载请注明出处,感谢您的尊重!
php+yii框架,yii框架源码分析(一)相关推荐
- 图片加载框架Picasso - 源码分析
简书:图片加载框架Picasso - 源码分析 前一篇文章讲了Picasso的详细用法,Picasso 是一个强大的图片加载缓存框架,一个非常优秀的开源库,学习一个优秀的开源库,,我们不仅仅是学习它的 ...
- 缓存框架OSCache部分源码分析
在并发量比较大的场景,如果采用直接访问数据库的方式,将会对数据库带来巨大的压力,严重的情况下可能会导致数据库不可用状态,并且时间的消耗也是不能容忍的,尤其对于某些获取起来比较昂贵的数据.在这种情况下, ...
- (4.2.40)阿里开源路由框架ARouter的源码分析
一需求背景 1 Android原生方案的不足 2 自定义路由框架的适用场景 3 对自定义路由框架的设想 二ARouter的概述 三ARouter的引入和使用 四源码分析 1 arouter-annot ...
- 01、JUL日志(JDK自带日志框架,包含源码分析)
文章目录 前言 一.JUL架构介绍 1.1.认识不同组件 1.2.Logger 1.3.Handler 二.输出日志信息 三.自定义日志级别配置 3.1.认识Level类 3.2.输出不同等级日志 3 ...
- 入理解分布式调度框架TBSchedule及源码分析
简介 由于最近工作比较忙,前前后后花了两个月的时间把TBSchedule的源码翻了个底朝天.关于TBSchedule的使用,网上也有很多参考资料,这里不做过多的阐述.本文着重介绍TBSchedule的 ...
- python Django之 DRF(一)框架介绍、源码分析
文章目录 一.django rest framework 框架的介绍 1.什么是RESTful规范? 2.RESTful API的介绍 二.drf框架源码解读 1.drf框架的使用 2.APIView ...
- 02、Log4j(第三方日志框架,带源码分析)
文章目录 前言 一.认识Log4j 1.1.介绍Log4j 1.2.第三方jar包 1.3.日志等级(6个) 二.Log4j的三大组件 Loggers Appenders Layouts 三.入门Lo ...
- Golang日志框架lumberjack包源码分析
github地址: https://github.com/natefinch/lumberjack 获取源码 go get gopkg.in/natefinch/lumberjack.v2 介绍 l ...
- server如何调用 thrift_一文带你了解 Thrift,一个可伸缩的跨语言 RPC 框架(pinpoint 源码分析系列)...
Thrift 是什么研究分布式调用链工具pinpoint的时候,在源码里看到了Thrift相关的代码,所以来了兴趣,想研究研究这个框架.Thrift 目前是 Apache 的一个项目,但是它是由fac ...
- k8s源码分析 pdf_rook源码分析之一:rook架构解析
rook简介 Rook是一款云原生环境下的开源分布式存储编排系统,目前支持 Ceph.NFS.Edegefs.Cassandra.CockroachDB等存储系统.它实现了一个自动管理的.自动扩容的. ...
最新文章
- [UE4]死亡后调整视角
- plt.scatter参数详解 s=25代表点的面积
- ubuntu 运行android sdk 下的工具adb报bash: ./adb: No such file or directory
- android computescroll_Android问题:自定义ViewGroup,重载computeScroll()方法有什么用?...
- vue --- 动画执行的周期(动画的钩子函数)
- git 拉取代码失败
- 组织需要什么样的我_为什么开放组织对我说话
- 枚举工具类 EnumUtils.java
- python从入门到实践答案博客园_《Python从入门到实践》--第八章 函数 课后练习4...
- Arcade扩展音色库:Output Arcade Brain Waves Library Content
- 用vim写python代码的两个关键设置
- 分享一个游戏源码、游戏服务端、下载网站
- Spring boot 开发 GA/T1400 协议之注册、保活、注销、校时功能
- WordPress 前端投稿/编辑发表文章插件 DJD Site Post(支持游客和已注册用户)汉化版 免费下载...
- php ic卡,IC卡的数据结构认识
- FPGA——输入原理图实现按键控制发光二极管的亮灭
- Linux系统中read的用法,Linux系统中read的使用技巧
- 1521端口已被占用解决方案
- 二、马尔可夫决策过程与贝尔曼方程
- IC电源去耦原理及滤波元件选型