• Как работает Yii::app()?

    copist
    @copist
    Empower people to give
    1. Инициализация Yii::app()

    Смотри, в файле www/index.php такие строки
    1. $config = APP_PATH . '/protected/config/main.php';
    2. $app = \Yii::createWebApplication($config); // @var CWebApplication $app
    3. $app->run();


    При отработке строки 2 выполняется код
    1. class YiiBase {
    2. {
    3.     // ... другой код
    4. 
    5.     public static function createWebApplication($config=null)
    6.     {
    7.         return self::createApplication('CWebApplication',$config);
    8.     }
    9.
    10.    public static function createApplication($class,$config=null)
    11.    {
    12.        return new $class($config);
    13.    }
    14. }


    В строке 12 с учётом динамического связывания выполняется return new CWebApplication($config);

    Если пройти по родительским классам, то можно найти, что
    class CWebApplication extends CApplication ...

    и далее
    1. abstract class CApplication extends CModule
    2. {
    3.     // ... другой код
    4. 
    5.     public function __construct($config=null)
    6.     {
    7.         Yii::setApplication($this);
    8.         // ... и другой код
    9.     }
    10. }


    В строке 7 вызывается Yii::setApplication($this);, а если посмотреть класс Yii и его родителя YiiBase, то можно увидеть как в итоге инициализируется переменная для конструкции Yii::app()
    class Yii extends YiiBase { }

    class YiiBase {
    {
        private static $_app;
        public static function setApplication($app) // тут определяется значение self::$_app для Yii::app()
        {
            if(self::$_app===null || $app===null)
                self::$_app=$app;
            else
                throw new CException(Yii::t('yii','Yii application can only be created once.'));
        }
        public static function app() // тут можно прочитать значение self::$_app через Yii::app()
        {
            return self::$_app;
        }
    }


    Теперь понятно, что при инициализации сущности типа CWebApplication эта сущность установилась в приватную переменную YiiBase::$_app, которая доступна через вызов Yii::app()

    Это была инициализация

    2. Использование Yii::app()->module или Yii::app()->component

    Ещё раз посмотри на класс CApplication
    abstract class CApplication extends CModule
    {
        public function __construct($config=null)
        {
            Yii::setApplication($this);
    
            // ... другой код
    
            $this->configure($config);
    
            // ... другой код
        }
    }


    В методе configure($config) происходит подготовка данных, которые понадобятся позже для таких вызовов как (к примеру) Yii::app()->db

    Давай посмотрим на класс Module, в котором и реализуется логика таких вызовов
    abstract class CModule extends CComponent
    {
        public function configure($config) // сохранить всё что передали по переменным
        {
            if(is_array($config))
            {
                foreach($config as $key=>$value)
                    $this->$key=$value;
            }
        }
    
        public function __get($name) // если кто-то пытается вызвать несуществующее свойство, например Yii::app()->db
        {
            if($this->hasComponent($name)) // проверить что есть настройки или готовая сущность компоненты
                return $this->getComponent($name); // вернуть сущность компоненты
            else
                return parent::__get($name);
        }
    
        public function hasComponent($id) // проверить что есть настройки или готовая сущность компоненты
        {
            return isset($this->_components[$id]) || isset($this->_componentConfig[$id]);
        }
    
        public function getComponent($id,$createIfNull=true) // вернуть сущность компоненты
        {
            if(isset($this->_components[$id])) // если есть готовая сущность компоненты, вернуть её
                return $this->_components[$id];
            elseif(isset($this->_componentConfig[$id]) && $createIfNull)
            {
                $config=$this->_componentConfig[$id];
                if(!isset($config['enabled']) || $config['enabled'])
                {
                    unset($config['enabled']);
                    $component=Yii::createComponent($config); // создать новую сущность компоненты
                    $component->init();
                    return $this->_components[$id]=$component; // сохранить и вернуть её
                }
            }
        }
    }


    Попробуем расшифровать вызов Yii::app()->db

    Как я уже показал, Yii::app() - это сущность типа CWebApplication, а у неё нет публичного свойства $db, поэтому PHP вызывает магический метод __get() из базового класса CModule. (Посмотри в документации "магические методы")

    Итак, свойства CWebApplication->db не существует и вызывается CModule->__get('db'), далее код считает, что, возможно производится вызов компоненты.

    Метод CModule->hasComponent('db') проверяет, что ранее были заданы какие-то настройки указанной через файл конфигурации protected/config/main.php или иным способом. Если так, то то вызывается CModule->getComponent('db', ...), который вызывает Yii::createComponent($config), где $config - это найденные настройки указанной компоненты, например, параметры соединения с базой данных для компоненты db

    Посмотрим, что делает Yii::createComponent($config)
    class YiiBase
    {
        public static function createComponent($config)
        {
            // посмотри сам, тут интересно
        }
    }


    Cложный метод. Суть его в инстанциировании новой сущности какого-то класса, 5 или 6 способов. Дело в том, что этот метод CModule::createComponent() может быть вызван откуда угодно и у него куча вариантов передачи параметров.

    Итак, CModule::createComponent() создал и вернул новую сущность. И на выходе из метода CModule::getComponent() полученная сущность записывается в массив CModule->_components под именем 'db'.

    Все последующие вызовы Yii::app()->db будут проверять наличие инстанциинованной сущности в массиве CModule->_components['db'] и использовать его, если он там найден

    Заключение:

    Вызов Yii::app()->db
    А. Динамически инициализирует компоненту, по мере необходимости
    Б. Настраивает её данными, которые в частновти можно определить через файл конфигурации protected/config/main.php
    В. Инициализируют компоненту только один раз за цикл работы PHP приложения

    Надеюсь я понятно изложил механизм ленивой инициализации компонент
    Ответ написан
    1 комментарий