Vorsicht!
Pimple ist jetzt wegen Änderungen geschlossen. Es werden keine neuen Funktionen hinzugefügt und auch keine kosmetischen Änderungen akzeptiert. Die einzigen akzeptierten Änderungen sind die Kompatibilität mit neueren PHP-Versionen und die Behebung von Sicherheitsproblemen.
Vorsicht!
Dies ist die Dokumentation für Pimple 3.x. Wenn Sie Pimple 1.x verwenden, lesen Sie die Dokumentation zu Pimple 1.x. Das Lesen des Pimple 1.x-Codes ist auch eine gute Möglichkeit, mehr darüber zu erfahren, wie man einen einfachen Dependency-Injection-Container erstellt (neuere Versionen von Pimple sind stärker auf Leistung ausgerichtet).
Pimple ist ein kleiner Dependency-Injection-Container für PHP.
Bevor Sie Pimple in Ihrem Projekt verwenden, fügen Sie es Ihrer composer.json
Datei hinzu:
$ ./composer.phar require pimple/pimple " ^3.0 "
Beim Erstellen eines Containers geht es darum, eine Container
zu erstellen:
use Pimple Container ;
$ container = new Container ();
Wie viele andere Dependency-Injection-Container verwaltet Pimple zwei verschiedene Arten von Daten: Dienste und Parameter .
Ein Dienst ist ein Objekt, das als Teil eines größeren Systems etwas tut. Beispiele für Dienste: eine Datenbankverbindung, eine Template-Engine oder ein Mailer. Fast jedes globale Objekt kann ein Dienst sein.
Dienste werden durch anonyme Funktionen definiert, die eine Instanz eines Objekts zurückgeben:
// define some services
$ container [ ' session_storage ' ] = fn ( $ c ) => new SessionStorage ( ' SESSION_ID ' );
$ container [ ' session ' ] = fn ( $ c ) => new Session ( $ c [ ' session_storage ' ]);
Beachten Sie, dass die anonyme Funktion Zugriff auf die aktuelle Containerinstanz hat und Verweise auf andere Dienste oder Parameter zulässt.
Da Objekte erst erstellt werden, wenn Sie sie erhalten, spielt die Reihenfolge der Definitionen keine Rolle.
Auch die Nutzung der definierten Dienste ist denkbar einfach:
// 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);
Standardmäßig gibt Pimple jedes Mal, wenn Sie einen Dienst erhalten, dieselbe Instanz davon zurück. Wenn Sie möchten, dass für alle Aufrufe eine andere Instanz zurückgegeben wird, schließen Sie Ihre anonyme Funktion mit der Methode factory()
ein
$ container [ ' session ' ] = $ container -> factory ( fn ( $ c ) => new Session ( $ c [ ' session_storage ' ]));
Jetzt gibt jeder Aufruf von $container['session']
eine neue Instanz der Sitzung zurück.
Durch die Definition eines Parameters können Sie die Konfiguration Ihres Containers von außen vereinfachen und globale Werte speichern:
// define some parameters
$ container [ ' cookie_name ' ] = ' SESSION_ID ' ;
$ container [ ' session_storage_class ' ] = ' SessionStorage ' ;
Wenn Sie die Dienstdefinition session_storage
wie folgt ändern:
$ container [ ' session_storage ' ] = fn ( $ c ) => new $ c [ ' session_storage_class ' ]( $ c [ ' cookie_name ' ]);
Sie können den Cookie-Namen jetzt einfach ändern, indem Sie den Parameter cookie_name
überschreiben, anstatt die Dienstdefinition neu zu definieren.
Da Pimple anonyme Funktionen als Dienstdefinitionen betrachtet, müssen Sie anonyme Funktionen mit der Methode protect()
umschließen, um sie als Parameter zu speichern:
$ container [ ' random_func ' ] = $ container -> protect ( fn () => rand ());
In manchen Fällen möchten Sie möglicherweise eine Dienstdefinition ändern, nachdem sie definiert wurde. Sie können die Methode extend()
verwenden, um zusätzlichen Code zu definieren, der direkt nach der Erstellung Ihres Dienstes ausgeführt werden soll:
$ container [ ' session_storage ' ] = fn ( $ c ) => new $ c [ ' session_storage_class ' ]( $ c [ ' cookie_name ' ]);
$ container -> extend ( ' session_storage ' , function ( $ storage , $ c ) {
$ storage ->. . .();
return $ storage ;
});
Das erste Argument ist der Name des zu erweiternden Dienstes, das zweite eine Funktion, die Zugriff auf die Objektinstanz und den Container erhält.
Wenn Sie dieselben Bibliotheken immer wieder verwenden, möchten Sie möglicherweise einige Dienste von einem Projekt zum nächsten wiederverwenden; Packen Sie Ihre Dienste in einen Anbieter, indem Sie PimpleServiceProviderInterface
implementieren:
use Pimple Container ;
class FooProvider implements Pimple ServiceProviderInterface
{
public function register ( Container $ pimple )
{
// register some services and parameters
// on $pimple
}
}
Registrieren Sie dann den Anbieter in einem Container:
$ pimple -> register ( new FooProvider ());
Wenn Sie auf ein Objekt zugreifen, ruft Pimple automatisch die von Ihnen definierte anonyme Funktion auf, die das Serviceobjekt für Sie erstellt. Wenn Sie Rohzugriff auf diese Funktion erhalten möchten, können Sie die Methode raw()
verwenden:
$ container [ ' session ' ] = fn ( $ c ) => new Session ( $ c [ ' session_storage ' ]);
$ sessionFunction = $ container -> raw ( ' session ' );
Aus historischen Gründen implementiert die Container
Klasse das PSR-11 ContainerInterface
nicht. Pimple bietet jedoch eine Hilfsklasse, mit der Sie Ihren Code von der Pimple-Containerklasse entkoppeln können.
Mit der PimplePsr11Container
Klasse können Sie mithilfe PsrContainerContainerInterface
Methoden auf den Inhalt eines zugrunde liegenden Pimple-Containers zugreifen:
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 );
Manchmal benötigt ein Dienst Zugriff auf mehrere andere Dienste, ohne sicher zu sein, dass alle tatsächlich genutzt werden. In diesen Fällen möchten Sie möglicherweise, dass die Instanziierung der Dienste verzögert erfolgt.
Die traditionelle Lösung besteht darin, den gesamten Service-Container einzuschleusen, um nur die wirklich benötigten Services zu erhalten. Dies wird jedoch nicht empfohlen, da dadurch den Diensten ein zu umfassender Zugriff auf den Rest der Anwendung gewährt wird und ihre tatsächlichen Abhängigkeiten verborgen bleiben.
Der ServiceLocator
soll dieses Problem lösen, indem er Zugriff auf eine Reihe vordefinierter Dienste ermöglicht und diese nur dann instanziiert, wenn sie tatsächlich benötigt werden.
Es ermöglicht Ihnen auch, Ihre Dienste unter einem anderen Namen als dem, unter dem sie registriert wurden, verfügbar zu machen. Beispielsweise möchten Sie möglicherweise ein Objekt verwenden, das erwartet, dass eine Instanz von EventDispatcherInterface
unter dem Namen event_dispatcher
verfügbar ist, während Ihr Event-Dispatcher unter dem Namen dispatcher
registriert wurde:
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 );
};
Das Übergeben einer Sammlung von Dienstinstanzen in einem Array kann sich als ineffizient erweisen, wenn die Klasse, die die Sammlung nutzt, sie erst zu einem späteren Zeitpunkt durchlaufen muss, wenn eine ihrer Methoden aufgerufen wird. Es kann auch zu Problemen führen, wenn eine zirkuläre Abhängigkeit zwischen einem der in der Sammlung gespeicherten Dienste und der Klasse besteht, die ihn nutzt.
Die ServiceIterator
-Klasse hilft Ihnen bei der Lösung dieser Probleme. Es empfängt während der Instanziierung eine Liste von Dienstnamen und ruft die Dienste ab, wenn es über Folgendes iteriert:
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 ' ));