博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Yii PHP 框架分析(三)
阅读量:5809 次
发布时间:2019-06-18

本文共 7937 字,大约阅读时间需要 26 分钟。

作者:wdy

Yii应用的入口脚本引用出了Yii类,Yii类的定义:

class Yii extends YiiBase

{
}

由yiic创建的应用里Yii类只是YiiBase类的“马甲”,我们也可以根据需求定制自己的Yii类。

Yii(即YiiBase)是一个“helper class”,为整个应用提供静态和全局访问入口。

Yii类的几个静态成员:

$_aliases : 存放系统的别名对应的真实路径
$_imports : 
$_classes :
$_includePaths php include paths
$_app : CWebApplication对象,通过 Yii::app() 访问到
$_logger : 系统日志对象

$_app 对象由 Yii::createWebApplication() 方法创建。

类自动加载

Yii基于php5的autoload机制来提供类的自动加载功能,自动加载器为YiiBase类的静态方法autoload()。

当程序中用new创建对象或访问到类的静态成员,php将类名传递给类加载器,由类加载器完成类文件的include。

autoload机制实现了类的“按需导入”,就是系统访问到类时才include类的文件。

YiiBase类的静态成员$_coreClasses 里预先存放了Yii自身的核心类名于对应的类文件路径。其他的Yii应用中用到的类可以用Yii::import() 导入,Yii::import()将单类的与对应类文件存放于$_classes中,以*通配符表示的路径加入到php include_path中。被Yii::import()导入的类文件或目录都记入$_imports中,避免多次导入。

