Maison > développement back-end > tutoriel php > Comment implémenter un conteneur léger en php (exemple de code)

Comment implémenter un conteneur léger en php (exemple de code)

不言
Libérer: 2023-04-04 21:22:02
avant
3182 Les gens l'ont consulté

Le contenu de cet article explique comment implémenter un conteneur léger (exemple de code) en PHP. Il a une certaine valeur de référence. Les amis dans le besoin peuvent s'y référer.

Qu'est-ce qu'un conteneur ?

Au cours du processus de développement, un concept souvent utilisé est l'injection de dépendances. Nous utilisons l'injection paresseuse pour découpler le code et charger sélectivement les services à la demande, qui sont généralement implémentés à l'aide de conteneurs.

Les conteneurs implémentent une gestion unifiée des objets et garantissent l'unicité des instances d'objet

Les conteneurs peuvent être facilement trouvés avec de nombreux exemples d'implémentation, tels que PHP-DI et YII - Divers les implémentations telles que DI sont généralement soit vastes et complètes, soit hautement adaptées à des entreprises spécifiques, ce qui entre en conflit avec les besoins réels.

Par nécessité, nous construisons nous-mêmes une roue légère. Afin de maintenir la spécification, nous la mettons en œuvre sur la base du PSR-11.

PSR-11

PSR est une recommandation de normalisation fournie par php-fig Bien qu'il ne s'agisse pas d'une organisation officielle, elle est largement reconnue. PSR-11 fournit une interface de conteneur. Il contient ContainerInterface et deux interfaces d'exception, et fournit des suggestions d'utilisation.

/**
 * 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);
}
Copier après la connexion

Exemple d'implémentation

Implémentons d'abord les deux méthodes requises dans l'interface

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]);
    }
Copier après la connexion

Les objets réels injectés dans notre conteneur Là Il existe de nombreux types, nous extrayons donc la méthode d'instanciation séparément.

    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;
    }
Copier après la connexion

Comme vous pouvez le voir, jusqu'à présent, nous avons uniquement retiré l'instance du conteneur. Où fournir la définition de l'instance, nous devons donc également fournir une méthode.

    /**
     * @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;
    }
Copier après la connexion

Est-ce le seul moyen ? Oui, avec ces opérations, nous disposons déjà d'un conteneur complet, prêt à l'emploi, prêt à l'emploi.

Cependant, pour faciliter l'utilisation, nous pouvons proposer des méthodes plus pratiques, telles que l'accès aux tableaux.

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]);
    }
}
Copier après la connexion

Nous disposons ainsi d’un conteneur léger, riche en fonctionnalités et facile à utiliser. Intégrons-le dans votre projet au plus vite.

Cliquez ici pour voirCode complet


Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:segmentfault.com
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal