Heute hatte ich ursprünglich vor, das Implementierungsprinzip des AR-Modells von yii2.0 zu studieren. Der Plan konnte jedoch nicht mit den Änderungen Schritt halten, und plötzlich wollte ich den Prozess der Erstellung der Datenbankkomponente von yii2.0 studieren Erste. Durch das Studium des yii-Quellcodes lernte ich den Prozess der Registrierung und Erstellung von yii-Komponenten kennen und stellte fest, dass die ursprünglichen yii-Komponenten nicht unmittelbar nach der Registrierung erstellt werden, sondern die entsprechenden Komponenten erst dann erstellt werden, wenn eine bestimmte Komponente tatsächlich benötigt wird. In diesem Artikel wird dieser Erkundungsprozess kurz beschrieben.
Um die Registrierung und Erstellung von yii-Komponenten zu verstehen, müssen wir natürlich mit der yiiEintragsdatei index.php beginnen. Der gesamte Dateicode lautet wie folgt:
<?php defined('YII_DEBUG') or define('YII_DEBUG', true); defined('YII_ENV') or define('YII_ENV', 'dev'); require(DIR . '/../../vendor/autoload.php'); require(DIR . '/../../vendor/yiisoft/yii2/Yii.php'); require(DIR . '/../../common/config/bootstrap.php'); require(DIR . '/../config/bootstrap.php'); $config = yii\helpers\ArrayHelper::merge( require(DIR . '/../../common/config/main.php'), require(DIR . '/../../common/config/main-local.php'), require(DIR . '/../config/main.php'), require(DIR . '/../config/main-local.php') ); (new yii\web\Application($config))->run();
Sie können sehen, dass die Eintragsdatei mehrere Konfigurationsdateien einführt, den Inhalt aller Konfigurationsdateien im $config-Konfigurationsarray zusammenführt und dieses Konfigurationsarray dann als Parameter zum Erstellen einer Anwendungsinstanz verwendet. Wenn Sie dieses Konfigurationsarray ausdrucken, werden Sie sehen, dass das Element, das dem Index „components“ entspricht, die Parameterinformationen der yii-Komponente enthält (hier wird nur ein kleiner Teil des Screenshots aufgenommen):
Die Informationen dieser Komponenten werden in mehreren importierten Konfigurationsdateien konfiguriert, die mithilfe dieser Parameterinformationen registriert und erstellt werden.
Der nächste Schritt besteht darin, den Instanziierungsprozess der yiiwebApplication-Klasse einzugeben. Die yiiwebApplication-Klasse verfügt nicht über einen Konstruktor , sondern erbt die yiibaseApplication-Klasse:
Die yiibaseApplication-Klasse wird automatisch ausgeführt:
public function construct($config = []) { Yii::$app = $this; static::setInstance($this); $this->state = self::STATE_BEGIN; $this->preInit($config); $this->registerErrorHandler($config); Component::construct($config); }
Übrigens ist hier die Vorinitialisierungsmethode preInit(), ihr Code lautet wie folgt:
public function preInit(&$config) { /* 此处省略对$config数组的预处理操作代码 */ // merge core components with custom components foreach ($this->coreComponents() as $id => $component) { if (!isset($config['components'][$id])) { $config['components'][$id] = $component; } elseif (is_array($config['components'][$id]) && !isset($config['components'][$id]['class'])) { $config['components'][$id]['class'] = $component['class']; } } }
Diese Funktion Behandelt das an den Konstruktor übergebene Konfigurationsarray $config. Nach einigen Vorverarbeitungsvorgängen (hier weggelassen) wurde das Array $config schließlich mithilfe des von der coreComponents()-Methode zurückgegebenen Arrays verbessert. Die coreComponents()-Methode sieht folgendermaßen aus:
public function coreComponents() { return [ 'log' => ['class' => 'yii\log\Dispatcher'], 'view' => ['class' => 'yii\web\View'], 'formatter' => ['class' => 'yii\i18n\Formatter'], 'i18n' => ['class' => 'yii\i18n\I18N'], 'mailer' => ['class' => 'yii\swiftmailer\Mailer'], 'urlManager' => ['class' => 'yii\web\UrlManager'], 'assetManager' => ['class' => 'yii\web\AssetManager'], 'security' => ['class' => 'yii\base\Security'], ]; }
Tatsächlich handelt es sich um eine Kernkomponentenkonfiguration, das heißt, diese Komponenten müssen nicht in der Konfigurationsdatei konfiguriert werden, Yii registriert sie automatisch.
OK, zurück zum Konstruktor der yiibaseApplication-Klasse. Diese Funktion ruft schließlich den Konstruktor der yiibaseComponent-Klasse auf, aber die yiibaseComponent-Klasse hat keinen Konstruktor, sondern erbt die yiibaseObject-Klasse:
Daher wird auch der Konstruktor der yiibaseObject-Klasse automatisch ausgeführt:
public function construct($config = []) { if (!empty($config)) { Yii::configure($this, $config); } $this->init(); }
Hier wird hauptsächlich die statische Methode configure() der yiiBaseYii-Klasse aufgerufen:
public static function configure($object, $properties) { foreach ($properties as $name => $value) { $object->$name = $value; } return $object; }
Diese Methode ist die Schleifeneintragsdatei< Das $config-Array in 🎜> (die Struktur dieses Arrays finden Sie im ersten Screenshot dieses Artikels) verwendet den Array-Schlüsselnamen als (new yiiwebApplication($config))->run();
Objektattributname und den entsprechenden Schlüsselwert als Objektattributwert für die Zuweisung. Wenn man also eine Schleife zu den Komponentenkonfigurationsparametern durchläuft, sieht das so aus: $object->components = $value ($value ist das Konfigurationsarray aller Komponenten), d. h. es wird dann dem Komponentenattribut von $object ein Wert zugewiesen this $object Um welche Objektklasse handelt es sich? Rückblickend auf die Quelle des ersten Aufrufs ist es tatsächlich das Objekt der yiiwebApplication-Klasse, das in der Eintragsdatei instanziiert werden muss. Allerdings verfügen weder diese Klasse noch ihre Vorgängerklasse über die Komponenten der Mitgliedsvariablen. Wir müssen die Vererbungsbeziehung der yiiwebApplication-Klasse erneut durchführen und nachschlagen Die Klasse erbt schließlich auch die yiibaseObject-Klasse, sodass auch die yiiwebApplication-Klasse Attribute unterstützt (Informationen zu Attributen finden Sie in meinem anderen Blog-Beitrag: Attribute von yii2). Wenn die Zuweisungsoperation die Komponenten-Mitgliedsvariable nicht finden kann, wird setComponents() verwendet aufgerufen. Methode, suchte nach dem Speicherort dieser Methode und fand schließlich die setComponents()-Methode in ihrer Vorgängerklasse yiidiServiceLocator Ja, das Zuweisen von Werten zum Komponentenattribut der Anwendungsinstanz ruft diese Methode tatsächlich auf!
public function setComponents($components) { foreach ($components as $id => $component) { $this->set($id, $component); } }
public function set($id, $definition) { unset($this->_components[$id]); if ($definition === null) { unset($this->_definitions[$id]); return; } if (is_object($definition) || is_callable($definition, true)) { // an object, a class name, or a PHP callable $this->_definitions[$id] = $definition; } elseif (is_array($definition)) { // a configuration array if (isset($definition['class'])) { $this->_definitions[$id] = $definition; } else { throw new InvalidConfigException("The configuration for the \"$id\" component must contain a \"class\" element."); } } else { throw new InvalidConfigException("Unexpected configuration type for the \"$id\" component: " . gettype($definition)); } }
搞了半天,原来yii创建应用实例的时候只是进行组件的注册,并没有实际创建组件,那么组件实例是什么时候进行创建的?在哪里进行创建的呢?别急。从上面推导的这个过程我们知道\yii\di\ServiceLocator类是\yii\web\Application类的祖先类,所以其实yii的应用实例其实就是一个服务定位器,比如我们想访问数据库组件的时候,我们可以这样来访问:Yii::$app->db,这个Yii::$app就是yii应用实例,也就是\yii\web\Application类的实例,但是\yii\web\Application类和它的父类、祖先类都找不到db这个属性啊。哈哈,别忘了,php读取不到类属性的时候会调用魔术方法get(),所以开始查找\yii\web\Application继承关系最近的祖先类中的get()方法,最后在\yii\di\ServiceLocator类中找到了,也就是说,Yii::$app->db最终会调用\yii\di\ServiceLocator类中的get()方法:
public function get($name) { if ($this->has($name)) { return $this->get($name); } else { return parent::get($name); } }
get()方法首先调用has()方法(这个不再贴代码了)判断组件是否已注册,若已注册则调用get()方法:
public function get($id, $throwException = true) { if (isset($this->_components[$id])) { return $this->_components[$id]; } if (isset($this->_definitions[$id])) { $definition = $this->_definitions[$id]; if (is_object($definition) && !$definition instanceof Closure) { return $this->_components[$id] = $definition; } else { return $this->_components[$id] = Yii::createObject($definition); } } elseif ($throwException) { throw new InvalidConfigException("Unknown component ID: $id"); } else { return null; } }
其中私有成员变量$_components是存储已经创建的组件实例的,若发现组件已经创建过则直接返回组件示例,否则使用$_definitions中对应组件的注册信息,调用\yii\BaseYii::createObject()方法进行组件创建,这个方法最终会调用依赖注入容器\yii\di\Container的get()方法,接着就是依赖注入创建对象的过程了,关于这个过程已经在我的上一篇博文中讲解过了,可以参考一下:yii2之依赖注入与依赖注入容器。
好了,yii组件注册与创建的整个过程就是这样的。最后总结一下,其实yii创建应用实例的时候只是进行了各个组件的注册,也就是将组件的配置信息存入\yii\di\ServiceLocator类的私有成员变量$_definitions中,并没有进行实际创建,等到程序运行过程中真正需要使用到某个组件的时候才根据该组件在$_definitions中保存的注册信息使用依赖注入容器\yii\di\Container进行组件实例的创建,然后把创建的实例存入私有成员变量$_components,这样下次访问相同组件的时候就可以直接返回组件实例,而不再需要执行创建过程了。yii的这个组件注册与创建机制其实是大有裨益的,试想一下,如果在应用实例创建的时候就进行所有组件的创建,将会大大增加应用实例创建的时间,用户每次刷新页面都会进行应用实例的创建的,也就是说用户每刷新一次页面都很慢,这用户体验就很不好了,而且很多情况下有很多组件其实是没有使用到的,但是我们还是花了不少时间去创建这些组件,这是很不明智的,所以yii的做法就是:先把组件参数信息保存起来,需要使用到哪些组件再去创建相应的实例,大大节省了应用创建的时间,同时也节省了内存,这种思路是很值得我们学习的!
总结
Das obige ist der detaillierte Inhalt vonDetaillierte Erläuterung der Registrierungs- und Erstellungsmethoden von Komponenten in Yii2. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!