Bir değişkende saklanan bir dizeden bir fonksiyon nasıl çağrılır?


289

Bir işlevi çağırmak gerekir, ancak işlev adı bir değişkende saklanır, bu mümkün mü? Örneğin:

foo işlevi ()
{
  // buraya kod yaz
}

işlev çubuğu ()
{
  // buraya kod yaz
}

$ functionName = "foo";
// $ functionName ne olduğuna bağlı olarak işlevi çağırmak gerekir

Yanıtlar:


465

85
Adı bir değişkende olan bir nesnenin işlevini çağırmanız gerekiyorsa şunu yapın: call_user_func (dizi ($ obj, $ func), $ params)
BlackDivine

1
Sınıflar ve yöntemlerle uğraşıyorsanız aşağıdaki cevabımı da görün. Bulduğumu beklemiyordum.
Chris K

İşlev tanımlanmadıysa işlev tanımı hatasını nasıl geçersiz kılabilirim? Eğer ($ functionName) yeterli mi?
SaidbakR

14
@ sємsєм: İşlevi kullanmalısınız is_callable. Bir değişkenin fonksiyon olarak çağrılabilecek bir şey içerip içermediğini kontrol eder (bir fonksiyonun adı, bir nesne örneği ve bir fonksiyon adı içeren bir dizi veya anonim bir fonksiyon / kapatma olsun)
knittl

6
@Pacerier: Dizinin ilk öğesi olarak sınıf call_user_func(array('ClassName', 'method'), $args)
adını girin

122

En sevdiğim versiyon satır içi versiyon:

${"variableName"} = 12;

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

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

Değişkenleri veya ifadeleri parantez içine de yerleştirebilirsiniz!


1
@marcioAlmada: başkalarının cevaplarını bu şekilde düzenlemek yerine yorum yapın; kafa karışıklığı ekliyor - kimin ne dediğini açık değil [meraklı: açıklamaların revizyonlarına bakın]
kryger

4
Tamam @ kryger. Satır 2 {"functionName"}();yasal değildir ve bir sözdizimi hatası verir.
marcio

4
Teşekkürler çocuklar, cevaplar için, gerçekten php 5.4 bile bir hata atar, böylece sözdizimi sadece nesne yöntemleri ile çalışır. Yayını düzenledi.
Lajos Meszaros


18

Daha önce de belirtildiği gibi, bunu en güvenli yöntemle elde etmenin birkaç yolu vardır call_user_func()veya eğer gerekiyorsa rotasını da kullanabilirsiniz $function_name(). Bu yöntemlerin her ikisini de kullanarak argüman geçirmek mümkündür

$function_name = 'foobar';

$function_name(arg1, arg2);

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

Aradığınız işlev bir nesneye aitse, yine de bunlardan birini kullanabilirsiniz.

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

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

Ancak $function_name()yöntemi kullanacaksanız , adın herhangi bir şekilde dinamik olup olmadığını görmek için işlevin varlığını test etmek iyi bir fikir olabilir.

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

13

Sınıfta bir yöntem için bir değişken kullanmaya çalıştıkları için başka birinin google tarafından buraya getirilmesi durumunda, aşağıda gerçekten çalışacak bir kod örneği verilmiştir. Yukarıdakilerin hiçbiri durumum için işe yaramadı. Temel fark, &beyanda bulunulma $c = & new...ve &$caktarılmadır call_user_func.

Birinin kod renk ve iki üye yöntemleri ile yapmak zorunda uygularken Benim özgü bir durumdur lighten()ve darken()csscolor.php sınıfından. Her ne sebeple olursa olsun, aynı kodun mantıkla seçilmesinden ziyade açıklaştırmak veya koyulaştırmak istedim. Bu benim inatçılığımın sadece if-else kullanmaması ya da bu yöntemi çağıran kodu değiştirmesinin sonucu olabilir.

$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);

İle bir şey denemek $c->{...}işe yaramadı unutmayın. Php.net sayfasının alt kısmında okuyucuya katkıda bulunan içeriği inceledikten sonra call_user_func, yukarıdakileri bir araya getirebildim. Ayrıca, $paramsbir dizi olarak benim için çalışmadığını unutmayın :

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

Yukarıdaki girişim, 2. argümanı (yüzde) bekleyen yöntem hakkında bir uyarı verecektir.


"$ c = & yeni CSS_Color" ne yapar? Amfiden (&) bahsediyorum. Bu neyi değiştiriyor?
Danon

@danon & "adres" veya "başvuru" operatörünün adresidir. Sınıf CSS_Color yeni bir nesne kışkırtıcı ve sonra referans (aka adresi) $ c atar. Kodu tekrarlamaktan nefret ediyorum, bu yüzden değişken değişken isimleri kullanıyorum.
Chris K

1
Anlamıyorum. Zaten nesneler referans olarak her yerde geçmiyor mu? Yani, bir işleve $ c iletir ve değiştirirseniz, orijinal nesne de etkilenir, değil mi? Bunu kullansanız da kullanmasanız da, haklı mıyım?
Danon

Farklı sembol kombinasyonlarını denedim ve sadece php.org'daki kullanıcı yorumlarını okurken çalışma kodu alabildim. Bu tartışmayı php'nin davranışından sorumlu kişilerle başlatmanız gerekir.
Chris K


13

Birkaç yıl geç, ama şimdi imho en iyi yolu:

$x = (new ReflectionFunction("foo"))->getClosure();
$x();

3
Benim için OOP yolu, ama henüz bir +1 verdim, çünkü henüz kimse Yansıma sınıflarından bahsetmedi.
Lajos Meszaros

Bu cevabı anlamıyorum. Tam olarak hangi problemi çözüyor?
David Spector

10

Tamlık uğruna eval () işlevini de kullanabilirsiniz :

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

Ancak, call_user_func()doğru yol.


8
Eval () işlevinin aynı davranışa izin vereceği, ancak herhangi bir PHP kodunun yürütülmesine de izin vereceği unutulmamalıdır. Böylece değişken sistemi ele geçirmek için kullanılabilir. Call_user_func () yöntemi bu tür güvenlik sorunları getirmez.
John

4
evalbu sorunun çözümü değildir.
ankr

1
Lütfen bunu yapmayın, çünkü bir saldırgan sunucunuzu tamamen silebilir.
andreszs

10

Çözüm: PHP7 kullanın

Not: Özetlenen bir sürüm için cevabın sonundaki TL; DR bölümüne bakınız.

Eski Yöntemler

Güncelleme : Burada açıklanan eski yöntemlerden biri kaldırıldı. Diğer yöntemlerle ilgili açıklama için diğer cevaplara bakınız, burada ele alınmamıştır. Bu arada, bu cevap size yardımcı olmazsa, öğelerinizi yükseltmeye geri dönmelisiniz. PHP 5.6 desteği Ocak 2019'da sona ermiştir (şimdi PHP 7.0 ve 7.1 bile desteklenmemektedir). Daha fazla bilgi için desteklenen sürümlere bakın .

Diğerlerinin de belirttiği gibi, PHP5'te (ve ayrıca PHP7 gibi yeni sürümlerde) işlev adları, kullanım call_user_func()ve call_user_func_array()(kişisel olarak bu işlevlerden nefret ediyorum) vb.

Yeni Yöntemler

PHP7'den itibaren tanıtılan yeni yollar var:

Not:<something> Köşeli ayraçların içindeki her şey, bir şey oluşturmak için bir veya daha fazla ifade <function_name>anlamına gelir ; örneğin , bir işlev adı oluşturan ifadeler anlamına gelir.

Dinamik İşlev Çağrısı: İşlev Adı Anında

Parantez içindeki bir veya daha fazla ifadeyi işlev adı olarak tek seferde şu şekilde kullanabiliriz:

(<function_name>)(arguments);

Örneğin:

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))()(); 

Not: Parantezleri kaldırmak str_replace()bir hata olmamasına rağmen, parantez koymak kodu daha okunaklı hale getirir. Ancak bunu bazen yapamazsınız, örneğin .operatör kullanırken . Tutarlı olmak için, parantezleri her zaman koymanızı öneririm.

Dinamik Yöntem Çağrısı: Yöntem Adı Anında

Dinamik işlev çağrıları gibi, parantez yerine kıvırcık ayraçlarla çevrili yöntem çağrılarıyla aynı şekilde yapabiliriz (ekstra formlar için TL; DR bölümüne gidin):

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

Bir örnekte görün:

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

$bar = "another thing";

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

Dinamik Yöntem Çağrısı: Dizi Sözdizimi

PHP7'ye eklenen daha zarif bir yol şudur:

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

Örnek olarak:

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

Not: Bu yöntemi bir öncekine göre kullanmanın yararı, arama türünü önemsememenizdir (yani statik olup olmadığı).

Ekstra Örnek: Anonim Sınıfları Kullanma

İşleri biraz karmaşık hale getirmek için, anonim sınıfların ve yukarıdaki özelliklerin bir kombinasyonunu kullanabilirsiniz :

$bar = "SomeThing";

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

TL; DR (Sonuç)

Genellikle PHP7'de aşağıdaki formların kullanılması mümkündür:

// 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)

Çoğunlukla bu PHP konuşmasına dayanır .


1
Güzel ifadeler listesi de şunları içerir:(expressions)->$bar()
Necro

1
@ Necro Teşekkürler, ekledim!
MAChitgarha

8

Dinamik işlev adları ve ad alanları

