Heim >Backend-Entwicklung >PHP-Tutorial >So implementieren Sie einen Lightweight-Container in PHP (Codebeispiel)
Der Inhalt dieses Artikels befasst sich mit der Implementierung eines Lightweight-Containers (Codebeispiel) in PHP. Ich hoffe, er wird Ihnen als Referenz dienen.
Was ist ein Container?
Während des Entwicklungsprozesses wird häufig das Konzept der Abhängigkeitsinjektion verwendet. Wir nutzen Lazy-Injection, um Code zu entkoppeln und Dienste gezielt nach Bedarf zu laden, die in der Regel mit Hilfe von Containern umgesetzt werden.
Container implementieren eine einheitliche Verwaltung von Objekten und stellen die Einzigartigkeit von Objektinstanzen sicher
Container können mit vielen Implementierungsbeispielen leicht gefunden werden, wie z. B. PHP-DI und YII – Verschiedene Implementierungen wie DI sind in der Regel entweder umfangreich und umfassend oder stark an bestimmte Unternehmen angepasst, was im Widerspruch zu den tatsächlichen Anforderungen steht.
Aus der Not heraus bauen wir selbst ein Leichtbaurad. Um die Spezifikation einzuhalten, setzen wir es auf Basis von PSR-11 um.
PSR-11
PSR ist eine Standardisierungsempfehlung von php-fig. Obwohl es sich nicht um eine offizielle Organisation handelt, ist sie weithin anerkannt. PSR-11 bietet eine Containerschnittstelle. Es enthält ContainerInterface und zwei Ausnahmeschnittstellen und bietet Verwendungsvorschläge.
/** * Describes the interface of a container that exposes methods to read its entries. */ interface ContainerInterface { /** * Finds an entry of the container by its identifier and returns it. * * @param string $id Identifier of the entry to look for. * * @throws NotFoundExceptionInterface No entry was found for **this** identifier. * @throws ContainerExceptionInterface Error while retrieving the entry. * * @return mixed Entry. */ public function get($id); /** * Returns true if the container can return an entry for the given identifier. * Returns false otherwise. * * `has($id)` returning true does not mean that `get($id)` will not throw an exception. * It does however mean that `get($id)` will not throw a `NotFoundExceptionInterface`. * * @param string $id Identifier of the entry to look for. * * @return bool */ public function has($id); }
Implementierungsbeispiel
Lassen Sie uns zunächst die beiden in der Schnittstelle erforderlichen Methoden implementieren
abstract class AbstractContainer implements ContainerInterface { protected $resolvedEntries = []; /** * @var array */ protected $definitions = []; public function __construct($definitions = []) { foreach ($definitions as $id => $definition) { $this->injection($id, $definition); } } public function get($id) { if (!$this->has($id)) { throw new NotFoundException("No entry or class found for {$id}"); } $instance = $this->make($id); return $instance; } public function has($id) { return isset($this->definitions[$id]); }
Eigentlich sind in unserem Container verschiedene Objekte injiziert Wir extrahieren die Instanziierungsmethode separat.
protected function make($name) { if (isset($this->resolvedEntries[$name])) { return $this->resolvedEntries[$name]; } $definition = $this->definitions[$name]; $params = []; if (is_array($definition) && isset($definition['class'])) { $params = $definition; $definition = $definition['class']; unset($params['class']); } $object = $this->reflector($definition, $params); return $this->resolvedEntries[$name] = $object; } public function reflector($concrete, array $params = []) { if ($concrete instanceof \Closure) { return $concrete($params); } elseif (is_string($concrete)) { $reflection = new \ReflectionClass($concrete); $dependencies = $this->getDependencies($reflection); foreach ($params as $index => $value) { $dependencies[$index] = $value; } return $reflection->newInstanceArgs($dependencies); } elseif (is_object($concrete)) { return $concrete; } } /** * @param \ReflectionClass $reflection * @return array */ private function getDependencies($reflection) { $dependencies = []; $constructor = $reflection->getConstructor(); if ($constructor !== null) { $parameters = $constructor->getParameters(); $dependencies = $this->getParametersByDependencies($parameters); } return $dependencies; } /** * * 获取构造类相关参数的依赖 * @param array $dependencies * @return array $parameters * */ private function getParametersByDependencies(array $dependencies) { $parameters = []; foreach ($dependencies as $param) { if ($param->getClass()) { $paramName = $param->getClass()->name; $paramObject = $this->reflector($paramName); $parameters[] = $paramObject; } elseif ($param->isArray()) { if ($param->isDefaultValueAvailable()) { $parameters[] = $param->getDefaultValue(); } else { $parameters[] = []; } } elseif ($param->isCallable()) { if ($param->isDefaultValueAvailable()) { $parameters[] = $param->getDefaultValue(); } else { $parameters[] = function ($arg) { }; } } else { if ($param->isDefaultValueAvailable()) { $parameters[] = $param->getDefaultValue(); } else { if ($param->allowsNull()) { $parameters[] = null; } else { $parameters[] = false; } } } } return $parameters; }
Wie Sie sehen können, haben wir bisher nur die Instanz aus dem Container herausgenommen, in dem die Instanzdefinition bereitgestellt werden soll, daher müssen wir auch eine Methode bereitstellen.
/** * @param string $id * @param string | array | callable $concrete * @throws ContainerException */ public function injection($id, $concrete) { if (!is_string($id)) { throw new \InvalidArgumentException(sprintf( 'The id parameter must be of type string, %s given', is_object($id) ? get_class($id) : gettype($id) )); } if (is_array($concrete) && !isset($concrete['class'])) { throw new ContainerException('数组必须包含类定义'); } $this->definitions[$id] = $concrete; }
Das ist alles ? Ja, mit diesen Vorgängen haben wir bereits einen kompletten Container, der sofort einsatzbereit ist.
Aus Gründen der Benutzerfreundlichkeit können wir jedoch einige praktischere Methoden bereitstellen, wie z. B. den Array-Zugriff.
class Container extends AbstractContainer implements \ArrayAccess { public function offsetExists($offset) { return $this->has($offset); } public function offsetGet($offset) { return $this->get($offset); } public function offsetSet($offset, $value) { return $this->injection($offset, $value); } public function offsetUnset($offset) { unset($this->resolvedEntries[$offset]); unset($this->definitions[$offset]); } }
Auf diese Weise haben wir einen leichten Container, der reich an Funktionen ist und einfach zu verwenden ist. Lassen Sie uns ihn so schnell wie möglich in Ihr Projekt integrieren.
Klicken Sie hier, um denvollständigen Code anzuzeigen
Das obige ist der detaillierte Inhalt vonSo implementieren Sie einen Lightweight-Container in PHP (Codebeispiel). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!