Você pode imaginar a sua aplicação como um conjunto de serviços(services). Por exemplo, você pode ter um serviço de autenticação responsável por fazer login os usuários do site, um serviço gerenciador de entity por acessar o banco de dados, service manager de eventos responsável por acionar eventos e entregá-los aos event listeners, etc.
No Zend Framework, a classe ServiceManager
é um container centralizado para todos
serviços da aplicação. O service maanger é incorporado ao componente Zend\ServiceManager
como a classe ServiceManager
. O diagrama de herança de classes é mostrado na imagem 3.5 logo abaixo:
O service manager é criado na inicialização do aplicativo (dentro do init()
)
método estático da classe Zend\Mvc\Application
).
Os serviços padrão disponíveis através do service manager são apresentados na tabela 3.1.
Esta tabela está incompleta, porque o número real de serviços registrados no gerenciador de serviços
pode ser muito maior.
Nome do Serviço | Descrição |
---|---|
Application |
Permite acessar o singleton da classe Zend\Mvc\Application . |
ApplicationConfig |
Array de configuração extraída do arquivo application.config.php. |
Config |
Array de configuração Incorporado do module.config.php mesclado com autoload/global.php e autoload/local.php . |
EventManager |
Permite uma nova instância da classe Zend\EventManager\EventManager . O event manager permite enviar eventos (triggers) e anexar event listeners. |
SharedEventManager |
Permite instânciar singleton da classe Zend\EventManager\SharedEventManager O Shared Event Manager permite usar event listeners definidos por outras classes e componentes. |
ModuleManager |
Permite acessar o singleton da classe Zend\ModuleManager\ModuleManager . module manager é responsável por carregar os módulos do aplicativo.. |
Request |
O singleton da classe Zend\Http\Request . Representa a solicitação HTTP recebida do cliente. |
Response |
O singleton da classe Zend\Http\Response . Representa a resposta HTTP que será enviada ao cliente. |
Router |
O singleton de Zend\Router\Http\TreeRouteStack . Executa o roteamento de URL. |
ServiceManager |
O Próprio Service manager em si. |
ViewManager |
O singleton da classe Zend\Mvc\View\Http\ViewManager . Responsável por preparar o view da página |
Um serviço é tipicamente uma classe PHP arbitrária, mas nem sempre. Por exemplo, quando ZF3
carrega os arquivos de configuração e incorpora os dados em arrays, salva as arrays
no service manager como serviços (!): ApplicationConfig
e Config
.
O primeiro é o array carregado a partir do arquivo de configuração em nível de aplicativo application.config.php,
e a mais recente é a matriz mesclada dos arquivos de configuração no nível do módulo e carregada automaticamente
arquivos de configuração em nível de aplicativo. Assim, no gerenciador de serviços, você pode armazenar qualquer coisa
você quer: uma classe PHP, uma variável simples ou um array.
Na tabela 3.1, você pode ver que no ZF3 quase tudo pode ser considerado como um serviço. O service manager
é registrado como um serviço. Além disso, a classe Application
é também
registrado como um serviço.
Uma coisa importante que você deve observar sobre os serviços é que eles são tipicamente rmazenado em uma única instância única (isso também é chamado o padrão singleton). Obviamente, você não precisa da segunda instância da classe @ Application (nesse caso você Eu teria um pesadelo.
Mas existe uma exceção importante da regra acima. Pode ser confuso no início, mas o
EventManager
ão é um singleton. Toda vez que você recupera o service manager do event manager, você recebe um objeto novo(new). Isso é feito por motivos de desempenho e para evitar possíveis conflitos de eventos entre componentes diferentes. Discutiremos isso mais adiante na seção Sobre o Event Manager mais adiante neste capítulo.
O service manager define vários métodos necessários para localizar e recuperar um serviço do service manager (veja a tabela 3.2 abaixo).
Nome do Método | Descrição |
---|---|
has($name) |
Verifica se esse serviço está registrado. |
get($name) |
Recupera a instância de um serviço registrado. |
build($name, $options) |
Sempre retorna uma nova instância do serviço solicitado. |
Você pode testar se um serviço está registrado, passando seu nome para o service manager's.
através do método has()
. Ele retorna um booleano true
se o serviço estiver registrado, ou
false
se o serviço com esse nome não estiver registrado.
Você pode recuperar um serviço pelo seu nome mais tarde com a ajuda do método get()
do service manager's.
Esse método usa um único parâmetro que representa o nome do serviço. Veja o exemplo:
<?php
// Retrieve the application config array.
$appConfig = $serviceManager->get('ApplicationConfig');
// Use it (for example, retrieve the module list).
$modules = $appConfig['modules'];
E o método build()
sempre cria uma nova instância do serviço quando você o chama (comparando com get()
, que
normalmente cria a instância do serviço apenas uma vez e a retorna em solicitações posteriores).
Você normalmente recuperará serviços do service manager não em qualquer lugar do seu código, mas dentro de um factory. Uma factory é um código responsável pela criação de um objeto. Ao criar o objeto, você pode recuperar serviços dos quais depende o service manager e passar esses serviços (dependências) para o construtor (constructor) do objeto. Isso também é chamado de injeção de dependência(dependency injection).
Se você tiver alguma experiência com o Zend Framework 2, poderá perceber que as coisas agora estão um pouco diferentes do que antes. No ZF2, havia o padrão
ServiceLocator
que permitia obter dependências do gerenciador de serviços em qualquer parte da sua aplicação (em controllers, services, etc.) No ZF3, você tem que passar dependências explicitamente. É um pouco mais chato, mas remove as dependências "ocultas" e torna seu código mais claro e fácil de entender.
Ao escrever seu site, você frequentemente precisará registrar seu próprio serviço
no service manager. Uma das maneiras de registrar um serviço é usando o método setService()
do service manager.
Por exemplo, vamos criar e registrar a classe de serviço de conversor de moeda, que
será usado, por exemplo, em uma página do carrinho de compras para converter moeda EUR em USD
<?php
// Define a namespace where our custom service lives.
namespace Application\Service;
// Define a currency converter service class.
class CurrencyConverter
{
// Converts euros to US dollars.
public function convertEURtoUSD($amount)
{
return $amount*1.25;
}
//...
}
Acima, nas linhas 6-15, definimos um exemplo da classe CurrencyConverter
(para simplificar, implementamos
apenas um único método convertEURtoUSD()
que é capaz de converter euros em dólares americanos).
// Create an instance of the class.
$service = new CurrencyConverter();
// Save the instance to service manager.
$serviceManager->setService(CurrencyConverter::class, $service);
No exemplo acima, instanciamos a classe com o operador new
, e registramos
com o service manager com o o método setService()
(assumimos que a variável $serviceManager
é da classe type Zend\ServiceManager\ServiceManager
, e que foi declarado em algum outro lugar).
O método setService()
usa dois parâmetros: o nome do serviço e a instância do serviço.
O nome do serviço deve ser exclusivo em todos os outros serviços possíveis.
Depois que o serviço é armazenado no service manager, você pode recuperá-lo pelo nome em qualquer
aplicação com a ajuda do método get()
do service manager. Olhe para o seguinte
exemplo:
<?php
// Retrieve the currency converter service.
$service = $serviceManager->get(CurrencyConverter::class);
// Use it (convert money amount).
$convertedAmount = $service->convertEURtoUSD(50);
Serviços diferentes podem usar diferentes estilos de nomenclatura. Por exemplo, o mesmo serviço de conversor de moeda
pode ser registrado sob os diferentes nomes: CurrencyConverter
, currency_converter
e assim por diante. Para introduzir alguma convenção de nomenclatura uniforme, recomenda-se registrar um serviço
seu nome de classe totalmente, como podemos ver abaixo:
$serviceManager->setService(CurrencyConverter::class);
No exemplo acima, usamos a palavra-chave class
. Está disponível desde o PHP 5.5 e é usado para classe
resolução de nomes. CurrencyConverter::class
é expandido para o nome completo da classe,
como \Application\Service\CurrencyConverter
.
Se você está tentando registrar o nome do serviço que já está presente, no setService()
método exibirá um errro. Mas às vezes
você deseja substituir o serviço com o mesmo nome (para substituí-lo por um novo). Para este propósito,
você pode usar o método setAllowOverride()
do service manager:
<?php
// Allow to replace services
$serviceManager->setAllowOverride(true);
// Save the instance to service manager. There will be no exception
// even if there is another service with such a name.
$serviceManager->setService(CurrencyConverter::class, $service);
Acima, o método setAllowOverride()
apenas aceita um único parâmetro booleano que define se
para permitir que você substitua o serviço "CurrencyConverter" se tal nome já estiver presente, ou não.
O que é ruim com o método setService()
é que você tem que criar a instância do serviço
antes que você realmente precise. Se você nunca usar o serviço, a instanciação do serviço será
desperdice o tempo e a memória. Para resolver esse problema, o service manager fornece a você
método setInvokableClass()
.
<?php
// Register an invokable class
$serviceManager->setInvokableClass(CurrencyConverter::class);
No exemplo acima, passamos para o service manager o nome completo da classe do
o serviço em vez de passar sua instância. Com esta técnica, o serviço
será instanciado pelo service manager somente quando alguém chamar o método get(CurrencyConverter::class)
.
Isso também é chamado de lazy loading.
Os serviços geralmente dependem uns dos outros. Por exemplo, o serviço de conversor de moeda pode usar o serviço gentity manager para ler as taxas de câmbio do banco de dados. A desvantagem do método
setInvokableClass()
é que ele não permite passar parâmetros (dependências) para o serviço na instanciação de objetos. Para resolver esse problema, você pode usar factories, conforme descrito abaixo.
A factory é uma classe que pode fazer apenas uma coisa - para criar outros objetos.
Você registra uma factory para um serviço com o método setFactory()
do service manager:
A fábrica mais simples é InvokableFactory
é análoga ao método da seção anterior setInvokableClass()
.
<?php
use Zend\ServiceManager\Factory\InvokableFactory;
// This is equivalent to the setInvokableClass() method from previous section.
$serviceManager->setFactory(CurrencyConverter::class, InvokableFactory::class);
Depois de ter registrado a fábrica, você pode recuperar o serviço do service manager como de costume com o método get()
. O serviço
será instanciado somente quando você recuperá-lo do service manager (lazy loading).
Às vezes, a instanciação de serviços é mais complexa do que apenas criar a instância de serviço
com o operador new
(como InvokableFactory
faz). Você pode precisar passar alguns parâmetros para o construtor do serviço ou
Invoque alguns métodos de serviço logo após a construção. Esta lógica de instanciação complexa
pode ser encapsulado dentro de sua própria classe factory.
A classe de fábrica geralmente implementa o FactoryInterface
[Zend\ServiceManager\Factory\ FactoryInterface]:
<?php
namespace Zend\ServiceManager\Factory;
use Interop\Container\ContainerInterface;
interface FactoryInterface
{
public function __invoke(ContainerInterface $container,
$requestedName, array $options = null);
}
Como vemos na definição do FactoryInterface
, a classe da factory deve fornecer
o método mágico __invoke
retornando a instância de um único serviço. O service manager é
passado para o método __invoke
como o parâmetro $container
; pode ser usado durante a construção de
o serviço para acessar outros serviços (para injetar dependências). O segundo argumento ($requestedName
) é
o nome do serviço. O terceiro argumento ($options
) pode ser usado para passar alguns parâmetros para o serviço, e
é usado somente quando você solicita o serviço com o método build()
do service manager.
Como exemplo, vamos escrever uma factory para o nosso serviço de conversão de moeda (observe o código abaixo).
Nós não usamos lógicas de construção complexas para o nosso serviço CurrencyConverter
, mas para serviços mais complexos.
serviços, talvez seja necessário usar um.
<?php
namespace Application\Service\Factory;
use Zend\ServiceManager\Factory\FactoryInterface;
use Application\Service\CurrencyConverter;
// Factory class
class CurrencyConverterFactory implements FactoryInterface
{
public function __invoke(ContainerInterface $container,
$requestedName, array $options = null)
{
// Create an instance of the class.
$service = new CurrencyConverter();
return $service;
}
}
Tecnicamente, no ZF3 você pode usar a mesma classe da factory para instanciar vários serviços que possuem código de instanciação (para esse propósito, você pode usar o argumento
$requestedName
passado para o método__invoke()
da factory). No entanto, principalmente você criará uma fábrica diferente para cada serviço.
Ainda mais complexo de uma factory é quando você precisa determinar na execução
em real time em que os nomes dos serviços devem ser registrados. Para tal situação,
você pode usar uma abstract factory. Uma classe de fábrica abstrata deve
implementar a interface AbstractFactoryInterface
:
<?php
namespace Zend\ServiceManager\Factory;
use Interop\Container\ContainerInterface;
interface AbstractFactoryInterface extends FactoryInterface
{
public function canCreate(ContainerInterface $container, $requestedName);
}
Uma abstract factory tem dois métodos: canCreate()
e __invoke ()
. O primeiro é necessário para testar se a factory pode
criar o serviço com o nome certo, e este último permite realmente
criar o serviço. Os métodos usam dois parâmetros: service manager ($container
) e
nome do serviço ($requestedName
).
Comparando com a usual factory classe, a diferença é que o classe de factory usual tipicamente cria apenas um único tipo de serviço, mas uma abstract factory pode dinamicamente criar tantos tipos de serviços quanto quiser.
Você registra uma abstract factory com o método setAbstractFactory()
do service manager.
Abstract factory são um recurso poderoso, mas você deve usá-las somente quando realmente necessário, porque Eles afetam negativamente o desempenho. É melhor usar as fábricas usuais (não abstratas).
Às vezes, você pode querer definir um alias para um serviço. O alias
é como um link simbólico: faz referência ao serviço já registrado.
Para criar um alias, você usa o método setAlias()
do service manager:
<?php
// Register an alias for the CurrencyConverter service
$serviceManager->setAlias('CurConv', CurrencyConverter::class);
Uma vez cadastrado, você pode recuperar o serviço pelo seu nome e alias usando o
método get()
do service manager.
Por padrão, os serviços são armazenados no service manager somente em instância única. Isso também é chamado do design patterns singleton
Por exemplo, quando você tenta recuperar o serviço CurrencyConverter
duas vezes, você receberá
o mesmo objeto. Isso também é chamado de serviço compartilhado (shared).
Mas, em algumas situações (raras), você precisará criar uma nova instância de um serviço sempre que alguém solicitar
do service manager. Um exemplo é o EventManager
- você obtém uma nova instância a cada vez que o solicita.
Para marcar um serviço como não compartilhado, você pode usar o método setShared()
do service manager:
$serviceManager->setShared('EventManager', false);
Em seu site, você normalmente usa a configuração do service manager para registrar seus serviços (em vez de chamar métodos do service manager, conforme descrito acima).
Para registrar automaticamente um serviço no service manager, geralmente
é usado um arquivo de configuração para o com uma chave service_manager
. Você pode colocar essa chave
dentro de um arquivo de configuração no nível do aplicativo ou em um nível de módulo
arquivo de configuração.
Se você está colocando essa chave em um arquivo de configuração de nível de módulo, seja Cuidado com o perigo de sobrescrever o nome durante a incorporação de configurações. Não registre o mesmo nome de serviço em módulos diferentes.
A chave de configuração service_manager
deve se parecer como:
<?php
return [
//...
// Register the services under this key
'service_manager' => [
'services' => [
// Register service class instances here
//...
],
'invokables' => [
// Register invokable classes here
//...
],
'factories' => [
// Register factories here
//...
],
'abstract_factories' => [
// Register abstract factories here
//...
],
'aliases' => [
// Register service aliases here
//...
],
'shared' => [
// Specify here which services must be non-shared
]
],
//...
];
No exemplo acima, você pode ver que o service_manager
pode conter vários
subchaves para registrar serviços de maneiras diferentes:
services
(linha 7) permite registrar instâncias de classes;invokables
(linha 11) permite registrar o nome completo da classe de um serviço;
o serviço será instanciado usando o carregamento lento;factories
(linha 15) permite registrar uma fábrica, que é capaz
criar instâncias de um único serviço;abstract_factories
(linha 19) pode ser usado para registrar fábricas abstratas,
que são capazes de registrar vários serviços pelo nome;aliases
(linha 23) fornece a capacidade de registrar um alias para um serviço.shared
(linha 27) permite especificar quais serviços devem ser não compartilhados.Como exemplo, vamos registrar nosso serviço CurrencyConverter
e criar um alias para ele:
<?php
use Zend\ServiceManager\Factory\InvokableFactory;
use Application\Service\CurrencyConverter;
return [
//...
// Register the services under this key
'service_manager' => [
'factories' => [
// Register CurrencyConverter service.
CurrencyConverter::class => InvokableFactory::class
],
'aliases' => [
// Register an alias for the CurrencyConverter service.
'CurConv' => CurrencyConverter::class
],
],
//...
];