Zend Framework 2:サービスマネージャー

ZF2のサービスマネージャー(SM、CM)。



Service ManagerはZend Framework 2の重要なコンポーネントの1つであり、サービスの作成と構成のためのコードの重複や日常的な操作を排除し、最高レベルで構成できるため、開発者の生活を大幅に簡素化します。 SMは本来、サービスのレジストリであり、その主なタスクはサービスの作成と保存です。 CMは、Zend Framework 1のZend_Registryコンポーネントの非常に高度なバージョンであると言えます。

CMはService Locatorパターンを実装します。 アプリケーションの多くの部分(AbstractActionControllerなど)で、Zend \ ServiceManager \ ServiceManagerクラスを返すgetServiceLocator()関数を見つけることができます。 メソッド名と戻り値の型の間のこの矛盾は、getServiceLocator()がServiceLocatorInterfaceインターフェースを実装するオブジェクトを返すという事実によって簡単に説明されます。



namespace Zend\ServiceManager; interface ServiceLocatorInterface { public function get($name); public function has($name); }
      
      





Zend \ ServiceManager \ ServiceManagerそのまま。 これは、フレームワーク自体が他のいくつかのタイプのSMを使用するためです。ところで、アプリケーションで独自のサービスマネージャーを使用することを禁止する人はいません。



サービス



サービスは、絶対に任意の型の通常の変数です(オブジェクトである必要はありません。Zend_Registryとの比較を参照)。



 // IndexController::indexAction() $arrayService = array('a' => 'b'); $this->getServiceLocator()->setService('arrayService', $arrayService); $retrievedService = $this->getServiceLocator()->get('arrayService'); var_dump($retrievedService); exit;
      
      





出力されます:

配列(
     a => 'b'
 )


CM構成



サービス管理を構成するには、4つの方法があります。

1.モジュール構成(module.config.php)を介して:

 return array( 'service_manager' => array( 'invokables' => array(), 'services' => array(), 'factories' => array(), 'abstract_factories' => array(), 'initializators' => array(), 'delegators' => array(), 'shared' => array(), 'aliases' => array() ) );
      
      





2. getServiceConfig()メソッドを定義します(コードの美しさのために、Zend \ ModuleManager \ Feature \ ServiceProviderInterfaceインターフェースを追加することもできます)。これは、ステップ1のフォーマットで配列またはTraversableを返します。



3.サービスを手動で作成し、CMに挿入します。

 // IndexController::indexAction() $arrayService = array('a' => 'b'); $this->getServiceLocator()->setService('arrayService', $arrayService);
      
      





4. application.config.phpの条項1の形式でサービスを説明する



サービスの名前はアプリケーション全体で一意でなければならないことに注意してください(もちろん、既存のサービスをオーバーライドすることが目的でない限り)。 アプリケーションの初期化中に、Zend \ ModuleManager \ ModuleManagerはすべての設定を1つにマージし、重複キーを上書きします。 サービスの名前に名前空間モジュールを追加することをお勧めします。 または、サービスクラスの絶対名を使用します。



SMを介したサービスの作成。



オブジェクト\単純型


最も単純なタイプ。 このようなサービスを作成するには、オブジェクト(配列、文字列、リソースなど)を手動で作成し、CMに転送するだけです。

 $myService = new MyService(); $serviceManager->setService('myService', $myService);
      
      





構成を通じて:

 array( 'service_manager' => array( 'services' => array( 'myService' => new MyService() ) ) );
      
      





$ serviceManager-> setService($ name、$ service)は、初期化されたすべてのサービスを格納する内部変数ServiceManager :: $インスタンスにオブジェクトを直接配置します。 このタイプにアクセスするとき、SMはそれを作成しようとせず、そのまま提供します

このタイプを使用すると、アプリケーション全体で使用できる任意のデータを保存できます(Zend_Registryの場合と同様)。



呼び出し可能


