Suatu hari kerja yang baik, saya menulis unit test untuk logika bisnis pada proyek tempat saya bekerja. Tugas saya adalah menginisialisasi beberapa properti pribadi dari kelas dengan nilai-nilai spesifik.
Setter normal tidak dapat digunakan, karena beberapa logika dieja di sana. Mewarisi atau mengunci kelas juga tidak berhasil, karena dinyatakan final. Dan bahkan refleksi tidak cocok. Jadi saya mulai mencari solusi untuk masalah ini.
Saya menemukan artikel menarik yang menjelaskan cara menggunakan perpustakaan dg / bypass-final untuk mengunci kelas akhir. Saya menyukai opsi ini dan saya mencoba menerapkannya. Sayangnya, saya tidak berhasil, karena proyek ini menggunakan versi lama PHPUnit.
Setelah refleksi, saya ingat kelas Closure
, dan khususnya tentang metode bind()
statisnya, yang dapat mengimplementasikan fungsi anonim dalam konteks objek kelas yang diinginkan. Informasi lebih lanjut tentang ini dapat ditemukan di dokumentasi resmi . Karena itu, saya membuat sifat yang saya gunakan dalam tes saya (mungkin seseorang juga akan berguna)
trait PrivatePropertySetterTrait { protected function assignValue($object, string $attribute, $value) { $setter = function ($value) use ($attribute) { $this->$attribute = $value; }; $setterClosure = \Closure::bind($setter, $object, \get_class($object)); $setterClosure($value); } }
Ciri ini mengambil objek kelas, nama properti tempat Anda ingin menetapkan nilai dan, pada kenyataannya, nilai itu sendiri. Selanjutnya, fungsi anonim sederhana dideklarasikan, yang, menggunakan $this
pointer, memberikan nilai yang dihasilkan ke properti kelas. Selanjutnya, kelas Closure
datang dengan metode bind()
statis. Metode menerima objek kelas, fungsi anonim yang dijelaskan di atas, dan nama lengkap kelas. Dengan demikian, fungsi anonim tertanam dalam konteks objek dan metode bind()
mengembalikan kita objek kelas Closure
, yang dapat kita sebut sebagai fungsi biasa, karena ia mendefinisikan metode magic __invoke()
. Dan voila!
Pada akhirnya, saya berhasil menyelesaikan masalah saya, dan kemudian saya ingat pola desain Singleton. Apakah mungkin dengan cara yang sama menerapkan fungsi anonim yang akan membuat objek baru di kelas? Tentu saja saya pergi untuk memeriksanya!
Dengan menulis sepotong kecil kode
Kotak pasir dengan kode
<?php final class Singleton { private static $instance; public static function getInstance() { if (null === self::$instance) { self::$instance = new self(); } return self::$instance; } private function __construct() { } private function __clone() { } private function __wakeup() { } } $s1 = Singleton::getInstance(); \var_dump(\spl_object_id($s1)); $createNewInstance = function () { return new self(); }; $newInstanceClosure = Closure::bind($createNewInstance, $s1, Singleton::class); $s2 = $newInstanceClosure(); \var_dump(\spl_object_id($s2));
yang bekerja pada prinsip yang sama, tetapi alih-alih menetapkan nilai ke properti kelas, objek baru dibuat menggunakan operator new
. Fungsi \spl_object_id()
mengembalikan pengidentifikasi unik untuk objek. Informasi lebih lanjut tentang fitur ini dapat ditemukan dalam dokumentasi . Menggunakan spl_object_id()
dan var_dump()
saya mendapatkan pengidentifikasi unik objek dan melihat bahwa mereka berbeda! Saya masih berhasil mengkonfirmasi teori ini dan membuat contoh baru dari kelas Singleton!
Pada artikel ini, saya ingin membagikan temuan saya yang sangat aneh dengan komunitas PHP.
Terima kasih atas perhatian anda!