Bagaimana cara memanggil fungsi dari string yang disimpan dalam variabel?

288

Saya harus bisa memanggil fungsi, tetapi nama fungsi disimpan dalam variabel, apakah ini mungkin? misalnya:

function foo ()
{
  // kode di sini
}

bilah fungsi ()
{
  // kode di sini
}

$ functionName = "foo";
// saya perlu memanggil fungsi berdasarkan apa yang $ functionName
Kadal
sumber

Jawaban:

464

$functionName() atau call_user_func($functionName)

rajutan
sumber
85
Jika Anda perlu memanggil fungsi objek yang namanya dalam variabel lakukan ini: call_user_func (array ($ obj, $ func), $ params)
BlackDivine
1
Lihat juga jawaban saya di bawah ini jika Anda berurusan dengan kelas dan metode. Saya tidak mengharapkan apa yang saya temukan.
Chris K
Bagaimana saya bisa membatalkan kesalahan definisi fungsi jika fungsi itu tidak didefinisikan? Apakah jika ($ functionName) cukup?
SaidbakR
14
@ sємsєм: Anda harus menggunakan is_callablefungsi ini. Ini akan memeriksa apakah variabel berisi sesuatu yang dapat disebut sebagai fungsi (baik itu nama fungsi, array yang berisi instance objek dan nama fungsi, atau fungsi / penutupan anonim)
knittl
6
@Pacerier: Berikan nama kelas sebagai elemen pertama dari array:call_user_func(array('ClassName', 'method'), $args)
knittl
121

Versi favorit saya adalah versi inline:

${"variableName"} = 12;

$className->{"propertyName"};
$className->{"methodName"}();

StaticClass::${"propertyName"};
StaticClass::{"methodName"}();

Anda dapat menempatkan variabel atau ekspresi di dalam tanda kurung juga!

Lajos Meszaros
sumber
1
@marcioAlmada: memposting komentar alih-alih mengedit jawaban orang lain dengan cara ini; itu menambah kebingungan - tidak jelas siapa yang mengatakan apa [kepada yang penasaran: lihat revisi untuk penjelasan]
kryger
4
Ok @kryger. Baris 2 {"functionName"}();tidak sah dan akan menimbulkan kesalahan sintaksis.
marcio
4
Terima kasih teman-teman, untuk balasannya, ia memang melempar kesalahan, bahkan di php 5.4, sehingga sintaks hanya berfungsi dengan metode objek. Mengedit pos.
Lajos Meszaros
17

Seperti yang telah disebutkan, ada beberapa cara untuk mencapai ini dengan kemungkinan metode teraman call_user_func()atau jika Anda harus, Anda juga dapat menempuh jalan $function_name(). Dimungkinkan untuk memberikan argumen menggunakan kedua metode ini seperti itu

$function_name = 'foobar';

$function_name(arg1, arg2);

call_user_func_array($function_name, array(arg1, arg2));

Jika fungsi yang Anda panggil milik objek Anda masih dapat menggunakan salah satu dari ini

$object->$function_name(arg1, arg2);

call_user_func_array(array($object, $function_name), array(arg1, arg2));

Namun jika Anda akan menggunakan $function_name()metode ini mungkin ide yang baik untuk menguji keberadaan fungsi jika namanya dinamis

if(method_exists($object, $function_name))
{
    $object->$function_name(arg1, arg2);
}
olliefinn
sumber
12

Jika ada orang lain yang dibawa ke sini oleh google karena mereka mencoba menggunakan variabel untuk metode di dalam kelas, di bawah ini adalah contoh kode yang benar-benar akan berfungsi. Tidak satu pun di atas yang berfungsi untuk situasi saya. Perbedaan utama adalah &dalam deklarasi $c = & new...dan &$cdisahkan call_user_func.

Kasus spesifik saya adalah ketika menerapkan kode seseorang yang berkaitan dengan warna dan dua metode anggota lighten()dan darken()dari kelas csscolor.php. Untuk alasan apa pun, saya ingin memiliki kode yang sama dapat memanggil terang atau gelap daripada memilihnya dengan logika. Ini mungkin hasil dari kekeraskepalaan saya untuk tidak hanya menggunakan if-else atau mengubah kode yang memanggil metode ini.

$lightdark="lighten"; // or optionally can be darken
$color="fcc";   // a hex color
$percent=0.15;  
include_once("csscolor.php");
$c = & new CSS_Color($color);
$rtn=call_user_func( array(&$c,$lightdark),$color,$percent);

Perhatikan bahwa mencoba apa pun dengan $c->{...}tidak berhasil. Setelah membaca konten yang disumbangkan pembaca di bagian bawah halaman php.net call_user_func, saya dapat menyatukannya di atas. Perhatikan juga bahwa $paramssebagai array tidak berfungsi untuk saya:

// This doesn't work:
$params=Array($color,$percent);
$rtn=call_user_func( array(&$c,$lightdark),$params);

Upaya di atas akan memberikan peringatan tentang metode yang mengharapkan argumen ke-2 (persen).

Chris K
sumber
apa yang dilakukan "$ c = & CSS_Color baru"? Saya berbicara tentang amp (&). Apa ini & ubah?
Danon
@danon & adalah "alamat" atau operator "referensi". Saya membuat objek baru kelas CSS_Color dan kemudian menetapkan referensi (alias alamat) ke $ c. Saya benci kode berulang, jadi saya sering menggunakan nama variabel variabel.
Chris K
1
Saya tidak mengerti. Bukankah benda dilewatkan dengan referensi di mana-mana? Maksud saya, jika Anda melewatkan $ c ke suatu fungsi dan mengubahnya, objek aslinya juga akan terpengaruh, bukan? Tidak masalah apakah Anda menggunakan ini & tidak, apakah saya benar?
Danon
Saya mencoba berbagai kombinasi simbol dan hanya ketika membaca komentar pengguna di php.org saya bisa mendapatkan kode yang berfungsi. Anda harus memulai diskusi itu dengan orang-orang yang bertanggung jawab atas perilaku php.
Chris K
Untuk contoh kedua Anda salah membaca, Anda sedang mencari call_user_func_array
Tofandel
12

Beberapa tahun terlambat, tetapi ini adalah cara terbaik sekarang imho:

$x = (new ReflectionFunction("foo"))->getClosure();
$x();
Kesalahan server dari dalam
sumber
3
Cara untuk OOP untuk saya, tetapi saya memberikannya +1, karena belum ada yang menyebutkan kelas Reflection.
Lajos Meszaros
Tidak mengerti jawaban ini. Masalah apa yang dipecahkannya, tepatnya?
David Spector
9

Demi kelengkapan, Anda juga dapat menggunakan eval () :

$functionName = "foo()";
eval($functionName);

Namun, call_user_func()itu cara yang tepat.

karim79
sumber
8
Perlu dicatat bahwa eval () akan memungkinkan perilaku yang sama tetapi juga akan memungkinkan untuk mengeksekusi kode PHP apa pun. Jadi variabel bisa digunakan untuk membajak sistem. Call_user_func () tidak membawa masalah keamanan seperti itu.
Yohanes
4
evalbukan solusi untuk pertanyaan ini.
ankr
1
Tolong jangan lakukan ini karena penyerang bisa menghapus server Anda sepenuhnya.
andreszs
9

Solusi: Gunakan PHP7

Catatan: Untuk versi yang diringkas, lihat TL; DR di akhir jawaban.

Metode Lama

Pembaruan : Salah satu metode lama yang dijelaskan di sini telah dihapus. Lihat jawaban lain untuk penjelasan tentang metode lain, itu tidak dibahas di sini. Ngomong-ngomong, jika jawaban ini tidak membantu Anda, Anda harus kembali memperbarui barang-barang Anda. Dukungan PHP 5.6 telah berakhir pada Januari 2019 (sekarang bahkan PHP 7.0 dan 7.1 tidak didukung). Lihat versi yang didukung untuk informasi lebih lanjut.

Seperti yang disebutkan lainnya, dalam PHP5 (dan juga dalam versi yang lebih baru seperti PHP7) kita bisa menggunakan variabel sebagai nama fungsi, menggunakan call_user_func()dan call_user_func_array()(yang, secara pribadi, saya benci fungsi-fungsi itu), dll.

Metode Baru

Pada PHP7, ada cara baru yang diperkenalkan:

Catatan: Segala sesuatu di dalam <something>tanda kurung berarti satu atau lebih ekspresi untuk membentuk sesuatu, misalnya <function_name>ekspresi yang membentuk nama fungsi.

Dynamic Function Call: Function Name On-the-fly

Kita dapat menggunakan satu atau lebih ekspresi di dalam tanda kurung sebagai nama fungsi hanya dalam sekali jalan, dalam bentuk:

(<function_name>)(arguments);

Sebagai contoh:

function something(): string
{
    return "something";
}

$bar = "some_thing";

(str_replace("_", "", $bar))(); // something

// Possible, too; but generally, not recommended, because makes your code more complicated
(str_replace("_", "", $bar))()(); 

Catatan: Meskipun menghapus tanda kurung str_replace()bukanlah kesalahan, menempatkan tanda kurung membuat kode lebih mudah dibaca. Namun, Anda terkadang tidak dapat melakukannya, misalnya saat menggunakan .operator. Agar konsisten, saya sarankan Anda untuk selalu menempatkan tanda kurung.

Dynamic Method Call: Metode Name On-the-fly

Sama seperti panggilan fungsi dinamis, kita dapat melakukan hal yang sama dengan pemanggilan metode, dikelilingi oleh kurung kurawal alih-alih tanda kurung (untuk formulir tambahan, navigasikan ke TL; bagian DR):

$object->{<method_name>}(arguments);
$object::{<method_name>}(arguments);

Lihat dalam contoh:

class Foo
{
    public function another(): string
    {
        return "something";
    }
}

$bar = "another thing";

(new Something())->{explode(" ", $bar)[0]}(); // something

Metode Panggilan Dinamis: Sintaks Array

Cara yang lebih elegan ditambahkan di PHP7 adalah sebagai berikut:

[<object>, <method_name>](arguments);
[<class_name>, <method_name>](arguments); // Static calls only

Sebagai contoh:

class Foo
{
    public function nonStaticCall()
    {
        echo "Non-static call";
    }
    public static function staticCall()
    {
        echo "Static call";
    }
}

$x = new X();

[$x, "non" . "StaticCall"](); // Non-static call
[$x, "static" . "Call"](); // Static call

Catatan: Manfaat menggunakan metode ini daripada yang sebelumnya adalah, Anda tidak peduli dengan jenis panggilan (yaitu apakah itu statis atau tidak).

Contoh Ekstra: Menggunakan Kelas Anonim

Membuat segalanya sedikit rumit, Anda bisa menggunakan kombinasi kelas anonim dan fitur di atas:

$bar = "SomeThing";

echo (new class {
    public function something()
    {
        return 512;
    }
})->{strtolower($bar)}(); // 512

TL; DR (Kesimpulan)

Secara umum, di PHP7, menggunakan formulir berikut ini dimungkinkan:

// Everything inside `<something>` brackets means one or more expressions
// to form something

// Dynamic function call
(<function_name>)(arguments)

// Dynamic method call on an object
$object->{<method_name>}(arguments)
$object::{<method_name>}(arguments)

// Dynamic method call on a dynamically-generated object
(<object>)->{<method_name>}(arguments)
(<object>)::{<method_name>}(arguments)

// Dynamic method call, statically
ClassName::{<method_name>}(arguments)
(<class_name>)::{<method_name>}(arguments)

// Dynamic method call, array-like (no different between static and non-static calls
[<object>, <method_name>](arguments)

// Dynamic method call, array-like, statically
[<class_name>, <method_name>](arguments)

Sebagian besar didasarkan pada pembicaraan PHP ini .

MAChitgarha
sumber
1
Daftar ekspresi yang bagus, juga mencakup:(expressions)->$bar()
Necro
1
@Necro Terima kasih, saya menambahkannya!
MAChitgarha
7

Nama fungsi dinamis dan ruang nama

Hanya untuk menambahkan titik tentang nama fungsi dinamis saat menggunakan ruang nama.

Jika Anda menggunakan ruang nama, yang berikut ini tidak akan berfungsi kecuali jika fungsi Anda ada di ruang nama global:

namespace greetings;

function hello()
{
    // do something
}

$myvar = "hello";
$myvar(); // interpreted as "\hello();"

Apa yang harus dilakukan?

Anda harus menggunakan call_user_func()sebagai gantinya:

// if hello() is in the current namespace
call_user_func(__NAMESPACE__.'\\'.$myvar);

// if hello() is in another namespace
call_user_func('mynamespace\\'.$myvar);
Jivan
sumber
3

Melengkapi jawaban @Chris K jika Anda ingin memanggil metode objek, Anda dapat menyebutnya menggunakan variabel tunggal dengan bantuan penutupan:

function get_method($object, $method){
    return function() use($object, $method){
        $args = func_get_args();
        return call_user_func_array(array($object, $method), $args);           
    };
}

class test{        

    function echo_this($text){
        echo $text;
    }
}

$test = new test();
$echo = get_method($test, 'echo_this');
$echo('Hello');  //Output is "Hello"

Saya memposting contoh lain di sini

David
sumber
3

Apa yang saya pelajari dari pertanyaan ini dan jawabannya. Terima kasih semuanya!

Katakanlah saya memiliki variabel dan fungsi ini:

$functionName1 = "sayHello";
$functionName2 = "sayHelloTo";
$functionName3 = "saySomethingTo";

$friend = "John";
$datas = array(
    "something"=>"how are you?",
    "to"=>"Sarah"
);

function sayHello()
{
echo "Hello!";
}

function sayHelloTo($to)
{
echo "Dear $to, hello!";
}

