Como chamar uma função de uma string armazenada em uma variável?

288

Eu preciso ser capaz de chamar uma função, mas o nome da função é armazenado em uma variável, isso é possível? por exemplo:

função foo ()
{
  // código aqui
}

barra de funções ()
{
  // código aqui
}

$ functionName = "foo";
// preciso chamar a função com base no que é $ functionName
Lagarto
fonte

Respostas:

464

$functionName() ou call_user_func($functionName)

knittl
fonte
85
Se você precisa chamar a função de um objeto cujo nome é em uma variável de fazer isso: call_user_func (array ($ obj, $ func), $ params)
BlackDivine
1
Veja também minha resposta abaixo se você estiver lidando com classes e métodos. Eu não esperava o que encontrei.
Chris K
Como anular o erro de definição de função se a função não foi definida? Se ($ functionName) é suficiente?
precisa saber é o seguinte
14
@ sємsєм: Você deve usar a is_callablefunção. Ele irá verificar se uma variável contém algo que pode ser chamado como uma função de (ser-lhe o nome de uma função, uma matriz que contém uma instância de objecto e um nome de função, ou uma função / fecho anónimo)
knittl
6
@Pacerier: Fornecer o nome de classe como primeiro elemento do array:call_user_func(array('ClassName', 'method'), $args)
knittl
121

Minha versão favorita é a versão embutida:

${"variableName"} = 12;

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

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

Você também pode colocar variáveis ​​ou expressões dentro dos colchetes!

Lajos Meszaros
fonte
1
@marcioAlmada: poste um comentário em vez de editar as respostas de outras pessoas dessa maneira; acrescenta confusão - não está claro quem disse o quê [para os curiosos: ver revisões para explicações]
Kryger
4
Ok @kryger. A linha 2 {"functionName"}();não é legal e gera um erro de sintaxe.
marcio
4
Obrigado pessoal, pelas respostas, isso realmente gera um erro, mesmo no php 5.4, para que a sintaxe funcione apenas com métodos de objeto. Editou a postagem.
Lajos Meszaros
17

Como já mencionado, existem algumas maneiras de conseguir isso, possivelmente com o método mais seguro call_user_func()ou, se necessário, você também pode seguir o caminho $function_name(). É possível passar argumentos usando esses dois métodos, assim

$function_name = 'foobar';

$function_name(arg1, arg2);

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

Se a função que você está chamando pertencer a um objeto, você ainda poderá usar um destes

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

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

No entanto, se você usar o $function_name()método, pode ser uma boa ideia testar a existência da função se o nome for dinâmico de alguma forma

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

Caso alguém seja trazido aqui pelo google porque eles estavam tentando usar uma variável para um método dentro de uma classe, a seguir é um exemplo de código que realmente funcionará. Nenhuma das opções acima funcionou para a minha situação. A principal diferença está &na declaração $c = & new...e &$cna transmissão call_user_func.

Meu caso específico é ao implementar o código de alguém que tem a ver com cores e métodos de dois membros lighten()e darken()da classe csscolor.php. Por qualquer motivo, eu queria que o mesmo código pudesse chamar clarear ou escurecer, em vez de selecioná-lo com lógica. Esse pode ser o resultado da minha teimosia em não apenas usar if-else ou alterar o código que chama esse método.

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

Observe que tentar algo com $c->{...}não funcionou. Ao ler atentamente o conteúdo fornecido pelo leitor na parte inferior da página do php.net, pude juntar os itens call_user_funcacima. Além disso, observe que, $paramscomo uma matriz não funcionou para mim:

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

Essa tentativa acima daria um aviso sobre o método que espera um segundo argumento (por cento).

