Yii應用的入口腳本引用出了Yii類,Yii類的定義:
class Yii extends YiiBase { }
由yiic創建的應用裡Yii類只是YiiBase類的“馬甲”,我們也可以根據需求定制自己的Yii類。
Yii(即YiiBase)是一個“helper class”,為整個應用提供靜態和全域存取入口。
Yii類別的幾個靜態成員:
$_aliases : 存放系統的別名對應的真實路徑
$_imports :
$_classes :
$_includePaths php include paths
_$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)的管理。
應用元件必須是IApplicationComponent介面的實例,需要實作介面的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; }
以上就是Yii框架分析(三)——类加载机制及应用组件的管理、配置、访问、创建的内容,更多相关内容请关注PHP中文网(www.php.cn)!