作成するには、ターゲットクラスの完全な名前をマネージャーに転送する必要があります。 CMは、 新しい演算子を使用して作成します。



 // ServiceManager::createFromInvokable() protected function createFromInvokable($canonicalName, $requestedName) { $invokable = $this->invokableClasses[$canonicalName]; if (!class_exists($invokable)) { // cut } $instance = new $invokable; return $instance; }
      
      





 $myService = new MyService(); $serviceManager->setInvokableClass('myService', $myService);
      
      





構成を通じて:

 array( 'service_manager' => array( 'invokables' => array( 'myService' => 'MyService' ) ) );
      
      





アプリケーション:CMを介した直接の依存関係のないクラスを作成する必要がある場合。

同時に、委任者とインスタンス生成者が呼び出され、必要に応じて依存関係を実装します。



工場。


サービスは、工場で作成および構成できます。 ファクトリには、クロージャーとZend \ ServiceManager \ FactoryInterfaceを実装するクラスの2つのタイプがあります。



閉鎖による実装:

 array( 'service_manager' => array( 'factories' => array( 'myService' => function (ServiceLocator $serviceManager) { return new MyService(); } ) ) );
      
      





この方法はコードの行数を減らしますが、落とし穴があります:クロージャーを文字列に正しくシリアル化できない。

実際の例:application.config.phpで結合された構成のキャッシュを有効にした場合、次にアプリケーションを起動すると、コンパイルできず、エラーでクラッシュします: 致命的なエラー:未定義メソッドClosure :: __ set_state()in / data / cache / module- config-cache..php



このような問題を回避するには、Zend \ ServiceManager \ FactoryInterfaceを実装するファクトリクラスを介してサービスを作成する必要があります。

 // Appliction/Service/ConfigProviderFactory.php class ConfigProviderFactory implements FactoryInterface { public function createService(ServiceLocatorInterface $serviceLocator) { return new ConfigProvider($serviceLocator->get('Configuration')); } }
      
      





構成に登録されています:

 array( 'service_manager' => array( 'factories' => array( 'ConfigProvider' => 'ConfigEx\Service\ConfigProviderFactory', ) ) );
      
      





また、ファクトリオブジェクトまたはクラス名をCMに直接転送できます。



 $serviceManager->setFactory('ConfigProvider', new ConfigEx\Service\ConfigProviderFactory());
      
      





アプリケーション:他のサービスに依存する、または構成する必要があるサービスを作成する必要がある場合。



抽象工場


AFは、要求されたサービスを作成するCMの最後の試みです。 CMがサービスを見つけられない場合、登録されたすべてのAFのポーリングを開始します(canCreateServiceWithName()メソッドを呼び出します)。 AFが肯定的な回答を返す場合、SMはファクトリからcreateServiceWithName()メソッドを呼び出し、サービスの作成をAFロジックに委任します。



ダイレクトAF転送:

 $serviceManager->addAbstractFactory(new AbstractFactory);
      
      





addAbstractFactoryは、クラスではなくオブジェクトを受け入れます!

