Heim  >  Artikel  >  Backend-Entwicklung  >  Erfahren Sie, wie Sie manuell einen PHP-DI-Container erstellen

Erfahren Sie, wie Sie manuell einen PHP-DI-Container erstellen

藏色散人
藏色散人nach vorne
2021-12-01 14:23:063377Durchsuche

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.

Beginnen Sie mit dem Fahren

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

Dependency-Injection ähnelt dem IoC-Modus und dem Factory-Modus. Es handelt sich um einen Modus, der die Abhängigkeitskopplungsbeziehung zwischen dem Anrufer und dem Angerufenen löst. Es löst die Abhängigkeiten zwischen Objekten auf, sodass die Objekte nur noch vom IoC/DI-Container abhängen und nicht mehr direkt voneinander abhängen, wodurch eine lose Kopplung erreicht wird. Wenn das Objekt dann erstellt wird, fügt der IoC/DI-Container seine Abhängigkeit ein. Objekte (Inject), dadurch kann eine maximale lose Kopplung erreicht werden. Um es klar auszudrücken: Abhängigkeitsinjektion bedeutet, dass der Container Instanzen anderer Klassen, von denen eine bestimmte Klasse abhängt, in Instanzen dieser Klasse einfügt.

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

Um eine Funktion zu schreiben, müssen wir zunächst das Problem analysieren. Daher müssen wir zunächst verstehen, welche Funktionen für einen einfachen DI-Container erforderlich sind, was in direktem Zusammenhang mit dem Schreiben unseres Codes steht. Für einen einfachen Container müssen mindestens die folgenden Punkte erfüllt sein:

    Erstellen Sie eine Instanz der erforderlichen Klasse
  • Vollständiges Abhängigkeitsmanagement (DI)
  • Sie können eine Instanz eines Singletons erhalten
  • Global einzigartig
  • Zusammenfassend sieht unsere Containerklasse so aus:
$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:

    Implementieren Sie die ArrayAccess-Schnittstelle, damit die Singleton-Instanz direkt über das Array abgerufen werden kann. Wenn die Instanz nicht vorhanden ist, erstellen Sie sie.
  • Schreiben Sie die __get-Methode neu, um sie bequemer zu machen um
  • Endversion:
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-Tutorial

“]

Das 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!

Stellungnahme:
Dieser Artikel ist reproduziert unter:csdn.net. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen