Як викликати функцію із рядка, що зберігається у змінній?


289

Мені потрібно мати можливість викликати функцію, але ім'я функції зберігається у змінній, чи це можливо? наприклад:

функція foo ()
{
  // код тут
}

панель функцій ()
{
  // код тут
}

$ functionName = "foo";
// Мені потрібно викликати функцію на основі $ functionName

Відповіді:


465

85
Якщо вам потрібно викликати функцію об'єкта, ім'я якого є змінною, зробіть це: call_user_func (масив ($ obj, $ func), $ params)
BlackDivine

1
Також дивіться мою відповідь нижче, якщо ви маєте справу з класами та методами. Я не очікував того, що знайшов.
Кріс К

Як я міг визнати помилку визначення функції, якщо функція не була визначена? Чи достатньо ($ functionName)?
SaidbakR

14
@ sêsêm: Ви повинні використовувати is_callableфункцію. Він перевірить, чи містить змінна щось, що можна викликати як функцію (будь то ім'я функції, масив, що містить екземпляр об'єкта та ім'я функції, або анонімну функцію / закриття)
knittl

6
@Pacerier: Вкажіть ім'я класу як перший елемент масиву:call_user_func(array('ClassName', 'method'), $args)
knittl

122

Моя улюблена версія - вбудована версія:

${"variableName"} = 12;

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

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

Ви також можете розміщувати змінні чи вирази всередині дужок!


1
@marcioAlmada: опублікуйте коментар замість редагування відповідей інших таким чином; це додає плутанини - не ясно, хто сказав що [допитливим: див. редакції для пояснень]
kryger

4
Гаразд @kryger. Рядок 2 {"functionName"}();не є законним і призведе до помилки синтаксису.
marcio

4
Дякую, хлопці, за відповіді, це дійсно видає помилку навіть у php 5.4, так що синтаксис працює лише з об'єктними методами. Редагував публікацію.
Lajos Meszaros


18

Як уже згадувалося, є кілька способів досягти цього за допомогою, можливо, найбезпечнішого методу, 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);
}

13

Якщо хтось інший приведе сюди google через те, що вони намагалися використовувати змінну для методу в класі, нижче наведений зразок коду, який насправді спрацює. Ніщо з вищезазначеного не спрацювало для моєї ситуації. Ключова відмінність полягає &в оголошенні $c = & new...та &$cпередачі call_user_func.

Мій конкретний випадок - це коли реалізувати чийсь код, що стосується кольорів та двох методів-членів lighten()та darken()класу csscolor.php. З будь-якої причини я хотів, щоб той самий код міг викликати полегшення або затемнення, а не вибирати його логікою. Це може бути наслідком моєї впертості не просто використовувати 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 = & новий CSS_Color"? Я говорю про підсилювач (&). Що це & змінює?
Данон

@danon & є "адресою" або оператором "посилання". Я підбурюю новий об'єкт класу CSS_Color, а потім присвоюю його посилання (ака-адреса) $ c. Я ненавиджу повторюваний код, тому я часто використовую імена змінних змінних.
Кріс К

1
Я не розумію. Невже об’єкти не передаються посиланнями скрізь? Я маю на увазі, якщо ви передасте функцію $ c функції та зміните її, на оригінальний об’єкт теж буде позначатися, правда? Незалежно від того, використовували ви це чи ні, я прав?
Данон

Я спробував різні комбінації символів, і лише читаючи коментарі користувачів на php.org, я зміг отримати робочий код. Вам потрібно буде розпочати цю дискусію з людьми, відповідальними за поведінку php.
Кріс К

Для другого прикладу, який ви неправильно прочитали, ви шукали call_user_func_array
Tofandel

13

Кілька років пізно, але це найкращий спосіб зараз:

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

3
Шлях до OOP для мене, але я дав йому +1, оскільки ніхто ще не згадував заняття Reflection.
Лайош Мецарос

Не розумію цієї відповіді. Яку проблему саме вирішує?
Девід Спектор

10

Для повноти ви також можете використовувати eval () :

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

Однак call_user_func()це правильний шлях.


8
Слід зазначити, що eval () дозволить таку ж поведінку, але також дозволить виконувати будь-який код PHP. Таким чином, змінна може бути використана для викрадення системи. Call_user_func () не викликає таких проблем безпеки.
Іван

4
evalне є вирішенням цього питання.
ankr

1
Не робіть цього, тому що зловмисник може повністю знищити ваш сервер.
andreszs

10

Рішення: Використовуйте PHP7

Примітка. Для узагальненої версії див. TL; DR в кінці відповіді.

Старі методи

Оновлення : Один із описаних тут старих методів видалено. Зверніться до інших відповідей для пояснення щодо інших методів, тут це не висвітлено. До речі, якщо ця відповідь не допоможе вам, вам слід повернутися до оновлення своїх речей. Підтримка PHP 5.6 закінчилася в січні 2019 року (зараз навіть PHP 7.0 і 7.1 не підтримуються). Додаткову інформацію див. У підтримуваних версіях .

Як згадували інші, у PHP5 (а також у нових версіях, таких як PHP7) ми могли б використовувати змінні як назви функцій, використання call_user_func()та call_user_func_array()(що особисто я ненавиджу ці функції) тощо.

Нові методи

Станом на PHP7, існують нові способи:

Примітка: Все, що знаходиться в <something>дужках, означає один або кілька виразів для формування чогось, наприклад, <function_name>означає вирази, що утворюють ім’я функції.

Динамічний виклик функції: ім'я функції на ходу

Ми можемо використовувати один або декілька виразів у дужках як ім'я функції лише за один перехід у вигляді:

(<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
@Necro Спасибі, я додав!
MAChitgarha

8

Імена динамічних функцій та простори імен

Просто додайте крапку про імена динамічних функцій при використанні просторів імен.

Якщо ви використовуєте простори імен, наступне не працюватиме, за винятком випадків, коли ваша функція знаходиться у глобальному просторі імен:

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

4

Доповнюючи відповідь @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"

Я розмістив тут ще один приклад


4

Що я дізнався з цього питання та відповідей. Дякую усім!

Скажімо, у мене є ці змінні та функції:

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

    Дорога Сара, як справи?

До побачення!



2

Наступний код може допомогти написати динамічну функцію в PHP. тепер ім'я функції можна динамічно змінити змінною '$ current_page'.

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

Анонімні функції (тобто динамічні функції) відрізняються від змінних функцій (тобто викликають функції динамічно). Крім того, ви замінюєте $functionзмінну без будь-яких причин.
MAChitgarha

1

Найпростіший спосіб безпечно викликати функцію за допомогою імені, збереженого в змінній,

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

-4

Один нетрадиційний підхід, який мені прийшов на думку, полягає в тому, що, якщо ви не генеруєте весь код через якийсь супер ультра автономний AI, який пише сам , є великі шанси, що функції, які ви хочете "динамічно" викликати, вже визначені у вашому коді база. То чому б просто не перевірити на струну і не зробити сумнозвісний ifelseтанець, щоб викликати ... ви зрозумієте мою думку.

напр.

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

Навіть switch-caseможна використовувати, якщо вам не подобається ніжний смак ifelseсходів.

Я розумію, що є випадки, коли " динамічно викликає функцію " було б абсолютно необхідним ( як і деяка рекурсивна логіка, яка сама модифікує ). Але більшість випадкових тривіальних випадків використання можна просто ухилитись.

Це усуває велику невпевненість у вашій програмі, надаючи вам можливість виконати резервну функцію, якщо рядок не відповідає жодному з доступних визначень функцій. ІМХО.


Якщо значення змінної вже є назвою функції, чому додаткова робота? також було б добре отримати зворотній зв'язок, щоб дізнатися, чи існує функція php, перш ніж її запустити: if (method_exists($functionName)) $functionName(); else //exception or handle
Necro

2
Так, ваша точка дійсна, але оскільки ім'я функції є динамічним, у вашій логіці будь-яка рядок, що надходить, виконується, якщо функція з цим іменем існує, незалежно від того, чи є ця функція актуальною в поточній ситуації чи ні. Мій підхід гарантує, що можна виконати лише певну купу функцій, інакше помилка може бути викинута. Це може бути спрощене масивами і як таке.
Мохд Абдул Муджіб

-10

Я не знаю, чому ви повинні використовувати це, не звучить мені так добре, але якщо є лише невелика кількість функцій, ви можете використовувати конструкцію if / elseif. Я не знаю, чи можливе пряме рішення.

щось на зразок $ foo = "бар"; $ test = "foo"; echo $$ test;

слід повернути панель, ви можете спробувати, але я не думаю, що це буде працювати для функцій

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.