Chris K
fonte
o que "$ c = & new CSS_Color" faz? Eu estou falando sobre o amplificador (&). O que isso muda?
Danon
@danon & é "endereço de" ou o operador "referência". Estou instanciando um novo objeto da classe CSS_Color e depois atribuindo sua referência (também conhecida como endereço) a $ c. Eu odeio repetir código, então geralmente uso nomes de variáveis ​​variáveis.
Chris K
1
Eu não entendo De qualquer maneira, os objetos não passam por referência em todos os lugares? Quero dizer, se você passar um $ c para uma função e alterá-lo, o objeto original também será afetado, certo? Não importa se você usou isso ou não, estou certo?
Danon
Tentei diferentes combinações de símbolos e somente ao ler os comentários do usuário no php.org eu consegui obter o código de trabalho. Você precisará iniciar essa discussão com pessoas responsáveis ​​pelo comportamento do php.
Chris K
Para o segundo exemplo que você
leu
12

Alguns anos atrasado, mas esta é a melhor maneira agora imho:

$x = (new ReflectionFunction("foo"))->getClosure();
$x();
Erro do Servidor Interno
fonte
3
Maneira de fazer POO para mim, mas dei +1, pois ninguém mencionou as aulas de reflexão ainda.
Lajos Meszaros
Não entendo esta resposta. Que problema está resolvendo exatamente?
David Spector
9

Por uma questão de integridade, você também pode usar eval () :

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

No entanto, call_user_func()é o caminho certo.

karim79
fonte
8
Deve-se notar que eval () permitirá o mesmo comportamento, mas também permitirá executar qualquer código PHP. Portanto, a variável pode ser usada para seqüestrar o sistema. O call_user_func () não traz esses problemas de segurança.
John John
4
evalnão é uma solução para esta pergunta.
Ankr
1
Por favor, não faça isso porque um invasor pode acabar com o servidor completamente.
andreszs
9

Solução: Use PHP7

Nota: Para uma versão resumida, consulte TL; DR no final da resposta.

Métodos antigos

Atualização : um dos métodos antigos explicados aqui foi removido. Consulte outras respostas para obter explicação sobre outros métodos, que não são abordados aqui. A propósito, se esta resposta não ajudar, você deve retornar atualizando suas coisas. O suporte ao PHP 5.6 terminou em janeiro de 2019 (agora nem o PHP 7.0 e 7.1 estão sendo suportados). Veja as versões suportadas para mais informações.

Como já mencionado, no PHP5 (e também em versões mais recentes como o PHP7), poderíamos usar variáveis ​​como nomes de funções, use call_user_func()ecall_user_func_array() (que, pessoalmente, odeio essas funções), etc.

Novos Métodos

A partir do PHP7, existem novas maneiras introduzidas:

Nota: Tudo <something>entre parênteses significa uma ou mais expressões para formar algo, por exemplo<function_name> significa formar um nome de função.

Chamada de Função Dinâmica: Nome da Função On-the-fly

Podemos usar uma ou mais expressões entre parênteses como o nome da função de uma só vez, na forma de:

(<function_name>)(arguments);

Por exemplo:

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

Nota: Embora remover os parênteses str_replace()não seja um erro, colocar parênteses torna o código mais legível. No entanto, você não pode fazer isso algumas vezes, por exemplo, enquanto estiver usando. operador. Para ser consistente, recomendo que você coloque os parênteses sempre.

Chamada de método dinâmico: nome do método On-the-fly

Assim como as chamadas de função dinâmica, podemos fazer o mesmo com as chamadas de método, cercadas por chaves em vez de parênteses (para formas extras, navegue até a seção TL; DR):

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

Veja em um exemplo:

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

$bar = "another thing";

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

Chamada de método dinâmico: a sintaxe da matriz

Uma maneira mais elegante adicionada no PHP7 é a seguinte:

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

Como um exemplo:

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

Nota: O benefício de usar esse método em relação ao anterior é que você não se importa com o tipo de chamada (por exemplo, se é estático ou não).

Exemplo extra: usando classes anônimas

Para tornar as coisas um pouco complicadas, você pode usar uma combinação de classes anônimas e os recursos acima:

$bar = "SomeThing";

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

TL; DR (Conclusão)

Geralmente, no PHP7, é possível usar os seguintes formulários:

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

Baseado principalmente nesta conversa sobre PHP .

MAChitgarha
fonte
1
Boa lista de expressões, também inclui:(expressions)->$bar()
Necro
1
@ Necro Obrigado, eu adicionei!
precisa saber é o seguinte
7

