คำเตือน!
ขณะนี้ Pimple ปิดให้บริการเนื่องจากการเปลี่ยนแปลง จะไม่มีการเพิ่มคุณสมบัติใหม่และไม่มีการยอมรับการเปลี่ยนแปลงด้านความสวยงามเช่นกัน การเปลี่ยนแปลงที่ยอมรับเพียงอย่างเดียวคือความเข้ากันได้กับ PHP เวอร์ชันใหม่กว่าและการแก้ไขปัญหาด้านความปลอดภัย
คำเตือน!
นี่คือเอกสารสำหรับ Pimple 3.x หากคุณใช้ Pimple 1.x โปรดอ่านเอกสารประกอบของ Pimple 1.x การอ่านโค้ด Pimple 1.x ยังเป็นวิธีที่ดีในการเรียนรู้เพิ่มเติมเกี่ยวกับวิธีสร้าง Dependency Injection Container แบบง่ายๆ (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 คุณสามารถใช้เมธอด raw()
ได้:
$ container [ ' session ' ] = fn ( $ c ) => new Session ( $ c [ ' session_storage ' ]);
$ sessionFunction = $ container -> raw ( ' session ' );
ด้วยเหตุผลทางประวัติศาสตร์ คลาส Container
จึงไม่ใช้ PSR-11 ContainerInterface
อย่างไรก็ตาม Pimple มีคลาสตัวช่วยที่จะให้คุณแยกโค้ดของคุณออกจากคลาสคอนเทนเนอร์ของ Pimple
คลาส PimplePsr11Container
ช่วยให้คุณเข้าถึงเนื้อหาของคอนเทนเนอร์ Pimple พื้นฐานได้โดยใช้เมธอด 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 );
บางครั้งบริการจำเป็นต้องเข้าถึงบริการอื่นๆ หลายอย่างโดยไม่แน่ใจว่าจะใช้บริการทั้งหมดจริงหรือไม่ ในกรณีดังกล่าว คุณอาจต้องการให้การสร้างอินสแตนซ์ของบริการเป็นแบบขี้เกียจ
วิธีแก้ปัญหาแบบเดิมคือการอัดฉีดคอนเทนเนอร์บริการทั้งหมดเพื่อรับเฉพาะบริการที่จำเป็นจริงๆ เท่านั้น อย่างไรก็ตาม ไม่แนะนำให้ทำเช่นนี้ เนื่องจากบริการต่างๆ สามารถเข้าถึงส่วนที่เหลือของแอปพลิเคชันได้กว้างเกินไป และซ่อนการอ้างอิงที่แท้จริงไว้ด้วย
ServiceLocator
มีวัตถุประสงค์เพื่อแก้ไขปัญหานี้โดยให้สิทธิ์เข้าถึงชุดบริการที่กำหนดไว้ล่วงหน้าในขณะที่สร้างอินสแตนซ์เมื่อจำเป็นจริงๆ เท่านั้น
นอกจากนี้ยังช่วยให้คุณให้บริการต่างๆ ของคุณพร้อมใช้งานภายใต้ชื่อที่แตกต่างจากชื่อที่ใช้ในการลงทะเบียนได้ ตัวอย่างเช่น คุณอาจต้องการใช้ออบเจ็กต์ที่คาดว่าอินสแตนซ์ของ EventDispatcherInterface
จะพร้อมใช้งานภายใต้ชื่อ event_dispatcher
ในขณะที่โปรแกรมเลือกจ่ายงานกิจกรรมของคุณได้รับการลงทะเบียนภายใต้ชื่อ 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 ' ));