変数に格納されている文字列から関数を呼び出す方法は?


288

関数を呼び出せるようにする必要がありますが、関数名は変数に格納されていますが、これは可能ですか?例えば:

関数foo()
{
  //ここにコーディング
}

機能バー()
{
  //ここにコーディング
}

$ functionName = "foo";
// $ functionNameに基づいて関数を呼び出す必要があります

回答:


464

85
名前が変数にあるオブジェクトの関数を呼び出す必要がある場合は、次のようにします
。call_user_func

1
クラスとメソッドを扱っている場合は、以下の私の回答も参照してください。私が見つけたものを期待していませんでした。
クリスK

関数が定義されていない場合、関数定義エラーをどのように無効にできますか?if($ functionName)で十分ですか?
SaidbakR 2013年

14
@sємsєм:is_callable関数を使用する必要があります。変数に関数として呼び出すことができる何かが含まれているかどうかをチェックします(関数の名前、オブジェクトインスタンスと関数名を含む配列、または匿名の関数/クロージャー)
knittl

6
@Pacerier:配列の最初の要素としてクラス名を指定します:call_user_func(array('ClassName', 'method'), $args)
knittl

121

私のお気に入りのバージョンはインラインバージョンです。

${"variableName"} = 12;

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

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

大括弧の中に変数や式を置くこともできます!


1
@marcioAlmada:この方法で他の人の回答を編集する代わりにコメントを投稿します。それは混乱を追加します-誰が何を言ったかは明確ではありません[好奇心が強い:説明については改訂を参照してください]
kryger

4
OK @kryger。2行目{"functionName"}();は正しくないため、構文エラーがスローされます。
marcio 2014

4
返信ありがとうございます。php5.4でもエラーが発生するため、構文はオブジェクトメソッドでのみ機能します。投稿を編集しました。
Lajos Meszaros 2014


17

すでに述べたように、これを実現するにはいくつかの方法call_user_func()があります$function_name()。おそらく最も安全な方法であるか、必要であればのルートをたどることもできます。これらのメソッドの両方を使用して引数を渡すことが可能です

$function_name = 'foobar';

$function_name(arg1, arg2);

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

呼び出す関数がオブジェクトに属している場合でも、これらのいずれかを使用できます

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

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

ただし、$function_name()メソッドを使用する場合は、名前が何らかの方法で動的である場合、関数の存在をテストすることをお勧めします

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

12

クラス内のメソッドに変数を使用しようとしたためにGoogleによって他の誰かがここに連れてきた場合、以下は実際に機能するコードサンプルです。上記のどれも私の状況ではうまくいきませんでした。主な違いは、&の宣言$c = & new...&$c渡されることcall_user_funcです。

私の特定のケースは、色と2つのメンバーメソッドlighten()を使用darken()して、csscolor.phpクラスからのコードを実装する場合です。なんらかの理由で、ロジックで選択するのではなく、同じコードでlightenまたはdarkenを呼び出せるようにしたかったのです。これは、if-elseだけを使用したり、このメソッドを呼び出すコードを変更したりしないという私の頑固さの結果かもしれません。

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

で何かを試しても$c->{...}うまくいかなかったことに注意してください。上のphp.netのページの下部にある読者提供のコンテンツを閲覧したところcall_user_func、上記をまとめることができました。また、$params配列が機能なかったので注意してください:

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

この上記の試みは、2番目の引数(パーセント)を期待するメソッドに関する警告を与えます。


「$ c =&new CSS_Color」は何をしますか?私はアンプ(&)について話している。これは何を変更しますか?
Danon、2015年

@danon&は「アドレス」または「参照」演算子です。CSS_Colorクラスの新しいオブジェクトをインスタンス化し、その参照(別名アドレス)を$ cに割り当てます。私は繰り返しコードが嫌いなので、変数変数名をよく使用します。
Chris K

1
分かりません。とにかく、オブジェクトはどこでも参照渡しされていませんか?つまり、$ cを関数に渡して変更すると、元のオブジェクトも影響を受けますよね?これを使用したかどうかに関係なく、私は正しいですか?
Danon

シンボルのさまざまな組み合わせを試してみましたが、php.orgのユーザーコメントを読んでいる間に初めて、機能するコードを取得できました。PHPの動作の責任者との話し合いを始める必要があります。
クリスK

あなたが
誤って読ん

12

数年遅れますが、これが今の私にとって最良の方法です。

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

3
私にとってOOPの方法ですが、Reflectionクラスについては誰も言及していないため、+ 1を与えました。
Lajos Meszaros 2016年

この答えは理解できません。正確にはどのような問題を解決していますか?
David Spector

9

完全を期すために、eval()を使用することもできます。

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

しかし、それcall_user_func()は適切な方法です。


8
eval()は同じ動作を許可しますが、PHPコードの実行も許可することに注意してください。したがって、変数を使用してシステムを乗っ取ることができます。call_user_func()は、このようなセキュリティ問題を引き起こしません。
John

4
evalこの質問に対する解決策ではありません。
ankr 2014年

1
攻撃者がサーバーを完全に一掃する可能性があるため、これを行わないでください。
andreszs 2018

9

解決策:PHP7を使用する

注:要約バージョンについては、回答の最後にあるTL; DRを参照してください。

古い方法

更新:ここで説明した古いメソッドの1つが削除されました。他の方法の説明については、他の回答を参照してください。ここでは説明しません。ちなみに、この回答が役に立たない場合は、アップグレードを元に戻す必要があります。PHP 5.6のサポートは2019年1月に終了しました(現在、PHP 7.0および7.1もサポートされていません)。詳細については、サポートされているバージョンを参照してください。