Nomes de funções dinâmicas e espaços para nome

Apenas para adicionar um ponto sobre nomes de funções dinâmicas ao usar espaços para nome.

Se você estiver usando espaços para nome, o seguinte não funcionará, exceto se sua função estiver no espaço para nome global:

namespace greetings;

function hello()
{
    // do something
}

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

O que fazer?

Você precisa usar 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);
Jivan
fonte
3

Complementando a resposta de @Chris K se você quiser chamar o método de um objeto, você pode chamá-lo usando uma única variável com a ajuda de um fechamento:

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"

Eu postei outro exemplo aqui

David
fonte
3

O que aprendi com esta pergunta e as respostas. Obrigado a todos!

Digamos que eu tenho essas variáveis ​​e funções:

$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. Para chamar função sem argumentos

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

    Olá!

  2. Para chamar função com 1 argumento

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

    Caro John, olá!

  3. Para chamar a função com 1 ou mais argumentos Isso será útil se você estiver chamando dinamicamente suas funções e cada função tiver um número diferente de argumentos. Este é o meu caso que eu tenho procurado (e resolvido). call_user_func_arrayÉ a chave

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

    Querida Sarah, como você está?

Yay tchau!

asmasyakirah
fonte
2

Use a função call_user_func.

PaulJWilliams
fonte
1

O código a seguir pode ajudar a escrever a função dinâmica no PHP. agora o nome da função pode ser alterado dinamicamente pela variável '$ current_page'.

$current_page = 'home_page';
$function = @${$current_page . '_page_versions'};
$function = function() {
    echo 'current page';
};
$function();
rakesh.falke
fonte
As funções anônimas (ou seja, funções dinâmicas) diferem das funções variáveis (ou seja, chamar funções dinamicamente). Além disso, você está substituindo a $functionvariável sem nenhum motivo.
MAChitgarha
0

A maneira mais fácil de chamar uma função com segurança usando o nome armazenado em uma variável é,

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

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

Eu usei como abaixo para criar tabelas de migração dinamicamente no Laravel,

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

Uma abordagem não convencional, que me veio à mente, é menos que você esteja gerando todo o código por meio de uma IA super ultra autônoma que se escreve , há grandes chances de que as funções que você deseja "dinamicamente" chamar já estejam definidas no seu código base. Então, por que não apenas verificar a corda e fazer o infameifelse dança para convocar o ... você entendeu o meu ponto.

por exemplo.

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

Até switch-case pode ser usado se você não gostar do sabor suave da ifelseescada.

Entendo que há casos em que a " chamada dinâmica da função " seria uma necessidade absoluta ( como uma lógica recursiva que se modifica ). Mas a maioria dos casos de uso triviais do dia a dia pode ser esquivada.

Ele elimina muita incerteza do seu aplicativo, enquanto oferece a chance de executar uma função de fallback se a string não corresponder a nenhuma das definições de funções disponíveis. NA MINHA HUMILDE OPINIÃO.

Mohd Abdul Mujib
fonte
Se o valor da variável já é o nome da função, por que o trabalho extra funciona? também seria bom obter um feedback sólido para ver se a função existe pelo php antes de executá-lo: if (method_exists($functionName)) $functionName(); else //exception or handle
Necro
2
Sim, seu argumento é válido, mas como o nome da função é dinâmico, em sua lógica, qualquer string que vier será executada se existir uma função com esse nome, independentemente de essa função ser relevante na situação atual ou não. Minha abordagem garante que apenas um determinado grupo de funções possa ser executado, caso contrário, um erro pode ser gerado. Pode ser simplificado por matrizes e, como tal.
Mohd Abdul Mujib
-11

Eu não sei por que você tem que usar isso, não parece tão bom para mim, mas se houver apenas uma pequena quantidade de funções, você pode usar uma construção if / elseif. Não sei se é possível uma solução direta.

algo como $ foo = "bar"; $ test = "foo"; teste de eco $$;

deve retornar bar, você pode tentar, mas eu não acho que isso funcionará para funções

Flo
fonte