構成による設定:

 array( 'service_manager' => array( 'abstract_factories' => array( 'DbTableAbstractFactory' => 'Application\Service\'DbTableAbstractFactory' ) ),
      
      





そしてファクトリークラス:

 class DbTableAbstractFactory implements \Zend\ServiceManager\AbstractFactoryInterface { public function canCreateServiceWithName(\Zend\ServiceManager\ServiceLocatorInterface $serviceLocator, $name, $requestedName) { return preg_match('/Table$/', $name); } public function createServiceWithName(\Zend\ServiceManager\ServiceLocatorInterface $serviceLocator, $name, $requestedName) { $table = new $name($serviceLocator->get('DbAdapter')); } }
      
      





次に、SMに2つのサービスを作成するように依頼できます。

 $serviceManager->get('UserTable'); $serviceManager->get('PostTable');
      
      





その結果、どのタイプのサービスにも記述されていない2つのオブジェクトが存在します。

これは非常に便利なことです。 しかし、私の意見では、この振る舞いは他の開発者にとってあまり予測できないため、賢明に使用する必要があります。 何からオブジェクトを作成する魔法のデバッグに多くの時間を費やしたいのですか?



エイリアス



これらは、他のサービスの単なるエイリアスです。

 array( 'service_manager' => array( 'aliases' => array( 'myservice' => 'MyService' ) ) ); $serviceLocator->get('myservice') === $serviceLocator->get('MyService'); // true
      
      





それでは、他のスナックに移りましょう。



初期化子。



これらはもはやサービスではなく、SM自体の機能です。 オブジェクトが作成された後、サービスの追加の初期化が可能になります。 彼らの助けを借りて、インターフェイスインジェクションを実装できます。

したがって、SMが新しいオブジェクトを作成した後、登録されたすべての初期化子を繰り返し処理し、最後の構成ステップのオブジェクトを渡します。



それらは工場のように同様の方法で登録されます:

回路を通して:

 array( 'service_manager' => array( 'initializers' => array( 'DbAdapterAwareInterface' => function ($instance, ServiceLocator $serviceLocator) { if ($instance instanceof DbAdapterAwareInterface) { $instance->setDbAdapter($serviceLocator->get('DbAdapter')); } } ) ) );
      
      





クラスを通して:

 class DbAdapterAwareInterface implements \Zend\ServiceManager\InitializerInterface { public function initialize($instance, \Zend\ServiceManager\ServiceLocatorInterface $serviceLocator) { if ($instance instanceof DbAdapterAwareInterface) { $instance->setDbAdapter($serviceLocator->get('DbAdapter')); } } } array( 'service_manager' => array( 'initializers' => array( 'DbAdapterAwareInterface' => 'DbAdapterAwareInterface' ) ) );
      
      





この例では、インターフェイスインジェクションを実装します。 $インスタンスのタイプがDbAdapterAwareInterfaceの場合、初期化子はデータベースアダプターをオブジェクトに渡します。



アプリケーション:インターフェイスインジェクション、オブジェクトチューニング。

CMは、作成されたオブジェクトごとにすべての初期化子を呼び出すため、パフォーマンスが低下する可能性があることを知っておくことが重要です。



委任者。


デリゲートは初期化子に似ていますが、唯一の違いは、列の全員ではなく特定のサービスに対して呼び出されることです。



登録:

 array( 'service_manager' => array( 'delegators' => array( 'Router' => array( 'AnnotatedRouter\Delegator\RouterDelegatorFactory' ) ) ) );
      
      





そして実装:

 class RouterDelegatorFactory implements DelegatorFactoryInterface { public function createDelegatorWithName(ServiceLocatorInterface $serviceLocator, $name, $requestedName, $callback) { //   ,     ,    ,  $callback  . $service = $callback(); //   ,   $service->doSomeCoolStuff(); // ,     //    return $service; } }
      
      





この例では、RouterDelegatorFactoryデリゲーターはルートサービスにのみ適用されます。



アプリケーション:オブジェクトの追加構成。サードパーティモジュールからサービスを再構成するのに役立ちます。 たとえば、アノテーション経由でルーティングするためのモジュールでは、デリゲーターを使用してルートを標準ルーターに追加しました。 Module.phpにEVENT_ROUTEサブスクライバーを標準リスナーよりも高い優先度で登録するオプションがありました。 しかし、それは何とか汚れているように見えます...



共有サービス。



デフォルトでは、SMはオブジェクトのインスタンスを1つだけ作成し、その後の呼び出しごとに同じオブジェクトが返されます(シングルトンなど)。 この動作をグローバルに禁止するには、setShareByDefault(false)メソッドを呼び出す必要があります。 設定を使用して、特定のサービスのこの動作を無効にすることもできます。

 array( 'service_manager' => array( 'shared' => array( 'MyService' => false ) ) ); $a = $serviceManager->get('MyService'); $b = $serviceManager->get('MyService'); spl_object_hash($a) === spl_object_hash($b); // false
      
      






All Articles