首頁  >  文章  >  php教程  >  Yii框架分析(三)-類別載入機制及應用元件的管理、設定、存取、創建

Yii框架分析(三)-類別載入機制及應用元件的管理、設定、存取、創建

黄舟
黄舟原創
2016-12-27 11:11:261373瀏覽

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)!


陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn