Peringatan!
Jerawat sekarang ditutup untuk perubahan. Tidak ada fitur baru yang akan ditambahkan dan perubahan tampilan juga tidak akan diterima. Satu-satunya perubahan yang diterima adalah kompatibilitas dengan versi PHP yang lebih baru dan perbaikan masalah keamanan.
Peringatan!
Ini adalah dokumentasi untuk Pimple 3.x. Jika Anda menggunakan Pimple 1.x, baca dokumentasi Pimple 1.x. Membaca kode Pimple 1.x juga merupakan cara yang baik untuk mempelajari lebih lanjut tentang cara membuat Kontainer Injeksi Ketergantungan sederhana (versi terbaru dari Pimple lebih fokus pada kinerja).
Pimple adalah Wadah Injeksi Ketergantungan kecil untuk PHP.
Sebelum menggunakan Pimple di proyek Anda, tambahkan ke file composer.json
Anda:
$ ./composer.phar require pimple/pimple " ^3.0 "
Membuat sebuah container adalah masalah membuat instance Container
:
use Pimple Container ;
$ container = new Container ();
Seperti banyak kontainer injeksi ketergantungan lainnya, Pimple mengelola dua jenis data yang berbeda: layanan dan parameter .
Layanan adalah objek yang melakukan sesuatu sebagai bagian dari sistem yang lebih besar. Contoh layanan: koneksi database, mesin templating, atau mailer. Hampir semua objek global dapat menjadi layanan.
Layanan ditentukan oleh fungsi anonim yang mengembalikan sebuah instance dari suatu objek:
// define some services
$ container [ ' session_storage ' ] = fn ( $ c ) => new SessionStorage ( ' SESSION_ID ' );
$ container [ ' session ' ] = fn ( $ c ) => new Session ( $ c [ ' session_storage ' ]);
Perhatikan bahwa fungsi anonim memiliki akses ke instance container saat ini, sehingga memungkinkan referensi ke layanan atau parameter lain.
Karena objek hanya dibuat saat Anda mendapatkannya, urutan definisinya tidak menjadi masalah.
Menggunakan layanan yang ditentukan juga sangat mudah:
// 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);
Secara default, setiap kali Anda mendapatkan layanan, Pimple mengembalikan contoh yang sama . Jika Anda ingin instance berbeda dikembalikan untuk semua panggilan, gabungkan fungsi anonim Anda dengan metode factory()
$ container [ ' session ' ] = $ container -> factory ( fn ( $ c ) => new Session ( $ c [ ' session_storage ' ]));
Sekarang, setiap panggilan ke $container['session']
mengembalikan instance sesi baru.
Mendefinisikan parameter memungkinkan untuk memudahkan konfigurasi kontainer Anda dari luar dan menyimpan nilai global:
// define some parameters
$ container [ ' cookie_name ' ] = ' SESSION_ID ' ;
$ container [ ' session_storage_class ' ] = ' SessionStorage ' ;
Jika Anda mengubah definisi layanan session_storage
seperti di bawah ini:
$ container [ ' session_storage ' ] = fn ( $ c ) => new $ c [ ' session_storage_class ' ]( $ c [ ' cookie_name ' ]);
Anda sekarang dapat dengan mudah mengubah nama cookie dengan mengganti parameter cookie_name
alih-alih mendefinisikan ulang definisi layanan.
Karena Pimple melihat fungsi anonim sebagai definisi layanan, Anda perlu menggabungkan fungsi anonim dengan metode protect()
untuk menyimpannya sebagai parameter:
$ container [ ' random_func ' ] = $ container -> protect ( fn () => rand ());
Dalam beberapa kasus, Anda mungkin ingin mengubah definisi layanan setelah didefinisikan. Anda dapat menggunakan metode extend()
untuk menentukan kode tambahan yang akan dijalankan pada layanan Anda setelah dibuat:
$ container [ ' session_storage ' ] = fn ( $ c ) => new $ c [ ' session_storage_class ' ]( $ c [ ' cookie_name ' ]);
$ container -> extend ( ' session_storage ' , function ( $ storage , $ c ) {
$ storage ->. . .();
return $ storage ;
});
Argumen pertama adalah nama layanan yang akan diperluas, argumen kedua adalah fungsi yang mendapat akses ke instance objek dan container.
Jika Anda menggunakan perpustakaan yang sama berulang kali, Anda mungkin ingin menggunakan kembali beberapa layanan dari satu proyek ke proyek berikutnya; paketkan layanan Anda ke penyedia dengan mengimplementasikan PimpleServiceProviderInterface
:
use Pimple Container ;
class FooProvider implements Pimple ServiceProviderInterface
{
public function register ( Container $ pimple )
{
// register some services and parameters
// on $pimple
}
}
Kemudian, daftarkan penyedia pada Container:
$ pimple -> register ( new FooProvider ());
Saat Anda mengakses suatu objek, Pimple secara otomatis memanggil fungsi anonim yang Anda tetapkan, yang membuat objek layanan untuk Anda. Jika Anda ingin mendapatkan akses mentah ke fungsi ini, Anda dapat menggunakan metode raw()
:
$ container [ ' session ' ] = fn ( $ c ) => new Session ( $ c [ ' session_storage ' ]);
$ sessionFunction = $ container -> raw ( ' session ' );
Karena alasan historis, kelas Container
tidak mengimplementasikan PSR-11 ContainerInterface
. Namun, Pimple menyediakan kelas pembantu yang memungkinkan Anda memisahkan kode dari kelas kontainer Pimple.
Kelas PimplePsr11Container
memungkinkan Anda mengakses konten container Pimple yang mendasarinya menggunakan metode 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 );
Terkadang, suatu layanan memerlukan akses ke beberapa layanan lain tanpa yakin bahwa semuanya benar-benar akan digunakan. Dalam kasus tersebut, Anda mungkin ingin instantiasi layanan menjadi lambat.
Solusi tradisionalnya adalah dengan menyuntikkan seluruh wadah layanan untuk mendapatkan layanan yang benar-benar dibutuhkan saja. Namun, hal ini tidak disarankan karena memberikan layanan akses yang terlalu luas ke seluruh aplikasi dan menyembunyikan ketergantungan sebenarnya.
ServiceLocator
dimaksudkan untuk memecahkan masalah ini dengan memberikan akses ke serangkaian layanan yang telah ditentukan sebelumnya sambil membuat instance layanan tersebut hanya jika benar-benar diperlukan.
Ini juga memungkinkan Anda membuat layanan Anda tersedia dengan nama yang berbeda dari nama yang digunakan untuk mendaftarkannya. Misalnya, Anda mungkin ingin menggunakan objek yang mengharapkan instance EventDispatcherInterface
tersedia dengan nama event_dispatcher
sementara operator acara Anda telah terdaftar dengan nama 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 );
};
Melewati kumpulan instance layanan dalam array mungkin terbukti tidak efisien jika kelas yang menggunakan koleksi tersebut hanya perlu mengulanginya di tahap berikutnya, ketika salah satu metodenya dipanggil. Hal ini juga dapat menimbulkan masalah jika ada ketergantungan melingkar antara salah satu layanan yang disimpan dalam koleksi dan kelas yang menggunakannya.
Kelas ServiceIterator
membantu Anda memecahkan masalah ini. Ia menerima daftar nama layanan selama pembuatan instance dan akan mengambil layanan ketika diulang:
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 ' ));