依赖注入容器PHP [英] Dependency Injection Container PHP

查看:79
本文介绍了依赖注入容器PHP的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我最近了解了在PHP应用程序中使用依赖注入(DI)的优点。

I've recently learned about the advantages of using Dependency Injection (DI) in my PHP application.

但是,我仍然不确定如何为依赖项创建容器。以前,我使用框架中的容器,我想了解他如何在后台做事并复制它。

However, I'm still unsure how to create my container for the dependencies. Before, I use a container from a framework and I want to understand how is he doing things in back and reproduce it.

例如:

Zend 2的容器。我知道容器使类成为动态的,他不必从一开始就知道它们,他检查他的注册表中是否已经有该类,以及是否有他不是要检查该类是否存在以及构造函数中是否包含什么参数,并将其放入自己的注册表中,以便下次可以从那里使用它,实用是动态进行的所有事情,并且它正在完成自己的注册表,因此我们不必小心一旦实现了容器,他就可以提供任何我们想要的类,即使我们只是创建该类也没有什么。

The container from Zend 2. I understand that the container make class dynamic, he does not have to know about them from the beginning, he checks if he already has that class in his registry and if he has not he check if that class exist and what parameters has inside constructor and put it in his own registry so next time could take it from there, practical is doing everything dynamic and it is completing his own registry, so we do not have to take care of nothing once we implement the container as he can give as any class we want even if we just make that class.

如果我想 A的getInstance 需要B,而B需要CI需要CI知道他这样做是递归的,所以他先实例化了C,然后是B,最后是A。

Also if I want to getInstance for A which needs B and B needs C I understand that he doing this recursive and he goes and instantiate C then B and finally A.

所以我了解全局,他应该怎么做可以,但是我不确定如何实现。

So I understand the big picture and what is he suppose to do but I am not so sure about how to implement it.

推荐答案

使用现有的依赖容器之一可能会更好在那里,例如PHP-DI或Pimple。但是,如果您正在寻找一个更简单的解决方案,那么在本文中写的文章中,我已经实现了一个依赖容器: http://software-architecture-php.blogspot.com/

You may be better off using one of the existing Dependency Containers out there, such as PHP-DI or Pimple. However, if you are looking for a simpler solution, then I've implemented a Dependency Container as part of an article that I wrote here: http://software-architecture-php.blogspot.com/

以下是容器的代码

    class Container implements \DecoupledApp\Interfaces\Container\ContainerInterface 
{
    /**
     * This function resolves the constructor arguments and creates an object
     * @param string $dataType
     * @return mixed An object
     */
    private function createObject($dataType)
    {
        if(!class_exists($dataType)) {
            throw new \Exception("$dataType class does not exist");
        }
        $reflectionClass = new \ReflectionClass($dataType);
        $constructor = $reflectionClass->getConstructor();
        $args = null;
        $obj = null;
        if($constructor !== null)
        {
            $block = new \phpDocumentor\Reflection\DocBlock($constructor);

            $tags = $block->getTagsByName("param");
            if(count($tags) > 0)
            {
                $args = array();
            }
            foreach($tags as $tag)
            {
                //resolve constructor parameters
                $args[] = $this->resolve($tag->getType());
            }
        }
        if($args !== null)
        {
            $obj = $reflectionClass->newInstanceArgs($args);
        }
        else
        {
            $obj = $reflectionClass->newInstanceArgs();
        }

        return $obj;
    }

    /**
     * Resolves the properities that have a type that is registered with the Container. 
     * @param mixed $obj
     */
    private function resolveProperties(&$obj)
    {
        $reflectionClass = new \ReflectionClass(get_class($obj));
        $props = $reflectionClass->getProperties();
        foreach($props as $prop)
        {
            $block = new \phpDocumentor\Reflection\DocBlock($prop);

            //This assumes that there is only one "var" tag.
            //If there are more than one, then only the first one will be considered.
            $tags = $block->getTagsByName("var");
            if(isset($tags[0]))
            {
                $value = $this->resolve($tags[0]->getType());

                if($value !== null)
                {
                    if($prop->isPublic()) {
                        $prop->setValue($obj, $value);
                    } else {
                        $setter = "set".ucfirst($prop->name);
                        if($reflectionClass->hasMethod($setter)) {
                            $rmeth = $reflectionClass->getMethod($setter);
                            if($rmeth->isPublic()){
                                $rmeth->invoke($obj, $value);
                            }
                        }
                    }
                }
            }
        }
    }

