Heim >Backend-Entwicklung >PHP-Tutorial >Erfahren Sie, wie Sie manuell einen PHP-DI-Container erstellen
Was die Abhängigkeitsinjektion angeht, glaube ich, dass jeder regelmäßig damit vertraut sein sollte oder zumindest davon gehört hat. Die bekannteren Frameworks unterstützen die Abhängigkeitsinjektion, wie Javas Spring, PHPs Laravel, Symfony usw. Lassen Sie mich nun mit der manuellen Implementierung eines einfachen DI-Containers beginnen.
Lass uns zuerst ein Auto fahren und dir ein Beispiel geben:
class Driver{ public function drive() { $car = new Car(); echo '老司机正在驾驶', $car->getCar(), PHP_EOL; } }class Car{ protected $name = '普通汽车'; public function getCar() { return $this->name; } }
Der alte Fahrer hat einen Methodenfahrer. Beim Aufruf muss zuerst das gesamte Auto sein $ Auto, und dann losfahren. Die meisten Schüler haben diesen oder einen ähnlichen Code geschrieben. An diesem Code ist nichts auszusetzen und er ist ganz normal. Wenn ich jedoch mein Auto wechseln möchte, kann ich mit einem normalen Auto keine Mädchen anlocken.
class Benz extends Car{ protected $name = '奔驰'; }
Zu diesem Zeitpunkt müssen Sie eine ziemlich ekelhafte Operation durchführen und den Code des erfahrenen Fahrers ändern. (Alter Fahrer: Was habe ich falsch gemacht? Wenn ich ein Auto wechsle, muss ich meinen Führerschein neu erlernen...). Deshalb müssen wir das Auto in die Außenwelt bringen und Fahrer und Auto entkoppeln, damit erfahrene Fahrer beim Fahren kein eigenes Auto mehr bauen müssen. Wir haben also das folgende Ergebnis: Zu diesem Zeitpunkt wurden die Klassen „Fahrer“ und „Auto“ entkoppelt, und die Abhängigkeiten dieser beiden Klassen werden vom Code der oberen Ebene verwaltet. Zu diesem Zeitpunkt „fährt“ der erfahrene Fahrer wie folgt:
class Driver{ protected $car; public function __construct(Car $car) { $this->car = $car; } public function drive() { echo '老司机正在驾驶', $this->car->getCar(), PHP_EOL; } }
Zu diesem Zeitpunkt erstellen wir eine Instanz der Treiberabhängigkeit und fügen sie ein. Im obigen Beispiel haben wir die Abhängigkeitsinjektion implementiert, diese erfolgte jedoch manuell und das Schreiben war immer noch unangenehm. Wie kann eine so schwere Arbeit manuell erledigt werden? Wir müssen das Programm selbst erledigen lassen. Seitdem wurde der DI-Container geboren.
Dependency-Injection-Container
Diese Passage ist vielleicht etwas abstrakt. Kehren wir zum Beispiel zurück. Ich habe die Abhängigkeitsinjektion gerade manuell durchgeführt, was ziemlich mühsam ist. Wenn es in einem großen Projekt durchgeführt wird, wird es definitiv sehr umständlich und nicht elegant genug sein. Deshalb brauchen wir einen Verwalter, der dies für uns erledigt, und dieser Verwalter ist der Behälter. Die gesamte Abhängigkeitsverwaltung von Klassen wird dem Container überlassen. Daher ist ein Container im Allgemeinen ein globales Objekt, das von allen gemeinsam genutzt wird.
Erstellen Sie Ihren eigenen DI-Container
$car = new Car(); $driver = new Driver($car); $driver->drive();
Das allgemeine Grundgerüst wurde bestimmt, und dann geben wir die Core-Make-Methode ein:
class Container{ /** * 单例 * @var Container */ protected static $instance; /** * 容器所管理的实例 * @var array */ protected $instances = []; private function __construct(){} private function __clone(){} /** * 获取单例的实例 * @param string $class * @param array ...$params * @return object */ public function singleton($class, ...$params) {} /** * 获取实例(每次都会创建一个新的) * @param string $class * @param array ...$params * @return object */ public function get($class, ...$params) {} /** * 工厂方法,创建实例,并完成依赖注入 * @param string $class * @param array $params * @return object */ protected function make($class, $params = []) {} /** * @return Container */ public static function getInstance() { if (null === static::$instance) { static::$instance = new static(); } return static::$instance; } }
Um die Verwendung des Containers zu vereinfachen Ich habe einige Verbesserungen vorgenommen:
protected function make($class, $params = []){ //如果不是反射类根据类名创建 $class = is_string($class) ? new ReflectionClass($class) : $class; //如果传的入参不为空,则根据入参创建实例 if (!empty($params)) { return $class->newInstanceArgs($params); } //获取构造方法 $constructor = $class->getConstructor(); //获取构造方法参数 $parameterClasses = $constructor ? $constructor->getParameters() : []; if (empty($parameterClasses)) { //如果构造方法没有入参,直接创建 return $class->newInstance(); } else { //如果构造方法有入参,迭代并递归创建依赖类实例 foreach ($parameterClasses as $parameterClass) { $paramClass = $parameterClass->getClass(); $params[] = $this->make($paramClass); } //最后根据创建的参数创建实例,完成依赖的注入 return $class->newInstanceArgs($params); } }
zu erhalten, schreiben wir nun mit Hilfe des Containers den obigen Code:
class Container implements ArrayAccess{ /** * 单例 * @var Container */ protected static $instance; /** * 容器所管理的实例 * @var array */ protected $instances = []; private function __construct(){} private function __clone(){} /** * 获取单例的实例 * @param string $class * @param array ...$params * @return object */ public function singleton($class, ...$params) { if (isset($this->instances[$class])) { return $this->instances[$class]; } else { $this->instances[$class] = $this->make($class, $params); } return $this->instances[$class]; } /** * 获取实例(每次都会创建一个新的) * @param string $class * @param array ...$params * @return object */ public function get($class, ...$params) { return $this->make($class, $params); } /** * 工厂方法,创建实例,并完成依赖注入 * @param string $class * @param array $params * @return object */ protected function make($class, $params = []) { //如果不是反射类根据类名创建 $class = is_string($class) ? new ReflectionClass($class) : $class; //如果传的入参不为空,则根据入参创建实例 if (!empty($params)) { return $class->newInstanceArgs($params); } //获取构造方法 $constructor = $class->getConstructor(); //获取构造方法参数 $parameterClasses = $constructor ? $constructor->getParameters() : []; if (empty($parameterClasses)) { //如果构造方法没有入参,直接创建 return $class->newInstance(); } else { //如果构造方法有入参,迭代并递归创建依赖类实例 foreach ($parameterClasses as $parameterClass) { $paramClass = $parameterClass->getClass(); $params[] = $this->make($paramClass); } //最后根据创建的参数创建实例,完成依赖的注入 return $class->newInstanceArgs($params); } } /** * @return Container */ public static function getInstance() { if (null === static::$instance) { static::$instance = new static(); } return static::$instance; } public function __get($class) { if (!isset($this->instances[$class])) { $this->instances[$class] = $this->make($class); } return $this->instances[$class]; } public function offsetExists($offset) { return isset($this->instances[$offset]); } public function offsetGet($offset) { if (!isset($this->instances[$offset])) { $this->instances[$offset] = $this->make($offset); } return $this->instances[$offset]; } public function offsetSet($offset, $value) { } public function offsetUnset($offset) { unset($this->instances[$offset]); } }
So einfach ist das, ein erfahrener Fahrer kann das Auto starten. Die Standardinjektion ist hier die Instanz von Car. Wenn Sie einen Mercedes-Benz fahren müssen, müssen Sie nur Folgendes tun:
$driver = $app->get(Driver::class); $driver->drive();//output:老司机正在驾驶普通汽车复制代码
Gemäß den Anforderungen von PSR-11 muss der Abhängigkeitsinjektionscontainer die PsrContainerContainerInterface-Schnittstelle implementieren. Dies ist nur eine Demonstration und wurde nicht implementiert, da die Einführung von Psr mühsamer ist, aber eigentlich sehr einfach. Interessierte können sich über die Anforderungen informieren PSR-11 (Portal) allein.
Dies ist nur ein sehr einfacher implementierter DI-Container. In der Praxis gibt es viel zu beachten und die Containerfunktion ist hier immer noch sehr einfach. Es gibt noch einige Fallstricke, die noch nicht behoben wurden, wie zum Beispiel der Umgang mit zirkulären Abhängigkeiten und dem Mechanismus des verzögerten Ladens ...
Dies ist nur eine Aufzeichnung meiner kostenlosen Wochenendpraxis. Wenn Sie interessiert sind, können Sie sie lesen Sehen Sie sich unten den Quellcode des Laravel- oder Symfony-Containers an oder erfahren Sie mehr. Schauen wir uns den Spring-Container an. Ich werde es weiter verbessern, wenn ich Zeit habe. [Empfohlenes Lernen: „
PHP-Video-TutorialDas obige ist der detaillierte Inhalt vonErfahren Sie, wie Sie manuell einen PHP-DI-Container erstellen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!