ホームページ >php教程 >PHP开发 >Yii フレームワーク分析 (3) - クラスローディングメカニズムとアプリケーションコンポーネントの管理、設定、アクセスおよび作成

Yii フレームワーク分析 (3) - クラスローディングメカニズムとアプリケーションコンポーネントの管理、設定、アクセスおよび作成

黄舟
黄舟オリジナル
2016-12-27 11:11:261419ブラウズ

Yii アプリケーションのエントリ スクリプトは Yii クラスを参照します。Yii クラスの定義は次のとおりです。

class Yii extends YiiBase
{
}

yiic によって作成されたアプリケーションでは、Yii クラスは YiiBase クラスの単なる「ベスト」です。ニーズに応じて独自の Yii クラスを作成します。

Yii (つまり YiiBase) は、アプリケーション全体への静的かつグローバルなアクセスを提供する「ヘルパー クラス」です。

Yii クラスのいくつかの静的メンバー:
$_aliases: システムのエイリアスに対応する実際のパスを格納します
$_imports:
$_classes:
$_includePaths php インクルード パス
$_app: Yii:: を介した CWebApplication オブジェクトapp()
$_logger へのアクセス: Yii::createWebApplication() メソッドによって作成されたシステム ログ オブジェクト
$_app オブジェクト。

クラスの自動ロード

Yii は php5 のオートロード機構に基づいてクラスの自動ロード機能を提供します。自動ローダーは YiiBase クラスの静的メソッド autoload() です。

new を使用してオブジェクトを作成したり、プログラム内のクラスの静的メンバーにアクセスしたりすると、PHP はクラス名をクラス ローダーに渡し、クラス ローダーはクラス ファイルのインクルードを完了します。

オートロード メカニズムは、クラスの「オンデマンド インポート」を実装します。これは、システムがクラスにアクセスする場合にのみ、システムがクラスのファイルを含めることを意味します。

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;
}

システム設定ファイルのインポート項目のクラスまたはパスは、スクリプトの起動時に自動的にインポートされます。ユーザーアプリケーションの個々のクラスにインポートする必要があるクラスの場合は、クラス定義の前に Yii::import() ステートメントを追加できます。

アプリケーションコンポーネント管理

前述したように、Yii の CComponent クラスはコンポーネントのプロパティ、イベント、動作へのアクセスインターフェイスを提供し、CComponent のサブクラス CModule もアプリケーションコンポーネントの管理を提供します。

アプリケーション コンポーネントは 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。

システム アプリケーション コンポーネントのパラメーターを変更したり、システム構成ファイルのコンポーネント プロジェクトで新しいアプリケーション コンポーネントを構成したりできます。

CModule はアプリケーションコンポーネントインスタンスの作成を担当しませんが、Yii::createComponent() 静的メソッドによって完了されます。

createComponent() のパラメーター $config は、クラス名の文字列、またはクラス名と初期化パラメーターを格納する配列にすることができます。

アプリケーションコンポーネントの設定

アプリケーションコンポーネントの設定は、システム $config 変数 (config/main.php) のコンポーネント項目に保存されます:

// 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 内のコンポーネント項目は、CApplication のコンストラクターで処理されます。

$this->configure($config);

configure() 関数の処理は非常に簡単です:

public function configure($config)
{
    if(is_array($config))
    {
        foreach($config as $key=>$value)
            $this->$key=$value;
    }
}

$config 内のすべての項目は、$_app オブジェクトの setXXX() 属性設定メソッドに属性として渡されます。 「コンポーネント」項目は、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 までご連絡ください。