PHPフィールドインジェクションその後

Posted: 2014-12-04 01:01 |  PHP全般 
前回の記事からある程度動く様になりまして、
丁度第84回PHP勉強会があったので参加して実装内容をおもしろおかしく発表してきました
 

PHP フィールドインジェクションに挑戦する PHP勉強会2014 from Yuuki Takezawa
まじめそうな内容ですが、所々笑いのエッセンスを混ぜてトークしました
前回から変更してある程度まともになった点を紹介しましょう!
Iono.Container
大分前にも記事を書いたGitHubで使える色んなバッジを付けてみました
テストコードレベルではHHVMでも動くようです

このコンテナライブラリは、Spring Frameworkを模したものですので、
javaのアノテーションというより、結局Springのものをそのまま使いました

ファイルスキャン用のスクリプトもそれなりに用意しまして、
どこのディレクトリにあってもプロジェクト内のファイルを検索してバインディングを定義したファイルを作る様にしました
"bin": [
    "bin/init.container.php"
]
composerでインストールをすると、binにこのコンテナを利用する為の初期ディレクトリを勝手に作る迷惑なスクリプトがいます
コマンドの引数でディレクトリを指定できる様にするのを忘れてました・
これをコマンドラインで実行すると、必要なファイルを自動で作成する様になってます
必要なものは、コンフィグファイルとアノテーションのキャッシュと
コンパイルされたクラスファイルを保存するディレクトリが作成されます
project root
  - resource(キャッシュ、コンパイルファイルなどの置き場)
  - scanner.php(ディレクトリスキャナースクリプト)
初期状態はこれらが作成されるので、実行権限与えるかなにかしないといけません
スキャナーは前述した通りプロジェクトルートを機転に
コンテナに登録するアノテーションが記述されたファイルを探します
削除用のスクリプトとしても動きますので最低限の事はできます
今後は実行前にコンパイルファイルも作る様なものを追加するつもりです

コンフィグファイルは
    // annotation file cache directory, and field injection cache  file only)
    'cache.path' => "/path/to/project/resource",

    // @Component, @Scope annotation scan target directory
    'scan.target.path' => "/path/to/project",

    // doctrine/annotation cache driver("file", "apc", "simple"(no cache))
    'annotation.cache.driver' => 'file',
とプロジェクトの場所に合わせてパス等が自動で反映されます
場所を変更するときは忘れずに変更して下さい

利用するためにコンテナのインスタンスを作る訳ですが、
色々ごちゃごちゃしてます
$compiler = new \Iono\Container\Compiler(
    new \Iono\Container\Annotation\AnnotationManager(),
    $config->set(require dirname(__FILE__) . '/../resource/config.php')
);
$compilerContainer = new \Iono\Container\Container($compiler);
$container = $compilerContainer->register();
アノテーションとコンパイラクラスをコンテナに加えないとフィールドインジェクションが使えません
register()でスキャンして作られたバインディングなどの定義ファイルを読み込みます

ちなみにコンテナに何も与えないでインスタンスを生成すると、
もとのilluminate/containerがそのまま利用できますので、
Pimpleライクな軽量コンテナとして利用できます
Laravelのコンテナなので、サービスロケーター以外にもきちんとDIコンテナとして動作しますので、
コンストラクタインジェクションや色んなものに応用できます

さてさて、あとはアノテーションを使って記述するだけです
定義ファイルを作る為のアノテーションは2種類です

@Component

前回書いた記事のものと同じです
インターフェースとクラスのバインディング等自動で解決したいものに記述します
use Iono\Container\Annotation\Annotations\Component;

/**
 * Class Repository
 * @Component
 */
class Repository implements RepositoryInterface
{
    public function get()
    {

    }
}
これでコンテナに自動で登録されます
RepositoryInterfaceが指示されていたらRepositoryクラスのインスタンスを渡してくれます
もう一つは

@Scope

インスタンスをsingletonで返すかどうかを指示できます
@Componentには名前をつけてクラスを登録する事が出来ますので、
併せて使うとすてきな感じになります
例えばこんな感じです
/**
 * @Component("acme")
 * @Scope("singleton")
 */
class Repository
{
    protected $test;

    /**
     * @param \stdClass $std
     */
    public function __construct(\stdClass $std)
    {
        $this->std = $std;
    }

}
これでこのクラスはコンテナに'acme'という名前で登録されて
以降はサービスロケーターとして利用されます
@Scopeでシングルトン指示がされているので、インスタンスは常にシングルトンになります

取得する場合はコンテナで直接指示すると、
$container->make('acme');
としてインスタンスを受け取る事が出来ます
ですが、このライブラリではそれをフィールドインジェクションで解決します

まずは名前をつけずに、インターフェースとの関連性だけを指示した@Componentを解決する場合

@Autowired

class AcmeClass
{
 
    /**
     * @Autowired("RepositoryInterface")
     */
    protected $class;
 
}
名前をつけた場合

@Value

これは@Resourceに変更するかもしれませんが使い方はこうなります
class Processer
{

    /**
     * @Value('acme')
     */
    protected $hello;
}
DIコンテナとしての機能が元からありますので、
フィールドインジェクションするクラスがコンストラクタインジェクションで指示されていたり、
もちろんその先のクラスでフィールドインジェクションを利用していれば
すべてコンテナで処理を行ってからインスタンスを生成しますので、
難しい事は意識せずに使えるはずですが、
若干まだ動きが怪しい所とパフォーマンス改善できていないパターンもありますので、
油断は禁物!

という感じで動く様になりました

about ytake

執筆に参加しています


Laravel お役立ち情報

share



このエントリーをはてなブックマークに追加

Categories

laravel 45

DTM 0

music 0

PHP全般 31

0

JAPAN 1

WORLD 1

javascript 4

RDBMS 1

NoSQL 1

NewSQL 1

Recent Posts

Ad

comments powered by Disqus

GitHub

Social Links

Author


クリエイティブ・コモンズ・ライセンス
Yuuki Takezawa 作『Ytake Blog』はクリエイティブ・コモンズ 表示 - 非営利 4.0 国際 ライセンス で提供されています。

© ytake/comnect All Rights Reserved. 2014