¡Precaución!
Pimple ahora está cerrado a cambios. No se agregarán nuevas funciones ni se aceptarán cambios estéticos. Los únicos cambios aceptados son la compatibilidad con versiones más recientes de PHP y la solución de problemas de seguridad.
¡Precaución!
Esta es la documentación de Pimple 3.x. Si está utilizando Pimple 1.x, lea la documentación de Pimple 1.x. Leer el código de Pimple 1.x también es una buena manera de aprender más sobre cómo crear un contenedor de inyección de dependencias simple (las versiones recientes de Pimple se centran más en el rendimiento).
Pimple es un pequeño contenedor de inyección de dependencias para PHP.
Antes de usar Pimple en tu proyecto, agrégalo a tu archivo composer.json
:
$ ./composer.phar require pimple/pimple " ^3.0 "
Crear un contenedor es cuestión de crear una instancia Container
:
use Pimple Container ;
$ container = new Container ();
Como muchos otros contenedores de inyección de dependencia, Pimple gestiona dos tipos diferentes de datos: servicios y parámetros .
Un servicio es un objeto que hace algo como parte de un sistema más grande. Ejemplos de servicios: una conexión a una base de datos, un motor de plantillas o un correo. Casi cualquier objeto global puede ser un servicio.
Los servicios se definen mediante funciones anónimas que devuelven una instancia de un objeto:
// define some services
$ container [ ' session_storage ' ] = fn ( $ c ) => new SessionStorage ( ' SESSION_ID ' );
$ container [ ' session ' ] = fn ( $ c ) => new Session ( $ c [ ' session_storage ' ]);
Observe que la función anónima tiene acceso a la instancia del contenedor actual, lo que permite referencias a otros servicios o parámetros.
Como los objetos sólo se crean cuando los obtienes, el orden de las definiciones no importa.
Utilizar los servicios definidos también es muy sencillo:
// 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);
De forma predeterminada, cada vez que obtienes un servicio, Pimple devuelve la misma instancia del mismo. Si desea que se devuelva una instancia diferente para todas las llamadas, ajuste su función anónima con el método factory()
$ container [ ' session ' ] = $ container -> factory ( fn ( $ c ) => new Session ( $ c [ ' session_storage ' ]));
Ahora, cada llamada a $container['session']
devuelve una nueva instancia de la sesión.
Definir un parámetro permite facilitar la configuración de su contenedor desde el exterior y almacenar valores globales:
// define some parameters
$ container [ ' cookie_name ' ] = ' SESSION_ID ' ;
$ container [ ' session_storage_class ' ] = ' SessionStorage ' ;
Si cambia la definición del servicio session_storage
como se muestra a continuación:
$ container [ ' session_storage ' ] = fn ( $ c ) => new $ c [ ' session_storage_class ' ]( $ c [ ' cookie_name ' ]);
Ahora puede cambiar fácilmente el nombre de la cookie anulando el parámetro cookie_name
en lugar de redefinir la definición del servicio.
Debido a que Pimple ve las funciones anónimas como definiciones de servicios, debes envolver las funciones anónimas con el método protect()
para almacenarlas como parámetros:
$ container [ ' random_func ' ] = $ container -> protect ( fn () => rand ());
En algunos casos, es posible que desee modificar una definición de servicio después de haberla definido. Puede utilizar el método extend()
para definir código adicional que se ejecutará en su servicio justo después de su creación:
$ container [ ' session_storage ' ] = fn ( $ c ) => new $ c [ ' session_storage_class ' ]( $ c [ ' cookie_name ' ]);
$ container -> extend ( ' session_storage ' , function ( $ storage , $ c ) {
$ storage ->. . .();
return $ storage ;
});
El primer argumento es el nombre del servicio a extender, el segundo una función que obtiene acceso a la instancia del objeto y al contenedor.
Si utiliza las mismas bibliotecas una y otra vez, es posible que desee reutilizar algunos servicios de un proyecto al siguiente; empaquete sus servicios en un proveedor implementando PimpleServiceProviderInterface
:
use Pimple Container ;
class FooProvider implements Pimple ServiceProviderInterface
{
public function register ( Container $ pimple )
{
// register some services and parameters
// on $pimple
}
}
Luego, registre el proveedor en un Contenedor:
$ pimple -> register ( new FooProvider ());
Cuando accede a un objeto, Pimple llama automáticamente a la función anónima que definió, que crea el objeto de servicio por usted. Si desea obtener acceso sin formato a esta función, puede utilizar el método raw()
:
$ container [ ' session ' ] = fn ( $ c ) => new Session ( $ c [ ' session_storage ' ]);
$ sessionFunction = $ container -> raw ( ' session ' );
Por razones históricas, la clase Container
no implementa PSR-11 ContainerInterface
. Sin embargo, Pimple proporciona una clase auxiliar que te permitirá desacoplar tu código de la clase contenedora de Pimple.
La clase PimplePsr11Container
le permite acceder al contenido de un contenedor Pimple subyacente utilizando los 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 );
A veces, un servicio necesita acceder a varios otros servicios sin estar seguro de que todos ellos serán realmente utilizados. En esos casos, es posible que desee que la creación de instancias de los servicios sea diferida.
La solución tradicional es inyectar todo el contenedor de servicios para obtener sólo los servicios realmente necesarios. Sin embargo, esto no se recomienda porque da a los servicios un acceso demasiado amplio al resto de la aplicación y oculta sus dependencias reales.
ServiceLocator
pretende resolver este problema dando acceso a un conjunto de servicios predefinidos y creando instancias de ellos sólo cuando realmente sea necesario.
También le permite hacer que sus servicios estén disponibles con un nombre diferente al utilizado para registrarlos. Por ejemplo, es posible que desee utilizar un objeto que espera que una instancia de EventDispatcherInterface
esté disponible con el nombre event_dispatcher
mientras su despachador de eventos se ha registrado con el nombre 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 );
};
Pasar una colección de instancias de servicios en una matriz puede resultar ineficiente si la clase que consume la colección solo necesita iterar sobre ella en una etapa posterior, cuando se llama a uno de sus métodos. También puede generar problemas si existe una dependencia circular entre uno de los servicios almacenados en la colección y la clase que lo consume.
La clase ServiceIterator
le ayuda a resolver estos problemas. Recibe una lista de nombres de servicios durante la creación de instancias y recuperará los servicios cuando se repitan:
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 ' ));