/* Yii::import()* $alias: 要导入的类名或路径* $forceInclude false:只导入不include类文件,true则导入并include类文件*/public static function import($alias,$forceInclude=false){   // 先判断$alias是否存在于YiiBase::$_imports[] 中,已存在的直接return, 避免重复import。   if(isset(self::$_imports[$alias])) // previously imported    return self::$_imports[$alias];   // $alias类已定义,记入$_imports[],直接返回   if(class_exists($alias,false) || interface_exists($alias,false))    return self::$_imports[$alias]=$alias;   // 已定义于$_coreClasses[]的类,或名字中不含.的类,记入$_imports[],直接返回   if(isset(self::$_coreClasses[$alias]) || ($pos=strrpos($alias,'.'))===false) // a simple class name   {    self::$_imports[$alias]=$alias;    if($forceInclude)    {     if(isset(self::$_coreClasses[$alias])) // a core class      require(YII_PATH.self::$_coreClasses[$alias]);     else      require($alias.'.php');    }    return $alias;   }   // 产生一个变量 $className,为$alias最后一个.后面的部分   // 这样的:'x.y.ClassNamer'   // $className不等于 '*', 并且ClassNamer类已定义的,      ClassNamer' 记入 $_imports[],直接返回   if(($className=(string)substr($alias,$pos+1))!=='*' && (class_exists($className,false) || interface_exists($className,false)))    return self::$_imports[$alias]=$className;   // $alias里含有别名,并转换真实路径成功   if(($path=self::getPathOfAlias($alias))!==false)   {    // 不是以*结尾的路径(单类)    if($className!=='*')    {     self::$_imports[$alias]=$className;     if($forceInclude)      require($path.'.php');     else      // 类名与真实路径记入$_classes数组      self::$_classes[$className]=$path.'.php';     return $className;    }    // $alias是'system.web.*'这样的已*结尾的路径,将路径加到include_path中    else // a directory    {     if(self::$_includePaths===null)     {      self::$_includePaths=array_unique(explode(PATH_SEPARATOR,get_include_path()));      if(($pos=array_search('.',self::$_includePaths,true))!==false)       unset(self::$_includePaths[$pos]);     }     array_unshift(self::$_includePaths,$path);     set_include_path('.'.PATH_SEPARATOR.implode(PATH_SEPARATOR,self::$_includePaths));     return self::$_imports[$alias]=$path;    }   }   else    throw new CException(Yii::t('yii','Alias "{alias}" is invalid. Make sure it points to an existing directory or file.',     array('{alias}'=>$alias)));}

然后看看 YiiBase::autoload() 函数的处理:

public static function autoload($className){   // $_coreClasses中配置好的类直接引入   if(isset(self::$_coreClasses[$className]))    include(YII_PATH.self::$_coreClasses[$className]);   // $_classes 中登记的单类直接引入   else if(isset(self::$_classes[$className]))    include(self::$_classes[$className]);   else   {    // 其他的认为文件路径以记入 include_path 里,以$className.'.php'直接引入    include($className.'.php');    return class_exists($className,false) || interface_exists($className,false);   }   return true;}

系统配置文件里的 import 项里的类或路径在脚本启动中会被自动导入。用户应用里个别类需要引入的类可以在类定义前加入 Yii::import() 语句。

应用组件管理

前面提到Yii的CComponent类提供了组件的属性、事件、行为的访问接口,而CComponent的子类CModule更提供了应用组件(application components)的管理。

应用组件必须是IApplicationComponen接口的实例,需要实现接口的init()和getIsInitialized()方法。init()会在应用组件初始化参数后被自动调用。

Yii自身的功能模块都是通过应用组件的方式来提供的,比如常见的 Yii::app()->user, Yii::app()->request 等。用户也可以定义应用组件。

作为 Yii::app() 对象(CWebApplication)的父类,CModule提供了完整的组件生命周期管理,包括组件的创建、初始化、对象存储等。

每个应用组件用一个字符串名字来标识,通过CModule类的__get() 方法来访问。

CModule类的$_components[] 成员存放应用组件的对象实例($name => $object),$_componentConfig[] 里存放应用组件的类名和初始化参数。

使用应用组件的时候,先在$_componentConfig里设置好组件的类名和初始化参数,在第一次访问组件的时候,CModule会自动创建应用组件对象实例并初始化给定的参数,然后会调用应用组件的init()方法。

Yii::app()对象的类CWebApplication及其父类CApplication预先配置了系统自身用到的应用组件:

urlManager, request, session, assetManager, user, themeManager, authManager, clientScript, coreMessages, db, messages, errorHandler, securityManager, statePersister。

我们可以再系统配置文件的components项目里修改系统应用组件的参数或配置新的应用组件。

CModule并不负责应用组件实例的创建,而是由Yii::createComponent() 静态方法来完成的。

createComponent()的参数$config 可以是类名的字符串或是存储了类名和初始化参数的数组。

应用组件的配置

应用组件的配置存储在系统$config变量中(config/main.php里)的components项里:

// application components'components'=>array(   'log'=>array(    'class'=>'CLogRouter',    'routes'=>array(     array(      'class'=>'CFileLogRoute',      'levels'=>'error, warning',     ),    ),   ),   'user'=>array(    // enable cookie-based authentication    'allowAutoLogin'=>true,   ),),$config里的components项在CApplication的构造函数里被处理:$this->configure($config);configure()函数的处理很简单:public function configure($config){   if(is_array($config))   {    foreach($config as $key=>$value)     $this->$key=$value;   }}

$config里的每一项被当做属性传给$_app对象的setXXX()属性设置方法,其中'components’项在CWebApplication的CModule的父类setComponents()处理。

setComponents() 将'components’项里的类名及初始化参数存放到 $_componentConfig[]里:

public function setComponents($components){   // $config 里的’components’每一项   foreach($components as $id=>$component)   {    if($component instanceof IApplicationComponent)     $this->setComponent($id,$component);    // $_componentConfig里已经存在配置,合并$component    else if(isset($this->_componentConfig[$id]))     $this->_componentConfig[$id]=CMap::mergeArray($this->_componentConfig[$id],$component);    // 在$_componentConfig里新建项目    else     $this->_componentConfig[$id]=$component;   }}

应用组件的访问

CModule类重载了CComponent的__get()方法,优先访问应用组件对象。

public function __get($name)

{
   if($this->hasComponent($name))
    return $this->getComponent($name);
   else
    return parent::__get($name);
}

hasComponent() 判断$_components[]中是否已存在组件实例,或$_componentConfig[]中存在组件配置信息。

public function hasComponent($id)

{
   return isset($this->_components[$id]) || isset($this->_componentConfig[$id]);
}

getComponent() 判断组件实例已经存在于$_components[]中,则直接返回对象。

否则根据$_componentConfig[]里的组件配置数据调用 Yii::createComponent() 来创建组件,并将对象存入$_components[]中然后返回。

public function getComponent($id,$createIfNull=true){   if(isset($this->_components[$id]))    return $this->_components[$id];   else if(isset($this->_componentConfig[$id]) && $createIfNull)   {    $config=$this->_componentConfig[$id];    unset($this->_componentConfig[$id]);    if(!isset($config['enabled']) || $config['enabled'])    {     Yii::trace("Loading \"$id\" application component",'system.web.CModule');     unset($config['enabled']);     $component=Yii::createComponent($config);     $component->init();     return $this->_components[$id]=$component;    }   }}

应用组件的创建

Yii::createComponent() 来完成应用组件的创建

public static function createComponent($config){   if(is_string($config))   {    $type=$config;    $config=array();   }   else if(isset($config['class']))   {    $type=$config['class'];    unset($config['class']);   }   else    throw new CException(Yii::t('yii','Object configuration must be an array containing a "class" element.'));   if(!class_exists($type,false))    $type=Yii::import($type,true);   if(($n=func_num_args())>1)   {    $args=func_get_args();    if($n===2)     $object=new $type($args[1]);    else if($n===3)     $object=new $type($args[1],$args[2]);    else if($n===4)     $object=new $type($args[1],$args[2],$args[3]);    else    {     unset($args[0]);     $class=new ReflectionClass($type);     // Note: ReflectionClass::newInstanceArgs() is available for PHP 5.1.3+     // $object=$class->newInstanceArgs($args);     $object=call_user_func_array(array($class,'newInstance'),$args);    }   }   else    $object=new $type;   foreach($config as $key=>$value)    $object->$key=$value;   return $object;}

 

转载于:https://www.cnblogs.com/imxiu/p/3414235.html

你可能感兴趣的文章
LeetCode--112--路径总和
查看>>
感悟贴2016-05-13
查看>>
百度编辑器ueditor 光标位置的坐标
查看>>
DEV-C++ 调试方法简明图文教程(转)
查看>>
参加婚礼
查看>>
Java重写equals方法和hashCode方法
查看>>
Spark API编程动手实战-07-join操作深入实战
查看>>
EasyUI基础入门之Easyloader(载入器)
查看>>
Spring ’14 Wave Update: Installing Dynamics CRM on Tablets for Windows 8.1
查看>>
MySQL 备份与恢复
查看>>
TEST
查看>>
PAT A1037
查看>>
(六)Oracle学习笔记—— 约束
查看>>
[Oracle]如何在Oracle中设置Event
查看>>
top.location.href和localtion.href有什么不同
查看>>
02-创建hibernate工程
查看>>
Scrum之 Sprint计划会议
查看>>
svn命令在linux下的使用
查看>>
Gradle之module间依赖版本同步
查看>>
java springcloud版b2b2c社交电商spring cloud分布式微服务(十五)Springboot整合RabbitMQ...
查看>>