Cuidado!
Pimple agora está fechado para alterações. Nenhum novo recurso será adicionado e nenhuma alteração cosmética será aceita. As únicas alterações aceitas são a compatibilidade com versões mais recentes do PHP e correções de problemas de segurança.
Cuidado!
Esta é a documentação do Pimple 3.x. Se você estiver usando o Pimple 1.x, leia a documentação do Pimple 1.x. Ler o código do Pimple 1.x também é uma boa maneira de aprender mais sobre como criar um contêiner de injeção de dependência simples (as versões recentes do Pimple são mais focadas no desempenho).
Pimple é um pequeno contêiner de injeção de dependência para PHP.
Antes de usar o Pimple em seu projeto, adicione-o ao arquivo composer.json
:
$ ./composer.phar require pimple/pimple " ^3.0 "
Criar um contêiner é uma questão de criar uma instância Container
:
use Pimple Container ;
$ container = new Container ();
Como muitos outros contêineres de injeção de dependência, o Pimple gerencia dois tipos diferentes de dados: serviços e parâmetros .
Um serviço é um objeto que faz algo como parte de um sistema maior. Exemplos de serviços: uma conexão de banco de dados, um mecanismo de modelagem ou um mailer. Quase qualquer objeto global pode ser um serviço.
Os serviços são definidos por funções anônimas que retornam uma instância de um objeto:
// define some services
$ container [ ' session_storage ' ] = fn ( $ c ) => new SessionStorage ( ' SESSION_ID ' );
$ container [ ' session ' ] = fn ( $ c ) => new Session ( $ c [ ' session_storage ' ]);
Observe que a função anônima tem acesso à instância atual do contêiner, permitindo referências a outros serviços ou parâmetros.
Como os objetos só são criados quando você os obtém, a ordem das definições não importa.
Usar os serviços definidos também é muito fácil:
// 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);
Por padrão, cada vez que você obtém um serviço, o Pimple retorna a mesma instância dele. Se você deseja que uma instância diferente seja retornada para todas as chamadas, envolva sua função anônima com o método factory()
$ container [ ' session ' ] = $ container -> factory ( fn ( $ c ) => new Session ( $ c [ ' session_storage ' ]));
Agora, cada chamada para $container['session']
retorna uma nova instância da sessão.
Definir um parâmetro permite facilitar a configuração externa do seu contêiner e armazenar valores globais:
// define some parameters
$ container [ ' cookie_name ' ] = ' SESSION_ID ' ;
$ container [ ' session_storage_class ' ] = ' SessionStorage ' ;
Se você alterar a definição do serviço session_storage
como abaixo:
$ container [ ' session_storage ' ] = fn ( $ c ) => new $ c [ ' session_storage_class ' ]( $ c [ ' cookie_name ' ]);
Agora você pode alterar facilmente o nome do cookie substituindo o parâmetro cookie_name
em vez de redefinir a definição de serviço.
Como o Pimple vê funções anônimas como definições de serviço, você precisa agrupar funções anônimas com o método protect()
para armazená-las como parâmetros:
$ container [ ' random_func ' ] = $ container -> protect ( fn () => rand ());
Em alguns casos, talvez você queira modificar uma definição de serviço depois de ela ter sido definida. Você pode usar o método extend()
para definir código adicional a ser executado em seu serviço logo após ele ser criado:
$ container [ ' session_storage ' ] = fn ( $ c ) => new $ c [ ' session_storage_class ' ]( $ c [ ' cookie_name ' ]);
$ container -> extend ( ' session_storage ' , function ( $ storage , $ c ) {
$ storage ->. . .();
return $ storage ;
});
O primeiro argumento é o nome do serviço a ser estendido, o segundo é uma função que obtém acesso à instância do objeto e ao contêiner.
Se você usa as mesmas bibliotecas repetidamente, talvez queira reutilizar alguns serviços de um projeto para outro; empacotar seus serviços em um provedor implementando PimpleServiceProviderInterface
:
use Pimple Container ;
class FooProvider implements Pimple ServiceProviderInterface
{
public function register ( Container $ pimple )
{
// register some services and parameters
// on $pimple
}
}
Em seguida, registre o provedor em um Container:
$ pimple -> register ( new FooProvider ());
Quando você acessa um objeto, o Pimple chama automaticamente a função anônima que você definiu, que cria o objeto de serviço para você. Se quiser obter acesso bruto a esta função, você pode usar o método raw()
:
$ container [ ' session ' ] = fn ( $ c ) => new Session ( $ c [ ' session_storage ' ]);
$ sessionFunction = $ container -> raw ( ' session ' );
Por motivos históricos, a classe Container
não implementa o PSR-11 ContainerInterface
. No entanto, Pimple fornece uma classe auxiliar que permitirá dissociar seu código da classe contêiner Pimple.
A classe PimplePsr11Container
permite acessar o conteúdo de um contêiner Pimple subjacente usando métodos PsrContainerContainerInterface
:
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 );
Às vezes, um serviço precisa de acesso a vários outros serviços sem ter certeza de que todos eles serão realmente utilizados. Nesses casos, você pode querer que a instanciação dos serviços seja lenta.
A solução tradicional é injetar todo o contêiner de serviços para obter apenas os serviços realmente necessários. No entanto, isto não é recomendado porque dá aos serviços um acesso demasiado amplo ao resto da aplicação e esconde as suas dependências reais.
O ServiceLocator
pretende resolver este problema, dando acesso a um conjunto de serviços predefinidos e instanciando-os apenas quando realmente necessário.
Também permite que você disponibilize seus serviços com um nome diferente daquele usado para registrá-los. Por exemplo, você pode querer usar um objeto que espera que uma instância de EventDispatcherInterface
esteja disponível sob o nome event_dispatcher
enquanto seu despachante de eventos tiver sido registrado sob o nome 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 );
};
Passar uma coleção de instâncias de serviços em um array pode ser ineficiente se a classe que consome a coleção só precisar iterar sobre ela em um estágio posterior, quando um de seus métodos for chamado. Também pode causar problemas se houver uma dependência circular entre um dos serviços armazenados na coleção e a classe que o consome.
A classe ServiceIterator
ajuda a resolver esses problemas. Ele recebe uma lista de nomes de serviços durante a instanciação e recuperará os serviços quando for iterado:
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 ' ));