2014-07-24 150 views
2

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

不过,我仍然不确定如何创建我的容器的依赖关系。之前,我使用框架中的容器,并且想知道他是如何在后面做事情并重现它的。

例如:

从Zend的2.I容器了解到,容器类作动,他不必知道他们开始时,他会检查,如果他已经有他的注册表中类和如果他没有,他是否是类存在,并有内部的构造什么参数,并把它放在自己的注册表,以便下一次可以把它从那里,实际做的一切动态,并完成了自己的注册表,所以我们不”即使我们只是做这个班,我们一旦实施了集装箱,他就可以像任何我们想要的班级一样放弃。

另外,如果我想的GetInstance对于A至极需要B和B需要CI明白,他这样做递归和他去和实例C,那么B和最终A.

所以我理解大局,什么是他打算做,但我不知道如何实现它。

+0

香港专业教育学院一直在你的地方,我会建议你在看向[疙瘩DI(http://pimple.sensiolabs.org/)它是一个单独的类(据我记得),它是相对简单。看看它,了解它是如何工作的。这应该有助于你旋转DIC –

+0

你自己的变化你可以看看我的第一个答案在这里,是我前几天提出的解决方案,但我没有发表任何意见,但我仍然不会不知道是否有什么好处,如果你有时间看到它,我会非常想听到你对此的看法。谢谢! –

+0

我注意到的第一件事情之一是你正在大量使用反射,你能解释一下为什么? (虽然我没有深究) –

回答

4

您可以使用现有的依赖容器之一在那里,如PHP-DI会更好或疙瘩。不过,如果你正在寻找一个简单的解决方案,那么我已经实现了一个依赖容器作为一篇文章,我写在这里的一部分: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(在/容器文件夹下)

你可以注册你的依赖作为一个单身,作为一个类型(每次一个新的对象将被实例化),或作为封闭(容器将调用你注册的函数和该函数应该返回实例)。

例如,

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

此容器解决了一个类的属性以及构造参数。

1

因为我找不到接近我想要的东西,我想我自己的容器来实现,我希望听到有关如何在寻找一些意见,因为我已经开始学习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; 
    } 

} 
?> 
1

我做了一个非常简单的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(); 
?> 

我认为的代码是不言自明的,但让我知道如果事情是不明确

+0

我明白这个功能,我的第一种方法就是这样的,因为在这里读了一些东西,但是我想让我的东西有点动听,因为我不想必须告诉国际奥委会每一个阶级,我可能会希望他,我想问他一个阶级,他给我没有告诉他有关它之前。 –

+0

此外,您还需要告诉对象在注册时所具有的所有依赖关系,这是DIC的作用,您不必知道他拥有的所有依赖关系,在大型应用程序中它可以拥有20,30个依赖关系不得不说$ dic-> register('status',function()使用($ dic)返回新的Baz($ dic-> get('greeter'),dep2,dep3,dep4,.....) ; });我不这样做,我希望我可以像框架那样做并且有一些表现来做同样的事情。 –

+0

我知道我无法获得与创建框架的人相同的方法,因为他们会考虑很多时间,这就是为什么我问这里是否有人知道pro方法,框架在后面使用的方法,还告诉我是否我接近它,因为我想在框架中使用DIC之前了解事情是如何工作的,我认为这种方式在实际框架中更容易处理。 –