他の人が述べたように、PHP5(およびPHP7のような新しいバージョン)では、変数を関数名として使用しcall_user_func()たり、call_user_func_array()(そして、個人的にはこれらの関数が嫌いです)などを使用したりできます。

新しい方法

PHP7以降、新しい方法が導入されています。

注:<something>括弧内のすべては、何かを形成する1つ以上の式を<function_name>意味します。例えば、関数名を形成する式を意味します。

動的関数呼び出し:オンザフライでの関数名

かっこ内の1つ以上の式を関数名として一度に使用できます。形式は次のとおりです。

(<function_name>)(arguments);

例えば:

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

注:括弧を削除してstr_replace()もエラーにはなりませんが、括弧を使用するとコードが読みやすくなります。ただし、.演算子を使用しているときなど、これを実行できない場合があります。一貫性を保つために、常に括弧を付けることをお勧めします。

動的メソッド呼び出し:オンザフライでのメソッド名

動的関数呼び出しと同様に、かっこではなく中かっこで囲まれたメソッド呼び出しでも同じように実行できます(追加のフォームの場合は、TL; DRセクションに移動します)。

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

例でそれを見てください:

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

$bar = "another thing";

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

動的メソッド呼び出し:配列構文

PHP7で追加されたよりエレガントな方法は次のとおりです。

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

例として:

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

注:前の方法よりもこのメソッドを使用する利点は、呼び出しの種類(つまり、静的かどうか)を気にしないことです。

追加の例:匿名クラスの使用

少し複雑にすると、匿名クラスと上記の機能の組み合わせを使用できます。

$bar = "SomeThing";

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

TL; DR(結論)

一般に、PHP7では、次の形式を使用することがすべて可能です。

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

主にこのPHPトークに基づいています。


1
式のニースのリストも含まれています:(expressions)->$bar()
ネクロ

1
@ネクロありがとう、私はそれを追加しました!
MAChitgarha

7

動的関数名と名前空間

名前空間を使用するときに動的関数名に関するポイントを追加するだけです。

名前空間を使用している場合、関数がグローバル名前空間にある場合を除いて、以下は機能しません

namespace greetings;

function hello()
{
    // do something
}

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

何をすべきか?

call_user_func()代わりに使用する必要があります:

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

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

3

オブジェクトのメソッドを呼び出す場合は、@ Chris Kの答えを補完するために、クロージャーを使用して単一の変数を使用して呼び出すことができます。

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"

ここに別の例を投稿しました


3

この質問と答えから学んだこと。皆さんありがとう!

これらの変数と関数があるとしましょう:

$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. 引数なしで関数を呼び出すには

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

    こんにちは!

  2. 1つの引数で関数を呼び出すには

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

    親愛なるジョン、こんにちは!

  3. 1つ以上の引数を使用して関数を呼び出すには これは、関数を動的に呼び出しており、各関数の引数の数が異なる場合に役立ちます。これは私が探していた(そして解決した)私のケースです。call_user_func_array鍵です

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

    親愛なるサラ、元気?

イェイバイ!



1

次のコードは、PHPで動的関数を作成するのに役立ちます。現在、関数名は変数「$ current_page」によって動的に変更できます。

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

無名関数(動的関数)は、変数関数(動的関数の呼び出し)とは異なります。また、$function理由もなく変数を置き換えます。
MAChitgarha

0

変数に格納された名前を使用して関数を安全に呼び出す最も簡単な方法は、

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

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

以下のように動的にLaravelに移行テーブルを作成しました、

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

-5

私が思いついた1つの型破りなアプローチは、それ自体を書き込む超超自律型AIを介してコード全体を生成しない限り、「動的に」呼び出したい関数がすでにコードで定義されている可能性が高いということです。ベース。だから、弦をチェックして悪名高いifelseダンスをして召喚するだけではどうでしょう...

例えば。

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

はしごのswitch-case淡白な味が気に入らなくても使えますifelse

関数を動的に呼び出す」ことが絶対に必要になる場合があることを理解しています(それ自体を変更する再帰ロジックのように)。しかし、日常のささいなユースケースのほとんどは、回避できます。

文字列が使用可能な関数の定義のいずれにも一致しない場合、フォールバック関数を実行する機会を与えながら、アプリケーションから多くの不確実性を取り除きます。私見では。


変数の値が既に関数の名前である場合、なぜ余分な作業を行うのですか?また、あなたがそれを実行する前に、関数は、PHPで存在するかどうかを確認するために固体のフィードバックを得るために良いでしょう: if (method_exists($functionName)) $functionName(); else //exception or handle
ネクロ

2
ポイントは有効ですが、関数名は動的であるため、ロジックでは、その関数が現在の状況に関連しているかどうかに関係なく、その名前の関数が存在する場合に来る文字列が実行されます。私のアプローチでは、特定の機能の束しか実行できないことを確認します。そうしないと、エラーがスローされます。配列などによって簡略化できます。
Mohd Abdul Mujib

-11

なぜそれを使用しなければならないのかわからないので、私にはそれほどよく聞こえませんが、関数が少量の場合は、if / elseif構文を使用できます。直接的な解決策が可能かどうかわかりません。

$ foo = "bar"のようなもの; $ test = "foo"; エコー$$ test;

バーを返す必要があります、あなたは周りを試すことができますが、これが関数でうまくいくとは思いません

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.