    /**
     * 
     * @param string $dataType
     * @return object|NULL If the $dataType is registered, the this function creates the corresponding object and returns it;
     * otherwise, this function returns null
     */
    public function resolve($dataType) 
    {
        $dataType = trim($dataType, "\\");
        $obj = null;
        if(isset($this->singletonRegistry[$dataType])) 
        {
            //TODO: check if the class exists
            $className = $this->singletonRegistry[$dataType];
            $obj = $className::getInstance();
        } 
        else if(isset($this->closureRegistry[$dataType]))
        {
            $obj = $this->closureRegistry[$dataType]();
        }
        else if(isset($this->typeRegistry[$dataType])) 
        {
            $obj = $this->createObject($this->typeRegistry[$dataType]);
        }

        if($obj !== null) 
        {
            //Now we need to resolve the object properties
            $this->resolveProperties($obj);
        }
        return $obj;
    }

    /**
     * @see \DecoupledApp\Interfaces\Container\ContainerInterface::make()
     */
    public function make($dataType)
    {
        $obj = $this->createObject($dataType);
        $this->resolveProperties($obj);
        return $obj;
    }

    /**
     *
     * @param Array $singletonRegistry
     * @param Array $typeRegistry
     * @param Array $closureRegistry
     */
    public function __construct($singletonRegistry, $typeRegistry, $closureRegistry) 
    {
        $this->singletonRegistry = $singletonRegistry;
        $this->typeRegistry = $typeRegistry;
        $this->closureRegistry = $closureRegistry;
    }

    /**
     * An array that stores the mappings of an interface to a concrete singleton class. 
     * The key/value pair corresond to the interface name/class name pair.
     * The interface and class names are all fully qualified (i.e., include the namespaces).
     * @var Array
     */
    private $singletonRegistry;

    /**
     * An array that stores the mappings of an interface to a concrete class. 
     * The key/value pair corresond to the interface name/class name pair.
     * The interface and class names are all fully qualified (i.e., include the namespaces).
     * @var Array
     */
    private $typeRegistry;

    /**
     * An array that stores the mappings of an interface to a closure that is used to create and return the concrete object.
     * The key/value pair corresond to the interface name/class name pair.
     * The interface and class names are all fully qualified (i.e., include the namespaces).
     * @var Array
     */
    private $closureRegistry;

}

以上代码可在此处找到: https://github.com/abdulla16/decoupled-app (在/ Container文件夹下)

The above code can be found here: https://github.com/abdulla16/decoupled-app (under the /Container folder)

您可以将依赖项注册为单例,类型(每次实例化一个新对象)或闭包(容器将调用您注册的函数和该函数)

You can register your dependencies as a singleton, as a type (every time a new object will be instantiated), or as a closure (the container will call the function that you register and that function is expected to return the instance).

例如,

$singletonRegistry = array();
$singletonRegistry["DecoupledApp\\Interfaces\\UnitOfWork\\UnitOfWorkInterface"] =
    "\\DecoupledApp\\UnitOfWork\\UnitOfWork";


$typeRegistry = array();
$typeRegistry["DecoupledApp\\Interfaces\\DataModel\\Entities\\UserInterface"] = 
    "\\DecoupledApp\\DataModel\\Entities\\User";

$closureRegistry = array();
$closureRegistry["DecoupledApp\\Interfaces\\DataModel\\Repositories\\UserRepositoryInterface"] = 
    function() {
        global $entityManager;
        return $entityManager->getRepository("\\DecoupledApp\\DataModel\\Entities\\User");
    };

$container = new \DecoupledApp\Container\Container($singletonRegistry, $typeRegistry, $closureRegistry);

此容器解析类的属性以及构造函数参数。

This Container resolves properties of a class as well as the constructor parameters.

这篇关于依赖注入容器PHP的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