🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
# 1、引言 ## 1.1、Yii 简介 * Yii 的作者是美籍华人“薛强”,他原是 Prado 核心开发成员之一。2008 年薛强另起炉灶, 开发了 Yii 框架,于 2008 年 12 月 3 日发布了 Yii1.0 版本。 * Yii 是目前比较优秀的 PHP 框架之一,它的支持的特性包括:MVC、DAO/ActiveRecord、 I18N/L10N、caching、AJAX 支持、用户认证和基于角色的访问控制、脚手架、输入验证、部 件、事件、主题化以及 Web 服务等。 `Yii 的很多思想参考了其它一些比较优秀的 Web 框架(我们写东西时是不是也喜欢参考别人的? 有木有?嘿嘿,都喜欢站在别人的肩膀上干活!)` ## 1.2、本文内容与结构 本文对 Yii1.1.8 版本的源代码进行了深入的分析,本文的内容与结构为: * 组件化与模块化: * 对 Yii 的基于组件和事件驱动编程模式的基础类(CComponent)进行分析; * 对组件化和模块化的工作原理进行分析; * 对 WebApp 应用创建 Controller 流程等进行分析。 * 系统组件:对 Yii 框架自带的重要组件进行分析,主要包括:日志路由组件、Url 管理组 件、异常处理组件、Cache 组件、基于角色的访问控制组件等。 * 控制器层:控制器主要包含 Action 和 Filter,对 Action 与 Filter 的工作原理进行分析。 * 模型层:对 DAO 层、元数据和 Command 构造器、ORM 的原理进行分析 * 视图层:对视图层的渲染过程、Widget 和客户端脚本组件等进行分析 `本文档中的错误或不妥之处在所难免,殷切希望本文档的读者给予批评指正!` # 2、组件化与模块化 ## 2.1、框架加载和运行流程 * 首先从入口处分析index.php ```php $yii=dirname(__FILE__).'/../framework/yii.php'; $config=dirname(__FILE__).'/protected/config/main.php'; require_once($yii); //引入yii框架 $application = Yii::createWebApplication($config) //先从这里追踪 $application->run(); ``` $config 为配置文件,先看看如何加载配置文件内容的, 找到Yii类,该类没有任何内容,只是继承了Yiibase。 ``` class Yii extends YiiBase{ } ``` * Yiibase.php * 从Yiibase中找到了createWebApplication方法 调用createApplication并传递了参数'CWebApplication' 和 $config ``` public static function createWebApplication($config=null) { return self::createApplication('CWebApplication',$config); } ``` * 返回并实例化 CWebApplication 对象,$config也传递了过去。 ``` public static function createApplication($class,$config=null) { return new $class($config); } ``` * CWebApplication.php,因为该类没有构造方法,所以找到父类的CApplication.php的构造方法 * 执行构造方法__construct ``` public function __construct($config=null) { Yii::setApplication($this); //把当前对象属性值赋值给Yiibase,这样便可通过Yii::app()调用main.php中的值 // set basePath at early as possible to avoid trouble if(is_string($config)) $config=require($config); //引入main.php文件,并赋值给$config //basePath为项目路径,检测路径是否正确,并赋值给$this->_basePath。 if(isset($config['basePath'])) { $this->setBasePath($config['basePath']); unset($config['basePath']); } else $this->setBasePath('protected'); //初始化别名,用于import引入,如:Yii::import('system.web.*'); 或 'application.models.*' Yii::setPathOfAlias('application',$this->getBasePath()); Yii::setPathOfAlias('webroot',dirname($_SERVER['SCRIPT_FILENAME'])); Yii::setPathOfAlias('ext',$this->getBasePath().DIRECTORY_SEPARATOR.'extensions'); $this->preinit(); //初始化类autoloader和错误处理程序。 $this->initSystemHandlers(); //注册核心应用程序组件。将db,messages等注册到CModule类的_componentConfig属性中 //例如:Yii::app()->db,Yii::app()->message $this->registerCoreComponents(); //用指定的配置配置模块,即main.php中的所有配置注册到CModule中 $this->configure($config); //将行为列表附加到组件。这里$this->behaviors为空数组 $this->attachBehaviors($this->behaviors); //加载静态应用程序组件。 $this->preloadComponents(); $this->init(); } ``` * $this->registerCoreComponents(); ``` //注册核心应用程序组件。将db,messages等注册到CModule类的_componentConfig属性中 protected function registerCoreComponents() { $components=array( 'coreMessages'=>array( 'class'=>'CPhpMessageSource', 'language'=>'en_us', 'basePath'=>YII_PATH.DIRECTORY_SEPARATOR.'messages', ), 'db'=>array( 'class'=>'CDbConnection', ), 'messages'=>array( 'class'=>'CPhpMessageSource', ), 'errorHandler'=>array( 'class'=>'CErrorHandler', ), 'securityManager'=>array( 'class'=>'CSecurityManager', ), 'statePersister'=>array( 'class'=>'CStatePersister', ), 'urlManager'=>array( 'class'=>'CUrlManager', ), 'request'=>array( 'class'=>'CHttpRequest', ), 'format'=>array( 'class'=>'CFormatter', ), ); $this->setComponents($components); } ``` * $this->configure($config); ``` //用指定的配置配置模块,即main.php中的所有配置注册到CModule中, //配置中下标为class的注册到_componentConfig属性,其余的注册到_params属性 public function configure($config) { if(is_array($config)) { foreach($config as $key=>$value) $this->$key=$value; } } ``` * $this->preloadComponents(); ``` //加载静态应用程序组件。main.php中 //reload'=>array('log'),,即预加载日志组件 //CLogRouter 对象信息(包含main.php中log['routes']的所有配置信息 //赋值给CModule->_components['CLogRouter '] //当前可以调用Yii::log()来记录日志 protected function preloadComponents() { foreach($this->preload as $id) $this->getComponent($id); } ``` * $application->run(); ``` //运行控制器 public function run() { if($this->hasEventHandler('onBeginRequest')) $this->onBeginRequest(new CEvent($this)); register_shutdown_function(array($this,'end'),0,false); //运行默认控制器,或获取url地址运行指定控制器 $this->processRequest(); if($this->hasEventHandler('onEndRequest')) $this->onEndRequest(new CEvent($this)); } ``` * $this->processRequest(); ``` public function processRequest() { if(is_array($this->catchAllRequest) && isset($this->catchAllRequest[0])) { $route=$this->catchAllRequest[0]; foreach(array_splice($this->catchAllRequest,1) as $name=>$value) $_GET[$name]=$value; } else $route=$this->getUrlManager()->parseUrl($this->getRequest()); //获取url中的controller/action , 如message/list //运行控制器 $this->runController($route); } ``` * $this->runController($route); ``` public function runController($route) { //实例化控制器,如 new messageController if(($ca=$this->createController($route))!==null) { list($controller,$actionID)=$ca; $oldController=$this->_controller; $this->_controller=$controller; $controller->init(); //运行action方法 $controller->run($actionID); $this->_controller=$oldController; } else throw new CHttpException(404,Yii::t('yii','Unable to resolve the request "{route}".', array('{route}'=>$route===''?$this->defaultController:$route))); }` ``` * $controller->run($actionID); 运行控制器中的actionID方法 ``` public function run($actionID) { if(($action=$this->createAction($actionID))!==null) { if(($parent=$this->getModule())===null) $parent=Yii::app(); if($parent->beforeControllerAction($this,$action)) { $this->runActionWithFilters($action,$this->filters()); $parent->afterControllerAction($this,$action); } } else $this->missingAction($actionID); } ``` * 之后就会运行咱自己书写的控制机及方法