phpで簡単DiContainer
<?php require_once 'Zend/Reflection/Method.php'; /** * * cocktail is ioc container * * @author aileron */ class Cocktail { /** * @param $module * @return Cocktail_Injector */ public static function getInjector(&$module) { if($module instanceof Cocktail_Module) { $binder = new Cocktail_Binder(); $module->configure($binder); return new Cocktail_Injector($binder); } if($module instanceof Cocktail_Binder) { return new Cocktail_Injector($module); } } } /** * @author aileron */ class Cocktail_Binder { /** * @param $name * @return Cocktail_BinderTo */ function __get($name) { return new Cocktail_BinderTo($this,$name); } /** * @var Array */ public $classMap = Array(); /** * @var Array */ public $instanceMap = Array(); } /** * @author aileron */ class Cocktail_BinderTo { /** * @param $name * @param $arg */ function __set($name,$arg) { switch ($name) { case 'to': $this->binder->classMap[$this->name] = $arg; break; case 'toInstance': $this->binder->instanceMap[$this->name] = $arg; break; } } /** * @param $instance */ function toInstance(&$instance) { $this->binder->instanceMap[$this->name] = &$instance; } /** * @param $className * @param $filepath */ function to($className,$filepath=null) { $this->binder->classMap[$this->name] = Array( $className, $filepath ); } /** * @param Cocktail_Binder $self * @param String $name */ function __construct(Cocktail_Binder &$binder,$name) { $this->name = strtolower($name); $this->binder = &$binder; } /** * @var Cocktail_Binder */ private $binder; /** * @var String */ private $name; } /** * @author aileron */ class Cocktail_Injector { /** * @param $name * @return mixed */ function __get($name) { return $this->getInstance($name); } /** * @param $name * @return mixed */ private function getInstance($name) { $name = strtolower($name); $value = @$this->binder->instanceMap[$name]; if(!empty($value)) { return $value ; } $class = @$this->binder->classMap[$name]; if(empty($class)) { throw new Exception("undefined binding [$name]" ); } /* * ファイルパスが指定されていた時に、requireする */ if(!empty($class[1])) { require $class[1]; } $args = Array(); $constructor = null; try { $constructor = new Zend_Reflection_Method($class[0], '__construct'); } catch(Exception $e) { } if(!empty($constructor)) { foreach($constructor->getParameters() as $key => $p) { if (!$docblock = $p->getDeclaringFunction()->getDocblock()) { throw new Exception("none docblok"); } $params = $docblock->getTags('param'); if (!isset($params[$p->getPosition()])) { throw new Exception("none Type"); } $type = $params[$p->getPosition()]->getType(); $args[] = $this->getInstance($type); } $ref = new ReflectionClass($class[0]); $value = $ref->newInstanceArgs($args); } else { $value = new $class[0] ; } $this->binder->instanceMap[$name] = &$value; return $value; } /** * @param $binder */ function __construct(Cocktail_Binder &$binder) { $this->binder =&$binder ; } /** * @var Cocktail_Binder */ private $binder ; } /** * @author aileron */ interface Cocktail_Module { /** * @param Cocktail_Binder $binder * @return void */ function configure(Cocktail_Binder &$binder); }
テストコード
<?php require 'Cocktail.php'; class Db{} interface Count { function count(); } class CountImpl implements Count { private $i=0; /** * @param Db $db */ function __construct(Db &$db) { $db->test = 'test'; } function count(){ return $this->i++; } } class CountModule implements Cocktail_Module { function configure(Cocktail_Binder &$binder) { $binder->Db->to('Db'); $binder->count->to('CountImpl'); } } $injector = Cocktail::getInjector(new CountModule()); $count = $injector->count ; print $count->count() . PHP_EOL ; print $count->count() . PHP_EOL ; print $count->count() . PHP_EOL ; print $count->count() . PHP_EOL ; print $injector->db->test;
リフレクションに、Zend_Reflectionを使用
とりあえず、コンストラクタインジェクションだけサポートしてみた。
あと、適当なイニシャライズメソッドを使った
メソッドインジェクションも対応すれば、okかな。