Heim > Artikel > Backend-Entwicklung > Informationen zur Eigenschaftsinjektion und Methodeninjektion des Komponentenverhaltens des Yii-Frameworks in PHP
In diesem Artikel wird hauptsächlich die detaillierte Erklärung der Attributinjektion und Methodeninjektion des Komponentenverhaltens im Yii-Framework von PHP vorgestellt, einschließlich einer Erklärung der Abhängigkeitsinjektion. Freunde in Not können sich auf die Attribute und Methoden von
Prinzip der Methodeninjektion
Wir haben oben gelernt, dass der Zweck des Verhaltens darin besteht, seine eigenen Attribute und Methoden in die angehängte Klasse einzufügen. Wie fügt Yii also die Eigenschaften und Methoden eines Verhaltens yiibaseBehavior in eine yiibaseComponent ein? Bei Eigenschaften wird dies durch die magischen Methoden __get() und __set() erreicht. Bei Methoden erfolgt dies über die Methode __call().Injektion von Eigenschaften
Nehmen Sie das Lesen als Beispiel: Wenn Sie auf $Component->property1 zugreifen, was macht Yii hinter den Kulissen? Werfen wir einen Blick auf yiibaseComponent::__get()public function __get($name) { $getter = 'get' . $name; if (method_exists($this, $getter)) { return $this->$getter(); } else { // 注意这个 else 分支的内容,正是与 yii\base\Object::__get() 的 // 不同之处 $this->ensureBehaviors(); foreach ($this->_behaviors as $behavior) { if ($behavior->canGetProperty($name)) { // 属性在行为中须为 public。否则不可能通过下面的形式访问呀。 return $behavior->$name; } } } if (method_exists($this, 'set' . $name)) { throw new InvalidCallException('Getting write-only property: ' . get_class($this) . '::' . $name); } else { throw new UnknownPropertyException('Getting unknown property: ' . get_class($this) . '::' . $name); } }Konzentrieren Sie sich auf den Unterschied zwischen yiibaseComponent::__get() und yiibaseObject::__get(). Das heißt, für die Verarbeitung nach der undefinierten Getter-Funktion löst yiibaseObject direkt eine Ausnahme aus, die Ihnen mitteilt, dass die Eigenschaft, auf die Sie zugreifen möchten, nicht existiert. Aber für yiibaseComponent muss, nachdem kein Getter vorhanden ist, immer noch geprüft werden, ob es sich um ein Attribut des injizierten Verhaltens handelt: Zuerst wird $this->ensureBehaviors() aufgerufen. Diese Methode wurde bereits erwähnt, hauptsächlich um sicherzustellen, dass das Verhalten gebunden wurde.
Nachdem Sie sichergestellt haben, dass das Verhalten gebunden wurde, beginnen Sie mit dem Durchlaufen von $this->_behaviors . Yii speichert alle gebundenen Verhaltensweisen der Klasse im Array yiibaseComponent::$_behaviors[].
Bestimmen Sie abschließend, ob diese Eigenschaft eine lesbare Eigenschaft des gebundenen Verhaltens ist, indem Sie canGetProperty() des Verhaltens verwenden. Wenn ja, geben Sie die Eigenschaft $behavior->name dieses Verhaltens zurück. Vollständige Lektüre der Eigenschaften. canGetProperty() wurde im Abschnitt :ref::property kurz besprochen und wird später gezielt eingeführt.
Für den Setter ist der Code ähnlich, sodass er hier keinen Platz einnimmt.
Injektion von Methoden
ähnelt der Injektion von Attributen durch die magische Methode __get() __set() Yii implementiert die Methode im Verhalten durch __call(. ) magische Methode Injektion:public function __call($name, $params) { $this->ensureBehaviors(); foreach ($this->_behaviors as $object) { if ($object->hasMethod($name)) { return call_user_func_array([$object, $name], $params); } } throw new UnknownMethodException('Calling unknown method: ' . get_class($this) . "::$name()"); }Wie aus dem obigen Code ersichtlich ist, ruft Yii zuerst $this->ensureBehaviors() auf, um sicherzustellen, dass das Verhalten vorliegt gebunden worden. Dann durchläuft es auch das Array yiibaseComponent::$_behaviros[]. Bestimmen Sie mithilfe der Methode hasMethod(), ob die Methode vorhanden ist. Wenn die Methode, die im gebundenen Verhalten aufgerufen werden soll, vorhanden ist, rufen Sie sie mit call_user_func_array() von PHP auf. Was die Methode hasMethod() betrifft, werden wir später darauf eingehen.
Zugriffskontrolle injizierter Eigenschaften und Methoden
Im vorherigen Abschnitt haben wir aufgelistet, ob die öffentlichen, privaten und geschützten Mitglieder im Verhalten in der gebundenen Klasse zugänglich sind . konkrete Beispiele. Hier analysieren wir die Gründe auf Codeebene. Im obigen Inhalt wissen wir, dass es hauptsächlich vom Verhalten von canGetProperty() und canSetProperty() abhängt, ob auf eine Eigenschaft zugegriffen werden kann oder nicht. Ob eine Methode aufgerufen werden kann oder nicht, hängt hauptsächlich vom Verhalten von hasMethod() ab. Da yiibaseBehavior von unserem alten Freund yiibaseObject erbt, befinden sich die Codes für die drei oben genannten Beurteilungsmethoden tatsächlich in Object. Schauen wir sie uns einzeln an:public function canGetProperty($name, $checkVars = true) { return method_exists($this, 'get' . $name) || $checkVars && property_exists($this, $name); } public function canSetProperty($name, $checkVars = true) { return method_exists($this, 'set' . $name) || $checkVars && property_exists($this, $name); } public function hasMethod($name) { return method_exists($this, $name); }Diese drei Methoden sind wirklich überhaupt nicht kompliziert. In dieser Hinsicht können wir die folgenden Schlussfolgerungen ziehen: Wenn beim Lesen (Schreiben) einer Eigenschaft für ein an eine Komponente gebundenes Verhalten das Verhalten einen Getter (Setter) für die Eigenschaft definiert, kann darauf zugegriffen werden. Oder wenn das Verhalten diese Mitgliedsvariable hat, kann es das obige Urteil fällen. Zu diesem Zeitpunkt kann die Mitgliedsvariable öffentlich, privat oder geschützt sein. Letztendlich kann jedoch nur auf öffentliche Mitgliedsvariablen korrekt zugegriffen werden. Der Grund wurde oben erläutert, als es um das Prinzip der Injektion ging.
Wenn Sie eine Methode eines an eine Komponente gebundenen Verhaltens aufrufen und das Verhalten die Methode definiert hat, kann das obige Urteil gefällt werden. Derzeit kann diese Methode öffentlich, privat oder geschützt sein. Letztendlich kann aber nur die öffentliche Methode korrekt aufgerufen werden. Wenn Sie den Grund für den vorherigen verstehen, werden Sie ihn auch hier verstehen.
Dependency-Injection-Container
Dependency-Injection-Container (DI) ist ein Objekt, das weiß, wie das Objekt und alle Objekte, von denen es abhängt, initialisiert und konfiguriert werden. Martins Artikel erklärt bereits, warum DI-Container nützlich sind. Hier erklären wir hauptsächlich die Verwendung des von Yii bereitgestellten DI-Containers.
Abhängigkeitsinjektion
Yii stellt DI-Containerfunktionen über die yiidiContainer-Klasse bereit. Es unterstützt die folgenden Arten der Abhängigkeitsinjektion:class Foo { public function __construct(Bar $bar) { } } $foo = $container->get('Foo'); // 上面的代码等价于: $bar = new Bar; $foo = new Foo($bar);
Setter und Property Injection
Setter 和属性注入是通过配置提供支持的。当注册一个依赖或创建一个新对象时,你可以提供一个配置,该配置会提供给容器用于通过相应的 Setter 或属性注入依赖。例如:
use yii\base\Object; class Foo extends Object { public $bar; private $_qux; public function getQux() { return $this->_qux; } public function setQux(Qux $qux) { $this->_qux = $qux; } } $container->get('Foo', [], [ 'bar' => $container->get('Bar'), 'qux' => $container->get('Qux'), ]);
PHP 回调注入
这种情况下,容器将使用一个注册过的 PHP 回调创建一个类的新实例。回调负责解决依赖并将其恰当地注入新创建的对象。例如:
$container->set('Foo', function () { return new Foo(new Bar); }); $foo = $container->get('Foo');
注册依赖关系
可以用 yii\di\Container::set() 注册依赖关系。注册会用到一个依赖关系名称和一个依赖关系的定义。依赖关系名称可以是一个类名,一个接口名或一个别名。依赖关系的定义可以是一个类名,一个配置数组,或者一个 PHP 回调。
$container = new \yii\di\Container; // 注册一个同类名一样的依赖关系,这个可以省略。 $container->set('yii\db\Connection'); // 注册一个接口 // 当一个类依赖这个接口时,相应的类会被初始化作为依赖对象。 $container->set('yii\mail\MailInterface', 'yii\swiftmailer\Mailer'); // 注册一个别名。 // 你可以使用 $container->get('foo') 创建一个 Connection 实例 $container->set('foo', 'yii\db\Connection'); // 通过配置注册一个类 // 通过 get() 初始化时,配置将会被使用。 $container->set('yii\db\Connection', [ 'dsn' => 'mysql:host=127.0.0.1;dbname=demo', 'username' => 'root', 'password' => '', 'charset' => 'utf8', ]); // 通过类的配置注册一个别名 // 这种情况下,需要通过一个 “class” 元素指定这个类 $container->set('db', [ 'class' => 'yii\db\Connection', 'dsn' => 'mysql:host=127.0.0.1;dbname=demo', 'username' => 'root', 'password' => '', 'charset' => 'utf8', ]); // 注册一个 PHP 回调 // 每次调用 $container->get('db') 时,回调函数都会被执行。 $container->set('db', function ($container, $params, $config) { return new \yii\db\Connection($config); }); // 注册一个组件实例 // $container->get('pageCache') 每次被调用时都会返回同一个实例。 $container->set('pageCache', new FileCache);
Tip: 如果依赖关系名称和依赖关系的定义相同,则不需要通过 DI 容器注册该依赖关系。
通过 set() 注册的依赖关系,在每次使用时都会产生一个新实例。可以使用 yii\di\Container::setSingleton() 注册一个单例的依赖关系:
$container->setSingleton('yii\db\Connection', [ 'dsn' => 'mysql:host=127.0.0.1;dbname=demo', 'username' => 'root', 'password' => '', 'charset' => 'utf8', ]);
解决依赖关系
注册依赖关系后,就可以使用 DI 容器创建新对象了。容器会自动解决依赖关系,将依赖实例化并注入新创建的对象。依赖关系的解决是递归的,如果一个依赖关系中还有其他依赖关系,则这些依赖关系都会被自动解决。
可以使用 yii\di\Container::get() 创建新的对象。该方法接收一个依赖关系名称,它可以是一个类名,一个接口名或一个别名。依赖关系名或许是通过 set() 或 setSingleton() 注册的。你可以随意地提供一个类的构造器参数列表和一个configuration 用于配置新创建的对象。例如:
// "db" 是前面定义过的一个别名 $db = $container->get('db'); // 等价于: $engine = new \app\components\SearchEngine($apiKey, ['type' => 1]); $engine = $container->get('app\components\SearchEngine', [$apiKey], ['type' => 1]);
代码背后,DI 容器做了比创建对象多的多的工作。容器首先将检查类的构造方法,找出依赖的类或接口名,然后自动递归解决这些依赖关系。
如下代码展示了一个更复杂的示例。UserLister 类依赖一个实现了 UserFinderInterface 接口的对象;UserFinder 类实现了这个接口,并依赖于一个 Connection 对象。所有这些依赖关系都是通过类构造器参数的类型提示定义的。通过属性依赖关系的注册,DI 容器可以自动解决这些依赖关系并能通过一个简单的 get('userLister') 调用创建一个新的 UserLister 实例。
namespace app\models; use yii\base\Object; use yii\db\Connection; use yii\di\Container; interface UserFinderInterface { function findUser(); } class UserFinder extends Object implements UserFinderInterface { public $db; public function __construct(Connection $db, $config = []) { $this->db = $db; parent::__construct($config); } public function findUser() { } } class UserLister extends Object { public $finder; public function __construct(UserFinderInterface $finder, $config = []) { $this->finder = $finder; parent::__construct($config); } } $container = new Container; $container->set('yii\db\Connection', [ 'dsn' => '...', ]); $container->set('app\models\UserFinderInterface', [ 'class' => 'app\models\UserFinder', ]); $container->set('userLister', 'app\models\UserLister'); $lister = $container->get('userLister'); // 等价于: $db = new \yii\db\Connection(['dsn' => '...']); $finder = new UserFinder($db); $lister = new UserLister($finder);
实践中的运用
当在应用程序的入口脚本中引入 Yii.php 文件时,Yii 就创建了一个 DI 容器。这个 DI 容器可以通过 Yii::$container 访问。当调用 Yii::createObject() 时,此方法实际上会调用这个容器的 yii\di\Container::get() 方法创建新对象。如上所述,DI 容器会自动解决依赖关系(如果有)并将其注入新创建的对象中。因为 Yii 在其多数核心代码中都使用了 Yii::createObject() 创建新对象,所以你可以通过 Yii::$container 全局性地自定义这些对象。
例如,你可以全局性自定义 yii\widgets\LinkPager 中分页按钮的默认数量:
\Yii::$container->set('yii\widgets\LinkPager', ['maxButtonCount' => 5]);
这样如果你通过如下代码在一个视图里使用这个挂件,它的 maxButtonCount 属性就会被初始化为 5 而不是类中定义的默认值 10。
echo \yii\widgets\LinkPager::widget();
然而你依然可以覆盖通过 DI 容器设置的值:
echo \yii\widgets\LinkPager::widget(['maxButtonCount' => 20]);
另一个例子是借用 DI 容器中自动构造方法注入带来的好处。假设你的控制器类依赖一些其他对象,例如一个旅馆预订服务。你可以通过一个构造器参数声明依赖关系,然后让 DI 容器帮你自动解决这个依赖关系。
namespace app\controllers; use yii\web\Controller; use app\components\BookingInterface; class HotelController extends Controller { protected $bookingService; public function __construct($id, $module, BookingInterface $bookingService, $config = []) { $this->bookingService = $bookingService; parent::__construct($id, $module, $config); } }
如果你从浏览器中访问这个控制器,你将看到一个报错信息,提醒你 BookingInterface 无法被实例化。这是因为你需要告诉 DI 容器怎样处理这个依赖关系。
\Yii::$container->set('app\components\BookingInterface', 'app\components\BookingService');
现在如果你再次访问这个控制器,一个 app\components\BookingService 的实例就会被创建并被作为第三个参数注入到控制器的构造器中。
什么时候注册依赖关系
由于依赖关系在创建新对象时需要解决,因此它们的注册应该尽早完成。如下是推荐的实践:
如果你是一个应用程序的开发者,你可以在应用程序的入口脚本或者被入口脚本引入的脚本中注册依赖关系。
如果你是一个可再分发扩展的开发者,你可以将依赖关系注册到扩展的引导类中。
总结
Abhängigkeitsinjektion und Service Locator sind beliebte Entwurfsmuster, mit denen Sie Software in einem vollständig entkoppelten und testfreundlicheren Stil erstellen können. Ich empfehle Ihnen dringend, Martins Artikel zu lesen, um ein tieferes Verständnis der Abhängigkeitsinjektion und der Service-Locators zu erlangen.
Yii implementiert seinen Service Locator zusätzlich zum Dependency Check-in (DI)-Container. Wenn ein Service-Locator versucht, eine neue Objektinstanz zu erstellen, leitet er den Aufruf an den DI-Container weiter. Letzteres löst Abhängigkeiten automatisch auf, wie oben beschrieben.
Das Obige ist der gesamte Inhalt dieses Artikels. Ich hoffe, er wird für das Studium aller hilfreich sein. Weitere verwandte Inhalte finden Sie auf der chinesischen PHP-Website.
Verwandte Empfehlungen:
Analyse von Yii-Framework-Komponenten und Ereignisverhaltensmanagement
Über den Aufbau und die Implementierung des Backends von Yii2 rbac-Berechtigungskontrolle
Das obige ist der detaillierte Inhalt vonInformationen zur Eigenschaftsinjektion und Methodeninjektion des Komponentenverhaltens des Yii-Frameworks in PHP. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!