주의!
여드름은 이제 변경 사항으로 인해 폐쇄되었습니다. 새로운 기능은 추가되지 않으며 외관상의 변경도 허용되지 않습니다. 허용되는 유일한 변경 사항은 최신 PHP 버전과의 호환성 및 보안 문제 수정입니다.
주의!
이것은 Pimple 3.x에 대한 문서입니다. Pimple 1.x를 사용하는 경우 Pimple 1.x 설명서를 읽어보세요. Pimple 1.x 코드를 읽는 것도 간단한 종속성 주입 컨테이너를 만드는 방법을 자세히 알아볼 수 있는 좋은 방법입니다(최신 버전의 Pimple은 성능에 더 중점을 둡니다).
Pimple은 PHP용 작은 종속성 주입 컨테이너입니다.
프로젝트에서 Pimple을 사용하기 전에 composer.json
파일에 추가하세요.
$ ./composer.phar require pimple/pimple " ^3.0 "
컨테이너를 생성하는 것은 Container
인스턴스를 생성하는 것과 같습니다.
use Pimple Container ;
$ container = new Container ();
다른 많은 종속성 주입 컨테이너와 마찬가지로 Pimple은 서비스 와 매개 변수라는 두 가지 종류의 데이터를 관리합니다.
서비스는 더 큰 시스템의 일부로 무언가를 수행하는 객체입니다. 서비스의 예: 데이터베이스 연결, 템플릿 엔진 또는 메일러. 거의 모든 전역 객체가 서비스가 될 수 있습니다.
서비스는 객체의 인스턴스를 반환하는 익명 함수 로 정의됩니다.
// define some services
$ container [ ' session_storage ' ] = fn ( $ c ) => new SessionStorage ( ' SESSION_ID ' );
$ container [ ' session ' ] = fn ( $ c ) => new Session ( $ c [ ' session_storage ' ]);
익명 함수는 현재 컨테이너 인스턴스에 액세스할 수 있으므로 다른 서비스나 매개변수에 대한 참조를 허용합니다.
객체는 객체를 얻을 때만 생성되므로 정의 순서는 중요하지 않습니다.
정의된 서비스를 사용하는 것도 매우 쉽습니다.
// 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);
기본적으로 서비스를 받을 때마다 Pimple은 동일한 인스턴스를 반환합니다. 모든 호출에 대해 다른 인스턴스가 반환되도록 하려면 익명 함수를 factory()
메서드로 래핑하세요.
$ container [ ' session ' ] = $ container -> factory ( fn ( $ c ) => new Session ( $ c [ ' session_storage ' ]));
이제 $container['session']
을 호출할 때마다 세션의 새 인스턴스가 반환됩니다.
매개변수를 정의하면 외부에서 컨테이너 구성을 쉽게 하고 전역 값을 저장할 수 있습니다.
// define some parameters
$ container [ ' cookie_name ' ] = ' SESSION_ID ' ;
$ container [ ' session_storage_class ' ] = ' SessionStorage ' ;
아래와 같이 session_storage
서비스 정의를 변경하는 경우:
$ container [ ' session_storage ' ] = fn ( $ c ) => new $ c [ ' session_storage_class ' ]( $ c [ ' cookie_name ' ]);
이제 서비스 정의를 재정의하는 대신 cookie_name
매개변수를 재정의하여 쿠키 이름을 쉽게 변경할 수 있습니다.
Pimple은 익명 함수를 서비스 정의로 보기 때문에 익명 함수를 protect()
메서드로 래핑하여 매개변수로 저장해야 합니다.
$ container [ ' random_func ' ] = $ container -> protect ( fn () => rand ());
어떤 경우에는 서비스 정의를 정의한 후 수정해야 할 수도 있습니다. extend()
메서드를 사용하여 서비스가 생성된 직후에 서비스에서 실행될 추가 코드를 정의할 수 있습니다.
$ container [ ' session_storage ' ] = fn ( $ c ) => new $ c [ ' session_storage_class ' ]( $ c [ ' cookie_name ' ]);
$ container -> extend ( ' session_storage ' , function ( $ storage , $ c ) {
$ storage ->. . .();
return $ storage ;
});
첫 번째 인수는 확장할 서비스의 이름이고, 두 번째 인수는 개체 인스턴스와 컨테이너에 액세스하는 함수입니다.
동일한 라이브러리를 반복해서 사용하는 경우 한 프로젝트의 일부 서비스를 다음 프로젝트에서 재사용할 수 있습니다. PimpleServiceProviderInterface
구현하여 서비스를 공급자 로 패키징합니다.
use Pimple Container ;
class FooProvider implements Pimple ServiceProviderInterface
{
public function register ( Container $ pimple )
{
// register some services and parameters
// on $pimple
}
}
그런 다음 컨테이너에 공급자를 등록합니다.
$ pimple -> register ( new FooProvider ());
객체에 접근하면 Pimple은 정의한 익명 함수를 자동으로 호출하여 서비스 객체를 생성합니다. 이 함수에 대한 원시 액세스를 얻으려면 raw()
메서드를 사용할 수 있습니다.
$ container [ ' session ' ] = fn ( $ c ) => new Session ( $ c [ ' session_storage ' ]);
$ sessionFunction = $ container -> raw ( ' session ' );
역사적인 이유로 Container
클래스는 PSR-11 ContainerInterface
를 구현하지 않습니다. 그러나 Pimple은 Pimple 컨테이너 클래스에서 코드를 분리할 수 있는 도우미 클래스를 제공합니다.
PimplePsr11Container
클래스를 사용하면 PsrContainerContainerInterface
메서드를 사용하여 기본 Pimple 컨테이너의 콘텐츠에 액세스할 수 있습니다.
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 );
때로는 서비스가 실제로 사용될 것인지 확신하지 못한 채 다른 여러 서비스에 액세스해야 하는 경우도 있습니다. 이러한 경우 서비스 인스턴스화를 게으르게 수행할 수 있습니다.
전통적인 솔루션은 전체 서비스 컨테이너를 주입하여 실제로 필요한 서비스만 얻는 것입니다. 그러나 이는 서비스에 애플리케이션의 나머지 부분에 대한 너무 광범위한 액세스를 제공하고 실제 종속성을 숨기기 때문에 권장되지 않습니다.
ServiceLocator
실제로 필요할 때만 인스턴스화하면서 미리 정의된 서비스 집합에 대한 액세스를 제공함으로써 이 문제를 해결하기 위한 것입니다.
또한 등록 시 사용한 이름과 다른 이름으로 서비스를 제공할 수도 있습니다. 예를 들어, 이벤트 디스패처가 dispatcher
라는 이름으로 등록되어 있는 동안 EventDispatcherInterface
의 인스턴스가 event_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 );
};
컬렉션을 사용하는 클래스가 해당 메서드 중 하나가 호출될 때 이후 단계에서 컬렉션을 반복하기만 하면 배열에 있는 서비스 인스턴스 컬렉션을 전달하는 것이 비효율적일 수 있습니다. 컬렉션에 저장된 서비스 중 하나와 이를 사용하는 클래스 사이에 순환 종속성이 있는 경우에도 문제가 발생할 수 있습니다.
ServiceIterator
클래스는 이러한 문제를 해결하는 데 도움이 됩니다. 인스턴스화 중에 서비스 이름 목록을 수신하고 다음을 반복할 때 서비스를 검색합니다.
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 ' ));