Anda bisa menggunakan __call. Tapi tolong jangan gunakan __call. Mengubah perilaku objek secara dinamis adalah cara yang sangat mudah untuk membuat kode Anda tidak dapat dibaca dan dipertahankan.
Sangat, sangat pintar tapi kludgy dalam proyek dunia nyata IMO! (dan +1 untuk melawan downvoter anonim)
Pekka
2
@pekka - tapi bagaimana tepatnya, itu kludgy? Tentu, saya tidak mengatakan saya ahli dalam memperluas objek selama runtime di PHP, tapi jujur saya tidak bisa mengatakan saya melihat banyak yang salah dengannya. (mungkin saya hanya memiliki selera yang buruk)
karim79
16
Saya akan menambahkan check is_callable jika juga (Jadi Anda tidak sengaja mencoba memanggil string). Dan juga lemparkan BadMethodCallException () jika Anda tidak dapat menemukan metode, sehingga Anda tidak mengembalikannya dan menganggapnya berhasil kembali. Juga, buat parameter pertama fungsi sebagai objek itu sendiri, lalu lakukan array_unshift($args, $this);pemanggilan metode sebelum, sehingga fungsi tersebut mendapatkan referensi ke objek tanpa perlu mengikatnya secara eksplisit ...
ircmaxell
3
Saya pikir orang kehilangan intinya, persyaratan aslinya menggunakan objek tanpa kelas.
Thomas-peter
1
Saya benar-benar menyarankan agar Anda sepenuhnya menghapus tes isset () karena jika fungsi itu tidak ditentukan, Anda mungkin tidak ingin kembali secara diam-diam.
Menggunakan hanya __call untuk mengizinkan penambahan metode baru pada waktu proses memiliki kelemahan utama bahwa metode tersebut tidak dapat menggunakan $ referensi instance $ this. Semuanya bekerja dengan baik, hingga metode yang ditambahkan tidak menggunakan $ this dalam kode.
Pembaruan: Pendekatan yang ditampilkan di sini memiliki kekurangan utama: Fungsi baru bukan anggota kelas yang sepenuhnya memenuhi syarat; $thistidak ada dalam metode saat dipanggil dengan cara ini. Ini berarti Anda harus meneruskan objek ke fungsi sebagai parameter jika Anda ingin bekerja dengan data atau fungsi dari instance objek! Selain itu, Anda tidak akan dapat mengakses privateatau protectedanggota kelas dari fungsi ini.
Pertanyaan bagus dan ide cerdas menggunakan fungsi anonim baru!
Menariknya, ini berhasil: Ganti
$me->doSomething();// Doesn't work
oleh call_user_func pada fungsi itu sendiri :
call_user_func($me->doSomething);// Works!
apa yang tidak berhasil adalah cara yang "benar":
call_user_func(array($me,"doSomething"));// Doesn't work
jika dipanggil seperti itu, PHP membutuhkan metode untuk dideklarasikan dalam definisi kelas.
Apakah ini private/ public/ protectedvisibilitas masalah?
Pembaruan: Tidak. Tidak mungkin memanggil fungsi dengan cara normal bahkan dari dalam kelas, jadi ini bukan masalah visibilitas. Meneruskan fungsi sebenarnya ke call_user_func()adalah satu-satunya cara agar saya dapat membuat ini berfungsi.
Untuk melihat bagaimana melakukan ini dengan eval, Anda dapat melihat kerangka mikro PHP saya, Halcyon, yang tersedia di github . Ini cukup kecil sehingga Anda harus dapat mengetahuinya tanpa masalah - berkonsentrasilah pada kelas HalcyonClassMunger.
Saya berasumsi (dan begitu juga kode saya) bahwa Anda menjalankan PHP <5.3.0 dan karenanya membutuhkan kludges dan hacks untuk membuat ini berfungsi.
ivans
Tidak ada gunanya bertanya apakah ada yang mau mengomentari downvote, saya kira ...
ivans
Mungkin saja, ada beberapa downvoting massal yang terjadi di sekitar sini. Tapi mungkin Anda ingin menjelaskan sedikit tentang apa yang Anda lakukan? Seperti yang Anda lihat, ini adalah pertanyaan menarik yang belum ada jawaban pasti.
Pekka
1
Tanpa __callsolusi, Anda dapat menggunakan bindTo(PHP> = 5.4), untuk memanggil metode dengan $thisterikat $meseperti ini:
$me =new stdClass;// Property for proving that the method has access to the above object:
$me->message ="I\'ve done something";
$me->doSomething =function(){
echo $this->message;};
call_user_func($me->doSomething->bindTo($me));// "I've done something"
Alternatifnya, Anda dapat menetapkan fungsi terikat ke variabel, lalu memanggilnya tanpa call_user_func:
Satu-satunya cara lain adalah melalui penggunaan classkit , ekstensi php eksperimental. (juga di pos)
Ya, dimungkinkan untuk menambahkan metode ke kelas PHP setelah itu ditentukan. Anda ingin menggunakan classkit, yang merupakan ekstensi "eksperimental". Tampaknya ekstensi ini tidak diaktifkan secara default, jadi itu tergantung pada apakah Anda dapat mengkompilasi biner PHP kustom atau memuat DLL PHP jika di windows (misalnya Dreamhost mengizinkan binari PHP kustom, dan mereka cukup mudah untuk diatur ).
jawaban karim79 berfungsi namun menyimpan fungsi anonim di dalam properti metode dan deklarasinya dapat menimpa properti yang ada dengan nama yang sama atau tidak berfungsi jika properti yang ada privatemenyebabkan objek kesalahan fatal tidak dapat digunakan. Saya pikir menyimpannya dalam array terpisah dan menggunakan penyetel adalah solusi yang lebih bersih. metode penyetel injektor dapat ditambahkan ke setiap objek secara otomatis menggunakan sifat .
PS Tentu saja ini adalah hack yang tidak boleh digunakan karena melanggar prinsip open closed dari SOLID.
classMyClass{//create array to store all injected methodsprivate $anon_methods = array();//create setter to fill array with injected methods on runtimepublicfunction inject_method($name, $method){
$this->anon_methods[$name]= $method;}//runs when calling non existent or private methods from object instancepublicfunction __call($name, $args){if( key_exists($name, $this->anon_methods)){
call_user_func_array($this->anon_methods[$name], $args);}}}
$MyClass =newMyClass;//method one
$print_txt =function($text){
echo $text . PHP_EOL;};
$MyClass->inject_method("print_txt", $print_txt);//method
$add_numbers =function($n, $e){
echo "$n + $e = ".($n + $e);};
$MyClass->inject_method("add_numbers", $add_numbers);//Use injected methods
$MyClass->print_txt("Hello World");
$MyClass->add_numbers(5,10);
Jawaban:
Anda dapat memanfaatkan
__call
untuk ini:sumber
array_unshift($args, $this);
pemanggilan metode sebelum, sehingga fungsi tersebut mendapatkan referensi ke objek tanpa perlu mengikatnya secara eksplisit ...Dengan PHP 7 Anda dapat menggunakan kelas anonim
PHP RFC: Kelas Anonim
sumber
Menggunakan hanya __call untuk mengizinkan penambahan metode baru pada waktu proses memiliki kelemahan utama bahwa metode tersebut tidak dapat menggunakan $ referensi instance $ this. Semuanya bekerja dengan baik, hingga metode yang ditambahkan tidak menggunakan $ this dalam kode.
Untuk mengatasi masalah ini, Anda dapat menulis ulang pola tersebut dengan cara ini
Dengan cara ini Anda dapat menambahkan metode baru yang dapat menggunakan referensi instance
sumber
Pertanyaan bagus dan ide cerdas menggunakan fungsi anonim baru!
Menariknya, ini berhasil: Ganti
oleh call_user_func pada fungsi itu sendiri :
apa yang tidak berhasil adalah cara yang "benar":
jika dipanggil seperti itu, PHP membutuhkan metode untuk dideklarasikan dalam definisi kelas.
Apakah ini
private
/public
/protected
visibilitas masalah?Pembaruan: Tidak. Tidak mungkin memanggil fungsi dengan cara normal bahkan dari dalam kelas, jadi ini bukan masalah visibilitas. Meneruskan fungsi sebenarnya ke
call_user_func()
adalah satu-satunya cara agar saya dapat membuat ini berfungsi.sumber
Anda juga dapat menyimpan fungsi dalam array:
sumber
Untuk melihat bagaimana melakukan ini dengan eval, Anda dapat melihat kerangka mikro PHP saya, Halcyon, yang tersedia di github . Ini cukup kecil sehingga Anda harus dapat mengetahuinya tanpa masalah - berkonsentrasilah pada kelas HalcyonClassMunger.
sumber
Tanpa
__call
solusi, Anda dapat menggunakanbindTo
(PHP> = 5.4), untuk memanggil metode dengan$this
terikat$me
seperti ini:Skrip lengkapnya akan terlihat seperti ini:
Alternatifnya, Anda dapat menetapkan fungsi terikat ke variabel, lalu memanggilnya tanpa
call_user_func
:sumber
Ada posting serupa di stackoverflow yang menjelaskan bahwa ini hanya dapat dicapai melalui penerapan pola desain tertentu.
Satu-satunya cara lain adalah melalui penggunaan classkit , ekstensi php eksperimental. (juga di pos)
sumber
jawaban karim79 berfungsi namun menyimpan fungsi anonim di dalam properti metode dan deklarasinya dapat menimpa properti yang ada dengan nama yang sama atau tidak berfungsi jika properti yang ada
private
menyebabkan objek kesalahan fatal tidak dapat digunakan. Saya pikir menyimpannya dalam array terpisah dan menggunakan penyetel adalah solusi yang lebih bersih. metode penyetel injektor dapat ditambahkan ke setiap objek secara otomatis menggunakan sifat .PS Tentu saja ini adalah hack yang tidak boleh digunakan karena melanggar prinsip open closed dari SOLID.
sumber