Yalnızca ad alanlarını kullanırken dinamik işlev adları hakkında bir nokta eklemek için.

Ad alanlarını kullanıyorsanız, işlevinizin genel ad alanında olması dışında aşağıdakiler çalışmaz :

namespace greetings;

function hello()
{
    // do something
}

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

Ne yapalım?

Bunun call_user_func()yerine şunları kullanmalısınız :

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

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

4

Bir nesnenin yöntemini çağırmak istiyorsanız @Chris K cevabını tamamlayarak, bir kapatma yardımıyla tek bir değişken kullanarak çağırabilirsiniz:

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"

Başka bir örnek yayınlanmıştır burada


4

Bu sorudan öğrendiklerim ve cevapları. Hepinize teşekkürler!

Diyelim ki bu değişkenler ve fonksiyonlar var:

$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. İşlevi bağımsız değişkenler olmadan çağırmak için

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

    Merhaba!

  2. İşlevi 1 argümanla çağırmak için

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

    Sevgili John, merhaba!

  3. İşlevi 1 veya daha fazla bağımsız değişkenle çağırmak için İşlevlerinizi dinamik olarak çağırıyorsanız ve her işlevin farklı sayıda bağımsız değişkeni varsa bu yararlı olacaktır. Bu benim aradığım (ve çözdüğüm) durumum. call_user_func_arrayAnahtar mı

    // 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);

    Sevgili Sarah, nasılsın?

Güle güle!



2

Aşağıdaki kod PHP'de dinamik fonksiyon yazmaya yardımcı olabilir. şimdi işlev adı '$ current_page' değişkeni ile dinamik olarak değiştirilebilir.

$current_page = 'home_page';
$function = @${$current_page . '_page_versions'};
$function = function() {
    echo 'current page';
};
$function();

Anonim işlevler (yani dinamik işlevler) değişken işlevlerden farklıdır (yani işlevleri dinamik olarak çağırmak). Ayrıca, $functiondeğişkeni herhangi bir sebep olmadan değiştiriyorsunuz .
MAChitgarha

1

Bir değişkende saklanan adı kullanarak bir işlevi güvenli bir şekilde çağırmanın en kolay yolu,

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

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

Laravel'de dinamik olarak geçiş tabloları oluşturmak için aşağıdaki gibi kullandım,

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

-4

Aklıma gelen alışılmadık bir yaklaşım, tüm kodu kendini yazan bazı süper ultra özerk AI yoluyla üretmediğiniz sürece , "dinamik olarak" çağırmak istediğiniz işlevlerin kodunuzda zaten tanımlanmış olması olasılığı yüksektir. tabanı. Öyleyse neden sadece ipi kontrol ifelseedip çağırmak için rezil dansı yapmıyorsun ?

Örneğin.

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

Merdivenin switch-casemülayim tadından hoşlanmıyorsanız bile kullanılabilir ifelse.

" Fonksiyonu dinamik olarak çağırmak " ın mutlak bir zorunluluk olduğu durumlar olduğunu anlıyorum ( Kendini değiştiren bazı özyinelemeli mantık gibi ). Ancak günlük önemsiz kullanım durumlarının çoğu kaçabilir.

Dize, kullanılabilir işlevlerin herhangi bir tanımıyla eşleşmezse, bir geri dönüş işlevi yürütme şansı verirken, uygulamanızdan çok fazla belirsizliği ortadan kaldırır. BENİM NACİZANE FİKRİME GÖRE.


Değişkenin değeri zaten fonksiyonun adı ise ekstra neden çalışır? Ayrıca, işlevi çalıştırmadan önce php tarafından var olup olmadığını görmek için sağlam bir geri bildirim almak iyi olurdu: if (method_exists($functionName)) $functionName(); else //exception or handle
Necro

2
Evet, noktanız geçerlidir, ancak işlev adı dinamik olduğundan, mantığınızda, o işlevin geçerli durumda olup olmadığına bakılmaksızın, bu adla bir işlev varsa, gelen herhangi bir dize yürütülür. Benim yaklaşımım sadece belirli bir grup fonksiyonun çalıştırılabilmesini sağlar, aksi takdirde bir hata atılabilir. Diziler ve bu şekilde basitleştirilebilir.
Mohd Abdul Mujib

-10

Neden u kullanmak zorunda bilmiyorum, bana hiç iyi gelmiyor, ama işlevlerin sadece küçük bir miktarı varsa, bir if / elseif yapı kullanabilirsiniz. Doğrudan bir çözümün mümkün olup olmadığını bilmiyorum.

$ foo = "bar" gibi bir şey; $ test = "foo"; echo $$ testi;

bar dönmelidir, etrafında deneyebilirsiniz ama bu işlevler için işe yarayacağını sanmıyorum

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.