php - 依赖注入(inject)容器 PHP

标签 php dependency-injection frameworks ioc-container

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

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

例如:

来自 Zend 2 的容器。我知道容器使类动态化,他不必从一开始就知道它们,他检查他的注册表中是否已经有该类,如果没有,他检查是否有类存在以及构造函数内部有哪些参数并将其放入自己的注册表中,以便下次可以从那里获取它,实用的是动态执行所有操作,并且它正在完成自己的注册表,因此一旦我们实现,我们就不必关心任何事情容器,因为他可以提供我们想要的任何类,即使我们只是创建该类。

另外,如果我想为 A 获取实例,而 A 需要 B,B 需要 C,我知道他会递归执行此操作,然后实例化 C,然后实例化 B,最后实例化 A。

所以我了解大局以及他应该做什么,但我不太确定如何实现它。

最佳答案

您可能最好使用现有的依赖项容器之一,例如 PHP-DI 或 Pimple。但是,如果您正在寻找更简单的解决方案,那么我已经实现了一个依赖项容器,作为我在此处编写的文章的一部分: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文件夹下)

您可以将依赖项注册为单例、类型(每次实例化新对象时)或闭包(容器将调用您注册的函数,并且该函数预计返回实例) .

例如,

$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);

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

关于php - 依赖注入(inject)容器 PHP,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24927303/

相关文章:

php - Apple 推送通知服务 APNS - 通知未到达

php - MySQL规范化困境挑战

php - Wordpress 永久链接 GET 和 POST 报告 url 404,而我可以从浏览器访问该 URL

php - Sublime-phpcs - 无法使 PHP MD 和 CS 包正常运行

android - 依赖注入(inject)提供 Activity

java - 好的 Java EE 测试框架?

php - 建立一分钱拍卖网站 : use a php framework or not?

dependency-injection - blazor wasm如何从di注入(inject)自定义类

java - 有没有办法访问之前创建的 Guice 注入(inject)器?

asp.net - 更新 web.config 中的值