HHVM/Hack Dependency Injection/Service Location Container公開

About

Hackで簡単に利用できる Dependency Injection/Service Location Containerライブラリを公開しました。

github.com

Pimpleをはじめとして、
多くのライブラリをそのままHHVM上で動かすことはもちろんできますが、
typescriptやflowといった厳格さを利用できるHackを活かす為、
strictモードで利用できる + psr11 に対応しています。

*ライブラリの名前はtypoではなくわざとです

Headacke

インストール方法

hhvm上で次のコマンドを利用する、composer.jsonに加えるなどしてください

$ hhvm --php $(which composer) require ytake/headacke
"require": {
  "hhvm": ">=3.11.0",
  "ytake/headacke": "~0.0"
},

利用方法

サービスロケータとして任意のものをコンテナに追加することができます

<?hh // strict
$container = new \Headacke\FactoryContainer();
$container->set('testing', $container ==> 'testing');
$container->get('testing'); 

上記の場合は コンテナに'testing' サービスを登録し、文字列の ‘testing’ が返却されます。

簡単ですね

Singleton

当然シングルトンで利用する場合は次のように登録します。

<?hh // strict

$container = new \Headacke\FactoryContainer();
$container->set('scope:singleton', $container ==> new \stdClass(), \Headacke\Scope::SINGLETON);

stdClassをシングルトンで利用する例です。
Scopeを指定してください

内部はHackで簡単に利用できるMemoizeです

<?hh // strict

  <<__Memoize>>
  protected function shared(string $id): mixed
  {
    return call_user_func($this->bindings->at($id), $this);
  }

Prototype

都度新しいインスタンスが欲しい場合は次の通りです。

<?hh // strict

$container = new \Headacke\FactoryContainer();
$container->set('scope:prototype', $container ==> new \stdClass(), \Headacke\Scope::PROTOTYPE);

scopeの指定をしていない場合はデフォルトでprototypeとなります

Dependency Injection

サービスロケータとして使う場合はこれまで紹介したものだけで十分ですが、
やはりアプリケーション内にcontainerが出てくるのは良いとは言えません。

この為、インスタンス生成したいクラスの定義を先に記述することで簡単にDIが利用できます。

parameter registration

<?hh // strict

$container->parameters(
  'string className',
  'parameter name',
  $container ==> 'parameter value'
);

インスタンスを生成したいクラスの引数などを事前に登録します。

lamdaでコンテナ自体にアクセスできますので、サービスロケータに登録したものを利用できます

次のクラスを例にしましょう

<?hh // strict

// Constructor Parameter Promotion 

final class MessageClass {
  public function __construct(protected string $message) {
  }
  public function message(): string {
    return $this->message;
  }
}

final class MessageClient {
  public function __construct(protected MessageClass $message) {

  }
  public function message(): MessageClass {
    return $this->message;
  }
}

MessageClientクラスのコンストラクタにMessageClassがコンストラクタで指定されています。

これをコンテナ経由で取得する場合は次のように記述できます。

<?hh // strict

$container = new \Headacke\FactoryContainer();
$container->set('message.class', $container ==> new MessageClass('testing'));
$container->parameters(
  MessageClient::class, 
  'message', 
  $container ==> $container->get('message.class')
);
$instance = $container->get(MessageClient::class);

コンテナに'message.class'サービスとして MessageClass を登録します
MessageClientクラスの引数として、'message.class' を利用するにように指定します。
最後にコンテナからインスタンス生成を行います。

簡単に利用できるようになっていますが、
残念ながらlaravelやleague/containerのようにauto wiringのサポートはしていません。
(getしたらそれに関連するクラスも全て依存解決する機能)
あくまで取得したいものは事前に定義しなければ動作しません!

Hackで開発をしたい方のヒントになればと思います。