Home >Backend Development >PHP Tutorial >(转)yii流程,入口文件下的准备工作_PHP教程
一 目录文件
|-framework 框架核心库
|--base 底层类库文件夹,包含CApplication(应用类,负责全局的用户请求处理,它管理的应用组件集,将提供特定功能给整个应用程 序),CComponent(组件类,该文件包含了基于组件和事件驱动编程的基础类,从版本1.1.0开始,一个行为的属性(或者它的公共成员变量或它通 过getter和/或setter方法??定义的属性)可以通过组件的访问来调用),CBehavior(行为类,主要负责声明事件和相应事件处理程序的 方法、将对象的行为附加到组件等等),CModel(模型类,为所有的数据模型提供的基类),CModule(是模块和应用程序的基类,主要负责应用组件 和子模块)等等
|--caching 所有缓存方法,其中包含了Memcache缓存,APC缓存,数据缓存,CDummyCache虚拟缓存,CEAcceleratorCache缓存等等各种缓存方法
|--cli YII项目生成脚本
|--collections 用php语言构造传统OO语言的数据存储单元。如:队列,栈,哈希表等等
|--console YII控制台
|--db 数据库操作类
|--gii YII 代码生成器(脚手架),能生成包括模型,控制器,视图等代码
|--i18n YII 多语言,提供了各种语言的本地化数据,信息、文件的翻译服务、本地化日期和时间格式,数字等
|--logging 日志组件,YII提供了灵活和可扩展的日志记录功能。消息记录可分为根据日志级别和信息类别。应用层次和类别过滤器,可进一步选择的消息路由到不同的目的 地,例如文件,电子邮件,浏览器窗口,等等|--messages 提示信息的多语言
|--test YII提供的测试,包括单元测试和功能测试
|--utils 提供了常用的格式化方法
|--validators 提供了各种验证方法
|--vendors 这个文件夹包括第三方由Yii框架使用的资料库
|--views 提供了YII错误、日志、配置文件的多语言视图
|--web YII所有开发应用的方法
|---actions 控制器操作类
|---auth 权限认识类,包括身份认证,访问控制过滤,基本角色的访问控制等
|---filters 过滤器,可被配置在控制器动作执行之前或之后执行。例如, 访问控制过滤器将被执行以确保在执行请求的动作之前用户已通过身份验证;性能过滤器可用于测量控制器执行所用的时间
|---form 表单生成方法
|---helpers 视图助手,包含GOOGLE AJAX API,创建HTML,JSON,JAVASCRIPT相关功能
|---js JS库
|---renderers 视图渲染组件
|---services 封装SoapServer并提供了一个基于WSDL的Web服务
|---widgets 部件
|---CArrayDataProvider.php 可以配置的排序和分页属性自定义排序和分页的行为
|---CActiveDataProvider.php ActiveRecord方法类
|---CController.php 控制器方法,主要负责协调模型和视图之间的交互
|---CPagination.php 分页类
|---CUploadedFile.php 上传文件类
|---CUrlManager.php URL管理
|---CWebModule.php 应用模块管理,应用程序模块可被视为一个独立的子应用
等等方法
|--.htaccess 重定向文件
|--yii.php 引导文件
|--YiiBase.php YiiBase类最主要的功能是注册了自动加载类方法,加载框架要用到所有接口。
|--yiic Yii LINUX 命令行脚本
|--yiic.bat YII WINDOW 命令行脚本
|--yiilite.php 它是一些常用到的 Yii 类文件的合并文件。在文件中,注释和跟踪语句都被去除。因此,使用 yiilite.php 将减少被引用的文件数量并避免执行跟踪语句
二 源码分析
1. 启动
网站的唯一入口程序 index.php :
1. $yii =dirname( __FILE__ ). '/../framework/yii.php' ;
2. $config =dirname( __FILE__ ). '/protected/config/main.php' ;
3.
4. // remove the following line when in production mode
5. defined( 'YII_DEBUG' ) or define( 'YII_DEBUG' ,true);
6.
7. require_once ( $yii );
8. Yii::createWebApplication( $config )->run();
上面的 require_once ( $yii ) 引用出了后面要用到的全局类Yii,Yii类是YiiBase类的完全继承:
1. class Yii extends YiiBase
2. {
3. }
系统的全局访问都是通过Yii类(即YiiBase类)来实现的,Yii类的成员和方法都是 static 类型。
2. 类加载
Yii利用PHP5提供的spl库来完成类的自动加载。在YiiBase.php 文件结尾处
1. spl_autoload_register( array ( 'YiiBase' , 'autoload' ));
将YiiBase类的静态方法autoload 注册为类加载器。 PHP autoload 的简单原理就是执行 new 创建对象或通过类名访问静态成员时,系统将类名传递给被注册的类加载器函数,类加载器函数根据类名自行找到对应的类文件并 include 。
下面是YiiBase类的autoload方法:
1. public static function autoload( $className )
2. {
3. // use include so that the error PHP file may appear
4. if (isset(self:: $_coreClasses [ $className ]))
5. include (YII_PATH.self:: $_coreClasses [ $className ]);
6. else if (isset(self:: $_classes [ $className ]))
7. include (self:: $_classes [ $className ]);
8. else
9. include ( $className . '.php' );
10. }
可以看到YiiBase的静态成员 $_coreClasses 数组里预先存放着Yii系统自身用到的类对应的文件路径:
1. private static $_coreClasses = array (
2. 'CApplication' => '/base/CApplication.php' ,
3. 'CBehavior' => '/base/CBehavior.php' ,
4. 'CComponent' => '/base/CComponent.php' ,
5. ...
6. )
非 coreClasse 的类注册在YiiBase的 $_classes 数组中:
private static $_classes = array ();
其他的类需要用Yii::import()讲类路径导入PHP include paths 中,直接
include ( $className . '.php' )
3. CWebApplication的创建
回到前面的程序入口的 Yii::createWebApplication( $config )->run();
1. public static function createWebApplication( $config =null)
2. {
3. return new CWebApplication( $config );
4. }
现在autoload机制开始工作了。
当系统 执行 new CWebApplication() 的时候,会自动
include (YII_PATH. '/base/CApplication.php' )
将main.php里的配置信息数组 $config 传递给CWebApplication创建出对象,并执行对象的run() 方法启动框架。
CWebApplication类的继承关系
CWebApplication -> CApplication -> CModule -> CComponent
$config 先被传递给CApplication的构造函数
1. public function __construct( $config =null)
2. {
3. Yii::setApplication( $this );
4.
5. // set basePath at early as possible to avoid trouble
6. if ( is_string ( $config ))
7. $config = require ( $config );
8. if (isset( $config [ 'basePath' ]))
9. {
10. $this ->setBasePath( $config [ 'basePath' ]);
11. unset( $config [ 'basePath' ]);
12. }
13. else
14. $this ->setBasePath( 'protected' );
15. Yii::setPathOfAlias( 'application' , $this ->getBasePath());
16. Yii::setPathOfAlias( 'webroot' ,dirname( $_SERVER [ 'SCRIPT_FILENAME' ]));
17.
18. $this ->preinit();
19.
20. $this ->initSystemHandlers();
21. $this ->registerCoreComponents();
22.
23. $this ->configure( $config );
24. $this ->attachBehaviors( $this ->behaviors);
25. $this ->preloadComponents();
26.
27. $this ->init();
28. }
Yii::setApplication( $this ); 将自身的实例对象赋给Yii的静态成员 $_app ,以后可以通过 Yii::app() 来取得。
后面一段是设置CApplication 对象的_basePath ,指向 proteced 目录。
1. Yii::setPathOfAlias( 'application' , $this ->getBasePath());
2. Yii::setPathOfAlias( 'webroot' ,dirname( $_SERVER [ 'SCRIPT_FILENAME' ]));
设置了两个系统路径别名 application 和 webroot,后面再import的时候可以用别名来代替实际的完整路径。别名配置存放在YiiBase的 $_aliases 数组中。
$this ->preinit();
预初始化。preinit()是在 CModule 类里定义的,没有任何动作。
$this ->initSystemHandlers() 方法内容:
1. /**
2. * Initializes the class autoloader and error handlers.
3. */
4. protected function initSystemHandlers()
5. {
6. if (YII_ENABLE_EXCEPTION_HANDLER)
7. set_exception_handler( array ( $this , 'handleException' ));
8. if (YII_ENABLE_ERROR_HANDLER)
9. set_error_handler( array ( $this , 'handleError' ), error_reporting ());
10. }
设置系统exception_handler和 error_handler,指向对象自身提供的两个方法。
4. 注册核心组件
$this ->registerCoreComponents();
代码如下:
1. protected function registerCoreComponents()
2. {
3. parent::registerCoreComponents();
4.
5. $components = array (
6. 'urlManager' => array (
7. 'class' => 'CUrlManager' ,
8. ),
9. 'request' => array (
10. 'class' => 'CHttpRequest' ,
11. ),
12. 'session' => array (
13. 'class' => 'CHttpSession' ,
14. ),
15. 'assetManager' => array (
16. 'class' => 'CAssetManager' ,
17. ),
18. 'user' => array (
19. 'class' => 'CWebUser' ,
20. ),
21. 'themeManager' => array (
22. 'class' => 'CThemeManager' ,
23. ),
24. 'authManager' => array (
25. 'class' => 'CPhpAuthManager' ,
26. ),
27. 'clientScript' => array (
28. 'class' => 'CClientScript' ,
29. ),
30. );
31.
32. $this ->setComponents( $components );
33. }
注册了几个系统组件(Components)。
Components 是在 CModule 里定义和管理的,主要包括两个数组
1. private $_components = array ();
2. private $_componentConfig = array ();
每个 Component 都是 IApplicationComponent接口的实例,Componemt的实例存放在 $_components 数组里,相关的配置信息存放在 $_componentConfig 数组里。配置信息包括Component 的类名和属性设置。
CWebApplication 对象注册了以下几个Component:urlManager, request,session,assetManager,user,themeManager,authManager,clientScript。 CWebApplication的parent 注册了以下几个 Component:coreMessages,db,messages,errorHandler,securityManager,statePersister。
Component 在YiiPHP里是个非常重要的东西,它的特征是可以通过 CModule 的 __get() 和 __set() 方法来访问。 Component 注册的时候并不会创建对象实例,而是在程序里被第一次访问到的时候,由CModule 来负责(实际上就是 Yii::app())创建。
5. 处理 $config 配置
继续, $this ->configure( $config );
configure() 还是在CModule 里:
1. public function configure( $config )
2. {
3. if ( is_array ( $config ))
4. {
5. foreach ( $config as $key => $value )
6. $this -> $key = $value ;
7. }
8. }
实际上是把 $config 数组里的每一项传给 CModule 的 父类 CComponent __set() 方法。
1. public function __set( $name , $value )
2. {
3. $setter = 'set' . $name ;
4. if (method_exists( $this , $setter ))
5. $this -> $setter ( $value );
6. else if ( strncasecmp ( $name , 'on' ,2)===0
7. && method_exists( $this , $name ))
8. {
9. //duplicating getEventHandlers() here for performance
10. $name = strtolower ( $name );
11. if (!isset( $this ->_e[ $name ]))
12. $this ->_e[ $name ]= new CList;
13. $this ->_e[ $name ]->add( $value );
14. }
15. else if (method_exists( $this , 'get' . $name ))
16. throw new CException(Yii::t( 'yii' , 'Property "{class}.{property}" is read only.' ,
17. array ( '{class}' =>get_class( $this ), '{property}' => $name )));
18. else
19. throw new CException(Yii::t( 'yii' , 'Property "{class}.{property}" is not defined.' ,
20. array ( '{class}' =>get_class( $this ), '{property}' => $name )));
21. }
22. }
我们来看看:
if (method_exists( $this , $setter ))
根据这个条件, $config 数组里的basePath, params, modules, import, components 都被传递给相应的 setBasePath(), setParams() 等方法里进行处理。
6、 $config 之 import
其中 import 被传递给 CModule 的 setImport:
1. public function setImport( $aliases )
2. {
3. foreach ( $aliases as $alias )
4. Yii::import( $alias );
5. }
Yii::import( $alias )里的处理:
1. public static function import( $alias , $forceInclude =false)
2. {
3. // 先判断$alias是否存在于YiiBase::$_imports[] 中,已存在的直接return, 避免重复import。
4. if (isset(self:: $_imports [ $alias ])) // previously imported
5. return self:: $_imports [ $alias ];
6.
7. // $alias类已定义,记入$_imports[],直接返回
8. if ( class_exists ( $alias ,false))
9. return self:: $_imports [ $alias ]= $alias ;
10.
11. // 类似 urlManager 这样的已定义于$_coreClasses[]的类,或不含.的直接类名,记入$_imports[],直接返回
12. if (isset(self:: $_coreClasses [ $alias ]) || ( $pos = strrpos ( $alias , '.' ))===false) // a simple class name
13. {
14. self:: $_imports [ $alias ]= $alias ;
15. if ( $forceInclude )
16. {
17. if (isset(self:: $_coreClasses [ $alias ])) // a core class
18. require (YII_PATH.self:: $_coreClasses [ $alias ]);
19. else
20. require ( $alias . '.php' );
21. }
22. return $alias ;
23. }
24.
25. // 产生一个变量 $className,为$alias最后一个.后面的部分
26. // 这样的:'x.y.ClassNamer'
27. // $className不等于 '*', 并且ClassNamer类已定义的, ClassNamer' 记入 $_imports[],直接返回
28. if (( $className =(string) substr ( $alias , $pos +1))!== '*' && class_exists ( $className ,false))
29. return self:: $_imports [ $alias ]= $className ;
30.
31. // 取得 $alias 里真实的路径部分并且路径有效
32. if (( $path =self::getPathOfAlias( $alias ))!==false)
33. {
34. // $className!=='*',$className 记入 $_imports[]
35. if ( $className !== '*' )
36. {
37. self:: $_imports [ $alias ]= $className ;
38. if ( $forceInclude )
39. require ( $path . '.php' );
40. else
41. self:: $_classes [ $className ]= $path . '.php' ;
42. return $className ;
43. }
44. // $alias是'system.web.*'这样的已*结尾的路径,将路径加到include_path中
45. else // a directory
46. {
47. set_include_path(get_include_path().PATH_SEPARATOR. $path );
48. return self:: $_imports [ $alias ]= $path ;
49. }
50. }
51. else
52. throw new CException(Yii::t( 'yii' , 'Alias "{alias}" is invalid. Make sure it points to an existing directory or file.' ,
53. array ( '{alias}' => $alias )));
54. }
7. $config 之 components
$config 数组里的 $components 被传递给CModule 的setComponents( $components )
1. public function setComponents( $components )
2. {
3. foreach ( $components as $id => $component )
4. {
5. if ( $component instanceof IApplicationComponent)
6. $this ->setComponent( $id , $component );
7. else if (isset( $this ->_componentConfig[ $id ]))
8. $this ->_componentConfig[ $id ]=CMap::mergeArray( $this ->_componentConfig[ $id ], $component );
9. else
10. $this ->_componentConfig[ $id ]= $component ;
11. }
12. }
$componen 是IApplicationComponen的实例的时候,直接赋值:
$this ->setComponent( $id , $component ),
1. public function setComponent( $id , $component )
2. {
3. $this ->_components[ $id ]= $component ;
4. if (! $component ->getIsInitialized())
5. $component ->init();
6. }
如果 $id 已存在于_componentConfig[]中(前面注册的coreComponent),将 $component 属性加进入。
其他的component将component属性存入_compon
|