function saySomethingTo($something, $to)
{
echo "Dear $to, $something";
}
  1. Untuk memanggil fungsi tanpa argumen

    // Calling sayHello()
    call_user_func($functionName1); 

    Halo!

  2. Untuk memanggil fungsi dengan 1 argumen

    // Calling sayHelloTo("John")
    call_user_func($functionName2, $friend);

    John terkasih, halo!

  3. Untuk memanggil fungsi dengan 1 argumen atau lebih Ini akan berguna jika Anda memanggil fungsi Anda secara dinamis dan setiap fungsi memiliki jumlah argumen yang berbeda. Ini adalah kasus saya yang saya cari (dan selesaikan). call_user_func_arrayadalah kuncinya

    // You can add your arguments
    // 1. statically by hard-code, 
    $arguments[0] = "how are you?"; // my $something
    $arguments[1] = "Sarah"; // my $to
    
    // 2. OR dynamically using foreach
    $arguments = NULL;
    foreach($datas as $data) 
    {
        $arguments[] = $data;
    }
    
    // Calling saySomethingTo("how are you?", "Sarah")
    call_user_func_array($functionName3, $arguments);

    Sarah sayang, bagaimana kabarmu?

Yay sampai jumpa!

asmasyakirah
sumber
2

Gunakan fungsi call_user_func.

PaulJWilliams
sumber
1

Kode berikut dapat membantu menulis fungsi dinamis dalam PHP. sekarang nama fungsi dapat diubah secara dinamis dengan variabel '$ current_page'.

$current_page = 'home_page';
$function = @${$current_page . '_page_versions'};
$function = function() {
    echo 'current page';
};
$function();
rakesh.falke
sumber
Fungsi anonim (yaitu fungsi dinamis) berbeda dari fungsi variabel (yaitu fungsi panggilan secara dinamis). Juga, Anda mengganti $functionvariabel tanpa alasan apa pun.
MAChitgarha
0

Cara termudah untuk memanggil suatu fungsi dengan aman menggunakan nama yang disimpan dalam variabel adalah,

//I want to call method deploy that is stored in functionname 
$functionname = 'deploy';

$retVal = {$functionname}('parameters');

Saya telah menggunakan seperti di bawah ini untuk membuat tabel migrasi di Laravel secara dinamis,

foreach(App\Test::$columns as $name => $column){
        $table->{$column[0]}($name);
}
Debashis Prusty
sumber
-5

Salah satu pendekatan yang tidak konvensional, yang muncul di benak saya adalah, kecuali jika Anda menghasilkan seluruh kode melalui beberapa super otonom AI yang menulis sendiri , ada kemungkinan besar bahwa fungsi yang Anda ingin panggilan "dinamis", sudah ditentukan dalam kode Anda mendasarkan. Jadi mengapa tidak memeriksa string dan melakukan ifelsetarian terkenal untuk memanggil ... Anda mengerti maksud saya.

misalnya.

if($functionName == 'foo'){
  foo();
} else if($functionName == 'bar'){
  bar();
}

Bahkan switch-casedapat digunakan jika Anda tidak menyukai rasa ifelsetangga yang hambar .

Saya mengerti bahwa ada kasus di mana " memanggil fungsi secara dinamis " akan menjadi kebutuhan mutlak ( Seperti beberapa logika rekursif yang memodifikasi sendiri ). Tetapi sebagian besar kasus penggunaan sepele sehari-hari hanya bisa dihindari.

Ini menghilangkan banyak ketidakpastian dari aplikasi Anda, sambil memberi Anda kesempatan untuk mengeksekusi fungsi mundur jika string tidak cocok dengan definisi fungsi yang tersedia. MENURUT OPINI SAYA.

Mohd Abdul Mujib
sumber
Jika nilai variabel sudah menjadi nama fungsi mengapa kerja ekstra? juga akan lebih baik untuk mendapatkan umpan balik yang solid untuk melihat apakah fungsi tersebut ada oleh php sebelum Anda menjalankannya: if (method_exists($functionName)) $functionName(); else //exception or handle
Necro
2
Yup poin Anda valid, tetapi karena nama fungsi dinamis, dalam logika Anda, string apa pun yang datang harus dijalankan jika fungsi dengan nama itu ada, terlepas dari apakah fungsi itu relevan dalam situasi saat ini atau tidak. Pendekatan saya memastikan bahwa hanya sekelompok fungsi tertentu yang dapat dieksekusi jika tidak kesalahan dapat terjadi. Ini dapat disederhanakan dengan array dan dengan demikian.
Mohd Abdul Mujib
-11

Saya tidak tahu mengapa Anda harus menggunakan itu, sama sekali tidak terdengar baik bagi saya, tetapi jika hanya ada sedikit fungsi, Anda bisa menggunakan if / else jika membangun. Saya tidak tahu apakah solusi langsung mungkin.

sesuatu seperti $ foo = "bar"; $ test = "foo"; tes echo $$;

harus mengembalikan bar, Anda dapat mencoba tetapi saya tidak berpikir ini akan berfungsi untuk fungsi

Flo
sumber