注意!
Pimple は現在変更を受け付けていません。新しい機能は追加されず、外観上の変更も受け入れられません。受け入れられる変更は、新しい PHP バージョンとの互換性とセキュリティ問題の修正のみです。
注意!
これは Pimple 3.x のドキュメントです。 Pimple 1.x を使用している場合は、Pimple 1.x のドキュメントをお読みください。 Pimple 1.x コードを読むことは、単純な依存関係注入コンテナーの作成方法をさらに学ぶ良い方法です (Pimple の最近のバージョンはパフォーマンスに重点を置いています)。
Pimple は、PHP 用の小さな依存関係注入コンテナーです。
プロジェクトで Pimple を使用する前に、 composer.json
ファイルに Pimple を追加します。
$ ./composer.phar require pimple/pimple " ^3.0 "
コンテナを作成するには、 Container
インスタンスを作成します。
use Pimple Container ;
$ container = new Container ();
他の多くの依存関係注入コンテナーと同様に、Pimple はサービスとパラメーターという 2 つの異なる種類のデータを管理します。
サービスは、より大きなシステムの一部として何かを実行するオブジェクトです。サービスの例: データベース接続、テンプレート エンジン、またはメーラー。ほとんどすべてのグローバルオブジェクトをサービスにすることができます。
サービスは、オブジェクトのインスタンスを返す匿名関数によって定義されます。
// define some services
$ container [ ' session_storage ' ] = fn ( $ c ) => new SessionStorage ( ' SESSION_ID ' );
$ container [ ' session ' ] = fn ( $ c ) => new Session ( $ c [ ' session_storage ' ]);
匿名関数は現在のコンテナ インスタンスにアクセスできるため、他のサービスやパラメータへの参照が可能であることに注意してください。
オブジェクトは取得時にのみ作成されるため、定義の順序は重要ではありません。
定義されたサービスの使用も非常に簡単です。
// get the session object
$ session = $ container [ ' session ' ];
// the above call is roughly equivalent to the following code:
// $storage = new SessionStorage('SESSION_ID');
// $session = new Session($storage);
デフォルトでは、サービスを取得するたびに、Pimple はその同じインスタンスを返します。すべての呼び出しに対して異なるインスタンスが返されるようにしたい場合は、匿名関数をfactory()
メソッドでラップします。
$ container [ ' session ' ] = $ container -> factory ( fn ( $ c ) => new Session ( $ c [ ' session_storage ' ]));
これで、 $container['session']
を呼び出すたびにセッションの新しいインスタンスが返されるようになりました。
パラメーターを定義すると、外部からのコンテナーの構成が容易になり、グローバル値を保存できるようになります。
// define some parameters
$ container [ ' cookie_name ' ] = ' SESSION_ID ' ;
$ container [ ' session_storage_class ' ] = ' SessionStorage ' ;
session_storage
サービス定義を次のように変更すると、次のようになります。
$ container [ ' session_storage ' ] = fn ( $ c ) => new $ c [ ' session_storage_class ' ]( $ c [ ' cookie_name ' ]);
サービス定義を再定義する代わりに、 cookie_name
パラメーターをオーバーライドすることで、Cookie 名を簡単に変更できるようになりました。
Pimple は匿名関数をサービス定義として認識するため、匿名関数をprotect()
メソッドでラップしてパラメータとして保存する必要があります。
$ container [ ' random_func ' ] = $ container -> protect ( fn () => rand ());
場合によっては、サービス定義を定義した後に変更することが必要になる場合があります。 extend()
メソッドを使用して、サービスの作成直後にサービス上で実行される追加のコードを定義できます。
$ container [ ' session_storage ' ] = fn ( $ c ) => new $ c [ ' session_storage_class ' ]( $ c [ ' cookie_name ' ]);
$ container -> extend ( ' session_storage ' , function ( $ storage , $ c ) {
$ storage ->. . .();
return $ storage ;
});
最初の引数は拡張するサービスの名前で、2 番目の引数はオブジェクト インスタンスとコンテナにアクセスする関数です。
同じライブラリを何度も使用する場合は、あるプロジェクトから次のプロジェクトに一部のサービスを再利用することができます。 PimpleServiceProviderInterface
を実装して、サービスをプロバイダーにパッケージ化します。
use Pimple Container ;
class FooProvider implements Pimple ServiceProviderInterface
{
public function register ( Container $ pimple )
{
// register some services and parameters
// on $pimple
}
}
次に、プロバイダーをコンテナーに登録します。
$ pimple -> register ( new FooProvider ());
オブジェクトにアクセスすると、Pimple は定義した匿名関数を自動的に呼び出し、サービス オブジェクトを作成します。この関数に生でアクセスしたい場合は、 raw()
メソッドを使用できます。
$ container [ ' session ' ] = fn ( $ c ) => new Session ( $ c [ ' session_storage ' ]);
$ sessionFunction = $ container -> raw ( ' session ' );
歴史的な理由により、 Container
クラスは PSR-11 ContainerInterface
を実装しません。ただし、Pimple には、Pimple コンテナ クラスからコードを分離できるヘルパー クラスが用意されています。
PimplePsr11Container
クラスを使用すると、 PsrContainerContainerInterface
メソッドを使用して、基になる Pimple コンテナーのコンテンツにアクセスできます。
use Pimple Container ;
use Pimple Psr11 Container as PsrContainer ;
$ container = new Container ();
$ container [ ' service ' ] = fn ( $ c ) => new Service ();
$ psr11 = new PsrContainer ( $ container );
$ controller = function ( PsrContainer $ container ) {
$ service = $ container -> get ( ' service ' );
};
$ controller ( $ psr11 );
場合によっては、すべてのサービスが実際に使用されるかどうかを確認せずに、サービスが他のいくつかのサービスにアクセスする必要があることがあります。このような場合、サービスのインスタンス化を遅延させたい場合があります。
従来の解決策は、サービス コンテナ全体を注入して、本当に必要なサービスのみを取得することです。ただし、これはお勧めできません。サービスにアプリケーションの残りの部分へのアクセスが広すぎるため、サービスの実際の依存関係が隠蔽されてしまいます。
ServiceLocator
、実際に必要な場合にのみインスタンス化しながら、事前定義されたサービスのセットへのアクセスを提供することで、この問題を解決することを目的としています。
また、サービスの登録に使用した名前とは別の名前でサービスを利用できるようにすることもできます。たとえば、イベント ディスパッチャーがdispatcher
という名前で登録されているときに、 EventDispatcherInterface
のインスタンスがevent_dispatcher
という名前で利用可能であることを期待するオブジェクトを使用することができます。
use Monolog Logger ;
use Pimple Psr11 ServiceLocator ;
use Psr Container ContainerInterface ;
use Symfony Component EventDispatcher EventDispatcher ;
class MyService
{
/**
* "logger" must be an instance of PsrLogLoggerInterface
* "event_dispatcher" must be an instance of SymfonyComponentEventDispatcherEventDispatcherInterface
*/
private $ services ;
public function __construct ( ContainerInterface $ services )
{
$ this -> services = $ services ;
}
}
$ container [ ' logger ' ] = fn ( $ c ) => new Monolog Logger ();
$ container [ ' dispatcher ' ] = fn ( $ c ) => new EventDispatcher ();
$ container [ ' service ' ] = function ( $ c ) {
$ locator = new ServiceLocator ( $ c , array ( ' logger ' , ' event_dispatcher ' => ' dispatcher ' ));
return new MyService ( $ locator );
};
サービス インスタンスのコレクションを配列で渡すことは、コレクションを使用するクラスが、後の段階でそのメソッドの 1 つが呼び出されたときにそのコレクションを反復処理するだけでよい場合、非効率であることが判明する可能性があります。また、コレクションに格納されているサービスの 1 つとそれを使用するクラスの間に循環依存関係がある場合にも問題が発生する可能性があります。
ServiceIterator
クラスは、これらの問題の解決に役立ちます。インスタンス化中にサービス名のリストを受け取り、反復処理時にサービスを取得します。
use Pimple Container ;
use Pimple ServiceIterator ;
class AuthorizationService
{
private $ voters ;
public function __construct ( $ voters )
{
$ this -> voters = $ voters ;
}
public function canAccess ( $ resource )
{
foreach ( $ this -> voters as $ voter ) {
if ( true === $ voter -> canAccess ( $ resource )) {
return true ;
}
}
return false ;
}
}
$ container = new Container ();
$ container [ ' voter1 ' ] = fn ( $ c ) => new SomeVoter ();
$ container [ ' voter2 ' ] = fn ( $ c ) => new SomeOtherVoter ( $ c [ ' auth ' ]);
$ container [ ' auth ' ] = fn ( $ c ) => new AuthorizationService ( new ServiceIterator ( $ c , array ( ' voter1 ' , ' voter2 ' ));