2014-07-24 148 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之前瞭解事情是如何工作的,我認爲這種方式在實際框架中更容易處理。 –