Reflectionsを使用してPHP5にDIコンテナを実装します

このトピックは、DI(Dependency Injection)が何であるかを想像しているが、「内部でどのように機能するか」を考えたことのない人向けです。

たとえば、 ここまたはここで 、DIとは何かを読むことができます。



目標は、Production DIフレームワークを開発することではありませんでした。 そのような機能を最も便利に実装する方法を考えたかった(上記のPhemtoは 、たとえばMicrosoft Unityのメソッドよりも不便だと思われた)



実装されたオプションはコードで設定されます(他の実装のようにXML経由ではなく、誰かにとっては便利ですが)。

使用する各型は事前に登録する必要がありますが、たとえばPhemtoのように引数をリストする必要はありません-コンテナ自体がReflectionを通じてコン​​ストラクタ引数の型を判別します。





実装自体(以下の例):

<?

class PUnityException extends RuntimeException {



}



class PUnity {



const PUNITY_SINGLETON = 2;

const PUNITY_SIMPLE = 1;



private $data;

private $attributes;

private $singletons;



/**

*

*

* @param string $type

* @param string $concreteInstance

* @param int $attr

*/

public function RegisterType($type, $concreteInstance, $attr = PUnity::PUNITY_SIMPLE) {

// To get exceptions if types are not exists

$typeReflection = new ReflectionClass($type);

$concreteReflection = new ReflectionClass($concreteInstance);



$ this ->data[$type] = $concreteReflection;

$ this ->attributes[$type] = $attr;

}



/**

*

*

* @param string $type

* @return sdtclass

*/

public function Resolve($type) {



if ($ this ->attributes[$type] == PUnity::PUNITY_SINGLETON)

{

$typeReflection = $ this ->data[$type];

try { // May be class is taking care of it's instace by itself?

$getInstance = $typeReflection->getMethod( 'getInstance' ); // Yes, it's a hardcoding...

return $getInstance->invoke( null );

} catch (ReflectionException $e) { }



if (isset($ this ->singletons[$type])) // Try get existing one

return $ this ->singletons[$type];

}



$instance = $ this ->resolver($type); // Resolve type

if ($ this ->attributes[$type] == PUnity::PUNITY_SINGLETON) // Take care of storing the object instance

{

$ this ->singletons[$type] = $instance;

}



return $instance;

}



/**

*

*

* @param string $type

* @return stdclass

*/

private function resolver($type) {

$typeReflection = $ this ->data[$type];

$ctr = $typeReflection->getConstructor();

$args = array();

if ($ctr != null ) // Constructor is defined

{

$ctrParams = $ctr->getParameters();

foreach ($ctrParams as $p) {

$cls = $p->getClass();

if (!isset($ this ->data[$cls->getName()])) // No nothing about needed type

throw new PUnityException( "Type {$cls->getName()} not registered. Use RegisterType first" );

else

array_push($args, $ this ->Resolve($cls->getName()));

}

}

return count($args) ? $typeReflection->newInstanceArgs($args) : $typeReflection->newInstance();

}



}

?>




* This source code was highlighted with Source Code Highlighter .








簡単な使用例:

interface ILogger {

public function Logstr($str);

}



class MyLogger implements ILogger {

public function Logstr($str) {

echo "MyLogger: {$str}" ;

}

}



class UsingLogger {

public function __construct(ILogger $myLogger) {

$myLogger->Logstr( " On the move..." );

}

}



$u = new PUnity();

$u->RegisterType( 'ILogger' , 'MyLogger' );

$u->RegisterType( 'UsingLogger' , 'UsingLogger' );



$logger = $u->Resolve( 'UsingLogger' );




* This source code was highlighted with Source Code Highlighter .








そして、これがシングルトーンの作り方です:

<?php

interface ILogger {

public function Logstr($str);

}



class MyTrickyLogger implements ILogger {



private $timeCreated;



public function MyTrickyLogger() {

$ this ->timeCreated = time();

}



public function Logstr($str) {

echo "I created at " .date( 'dmY H:i:s .' , $ this ->timeCreated). 'Message: ' .$str. "<br/>\n" ;

}

}



class UsingLogger {

public function __construct(ILogger $myLogger) {

$myLogger->Logstr( " On the move..." );

}

}



$u = new PUnity();

$u->RegisterType( 'ILogger' , 'MyTrickyLogger' , PUnity::PUNITY_SINGLETON);

$u->RegisterType( 'UsingLogger' , 'UsingLogger' );



$logger = $u->Resolve( 'UsingLogger' );

sleep(2);

$logger2 = $u->Resolve( 'UsingLogger' );

?>




* This source code was highlighted with Source Code Highlighter .









All Articles