代码之家  ›  专栏  ›  技术社区  ›  Serban Alexandru

依赖注入容器PHP

  •  2
  • Serban Alexandru  · 技术社区  · 11 年前

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

    然而,我仍然不确定如何为依赖项创建容器。之前,我使用一个框架中的容器,我想了解他是如何在后台进行操作并复制它的。

    例如:

    来自Zend 2的容器。我知道容器使类成为动态的,他不必从一开始就知道它们,他检查自己的注册表中是否已经有这个类,如果没有,他检查这个类是否存在,构造函数中有什么参数,并将其放入自己的注册表,以便下次可以从那里获取它,实际情况是,做所有的事情都是动态的,它正在完成自己的注册表,因此,一旦我们实现了容器,我们就不必担心任何事情,因为容器可以作为我们想要的任何类提供,即使我们只是创建了那个类。

    如果我想的话 getInstance 对于A需要B,B需要C,我知道他在做递归,他去实例化C,然后B,最后A。

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

    3 回复  |  直到 11 年前
        1
  •  5
  •   Abdul    11 年前

    您最好使用现有的依赖容器之一,例如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);
    

    此容器解析类的财产以及构造函数参数。

        2
  •  2
  •   Community Mohan Dere    8 年前

    我已经完成了一个非常简单的IoC类,它可以按预期工作。我已经研究了IoC和DI模式,尤其是在阅读之后 this answer 。如果有不对劲或您有任何问题,请告诉我。

    <?php
    
    class Dependency {
     protected $object = null;
     protected $blueprint = null;
    
     /**
      * @param $instance callable The callable passed to the IoC object.
      */
     public function __construct($instance) {
       if (!is_object($instance)) {
         throw new InvalidArgumentException("Received argument should be object.");
       }
    
       $this->blueprint = $instance;
     }
    
     /**
      * (Magic function)
      *
      * This function serves as man-in-the-middle for method calls,
      * the if statement there serves for lazy loading the objects
      * (They get created whenever you call the first method and
      * all later calls use the same instance).
      *
      * This could allow laziest possible object definitions, like
      * adding annotation parsing functionality which can extract everything during
      * the call to the method. once the object is created it can get the annotations
      * for the method, automatically resolve its dependencies and satisfy them,
      * if possible or throw an error.
      *
      * all arguments passed to the method get passed to the method
      * of the actual code dependency.
      *
      * @param $name string The method name to invoke
      * @param $args array The array of arguments which will be passed
      *               to the call of the method
      *
      * @return mixed the result of the called method.
      */
     public function __call($name, $args = array())
     {
       if (is_null($this->object)) {
         $this->object = call_user_func($this->blueprint);
       }
    
       return call_user_func_array(array($this->object, $name), $args);
     }
    }
    
    /*
     * If the object implements \ArrayAccess you could
     * have easier access to the dependencies.
     *
     */
    class IoC {
      protected $immutable = array(); // Holds aliases for write-protected definitions
      protected $container = array(); // Holds all the definitions
    
      /**
       * @param $alias string Alias to access the definition
       * @param $callback callable The calback which constructs the dependency
       * @param $immutable boolean Can the definition be overriden?
       */
      public function register ($alias, $callback, $immutable = false) {
        if (in_array($alias, $this->immutable)) {
          return false;
        }
    
        if ($immutable) {
          $this->immutable[] = $alias;
        }
    
        $this->container[$alias] = new Dependency($callback);
        return $this;
      }
    
      public function get ($alias) {
        if (!array_key_exists($alias, $this->container)) {
          return null;
        }
    
        return $this->container[$alias];
      }
    }
    
    class FooBar {
      public function say()
      {
        return 'I say: ';
      }
    
      public function hello()
      {
        return 'Hello';
      }
    
      public function world()
      {
        return ', World!';
      }
    }
    
    class Baz {
      protected $argument;
    
      public function __construct($argument)
      {
        $this->argument = $argument;
      }
    
      public function working()
      {
        return $this->argument->say() . 'Yep!';
      }
    }
    
    /**
     * Define dependencies
     */
    
    $dic = new IoC;
    $dic->register('greeter', function () {
      return new FooBar();
    });
    
    $dic->register('status', function () use ($dic) {
      return new Baz($dic->get('greeter'));
    });
    
    /**
     * Real Usage
     */
    $greeter = $dic->get('greeter');
    
    print $greeter->say() . ' ' . $greeter->hello() . ' ' . $greeter->world() . PHP_EOL . '<br />';
    
    $status = $dic->get('status');
    print $status->working();
    ?>
    

    我认为代码很简单,但如果有不清楚的地方,请告诉我

        3
  •  1
  •   Badal    9 年前

    因为我没有找到任何接近我想要的东西,所以我尝试自己实现一个容器,我想听听一些关于它看起来怎么样的意见,因为我一个月前就开始学习php和oop了。反馈对我来说非常重要,因为我知道我有很多东西要学习,所以请随意使用我的代码:))

    <!DOCTYPE html>
    <!--
    To change this license header, choose License Headers in Project Properties.
    To change this template file, choose Tools | Templates
    and open the template in the editor.
    -->
    <?php
    
    class ioc
    {
        private $defs;
        static $instance;
        private $reflection;
        private function __construct()
        {
            $defs       = array();
            $reflection = array();
        }
        private function __clone()
        {
            ;
        }
        public static function getInstance()
        {
            if (!self::$instance) {
                self::$instance = new ioc();
            }
            return self::$instance;
        }
        public function getInstanceOf($class)
        {
            if (is_array($this->defs) && key_exists($class, $this->defs)) {
                if (is_object($this->defs[$class])) {
                    return $this->defs[$class];
                }
            } else {
                if (class_exists($class)) {
                    if (is_array($this->reflection) && key_exists($class, $this->reflection)) {
                        $reflection = $this->reflection[$class];
                    } else {
                        $reflection               = new ReflectionClass($class);
                        $this->reflection[$class] = $reflection;
    
                    }
                    $constructor = $reflection->getConstructor();
                    if ($constructor) {
                        $params = $constructor->getParameters();
                        if ($params) {
                            foreach ($params as $param) {
                                $obj[] = $this->getInstanceOf($param->getName());
    
                            }
                            $class_instance = $reflection->newInstanceArgs($obj);
                            $this->register($class, $class_instance);
                            return $class_instance;
                        }
                    }
                    if (!$constructor || !$params) {
                        $class_instance = new $class;
                        $this->register($class, $class_instance);
                        return $class_instance;
                    }
    
                }
            }
        }
        public function register($key, $class)
        {
            $this->defs[$key] = $class;
        }
    
    }
    ?>
    
    推荐文章