CoderCruise

Callbacks / Callables

Callbacks podem ser representadas pela declaração de tipo callable a partir do PHP 5.4. Esta documentação utiliza o tipo callback para referenciar a mesma coisa.

Algumas funções como call_user_func() ou usort() aceitam funções de callback definidas pelo usuário como parâmetro. Funções de callback não precisam ser apenas funções simples, mas também métodos de objetos, incluindo os estáticos.

Passagem por parâmetro

Uma função PHP é passada através do seu nome como uma string. Qualquer função interna ou definida pelo usuário pode ser utilizada, com exceção dos construtores de linguagem como: array(), echo, empty(), eval(), exit(), isset(), list(), print ou unset().

Um método de um objeto instanciado é passado como um array contendo um objeto no índice 0 e o nome do método no índice 1. Acessar métodos protegidos e privados de uma classe é permitido.

Métodos estáticos também podem ser passados sem a instância de um objeto dessa classe ao passar o nome da classe ao invés de um objeto no índice 0. A partir do PHP 5.2.3, também é possível utilizar 'NomeDaClasse::nomeDoMetodo'.

Além de funções comuns definidas pelo usuário, funções anônimas também podem ser passadas como um parâmetro de callback.

Exemplo #1 Exemplos de funções de callback

<?php

// Exemplo de função de callback
function my_callback_function() {
    echo 
'hello world!';
}

// Exemplo de método de callback
class MyClass {
    static function 
myCallbackMethod() {
        echo 
'Hello World!';
    }
}

// Tipo 1: Calback simples
call_user_func('my_callback_function');

// Tipo 2: Chamada à métodos estáticos
call_user_func(array('MyClass''myCallbackMethod'));

// Tipo 3: Chamada à metodos de objetos
$obj = new MyClass();
call_user_func(array($obj'myCallbackMethod'));

// Tipo 4: Chamada à métodos estáticos (a partir do PHP 5.2.3)
call_user_func('MyClass::myCallbackMethod');

// Tipo 5: Chamada relativa à métodos estáticos (a partir do PHP 5.3.0)
class {
    public static function 
who() {
        echo 
"A\n";
    }
}

class 
extends {
    public static function 
who() {
        echo 
"B\n";
    }
}

call_user_func(array('B''parent::who')); // A

// Type 6: Objetos que implementam __invoke podem sere utilizados como callables (desde o PHP 5.3)
class {
    public function 
__invoke($name) {
        echo 
'Hello '$name"\n";
    }
}

$c = new C();
call_user_func($c'PHP!');
?>

Exemplo #2 Exemplo de Callback utilizando uma Closure

<?php
// Nossa closure
$double = function($a) {
    return 
$a 2;
};

// Este é a série de números
$numbers range(15);

// O uso da closure aqui para
// dobrar o valor de cada elemento de nossa
// série
$new_numbers array_map($double$numbers);

print 
implode(' '$new_numbers);
?>

O exemplo acima irá imprimir:

2 4 6 8 10

Nota: No PHP 4, era necessário o uso de referência para criar um callback que realmente aponta para o objeto, e não para uma cópia dele. Para maiores detalhes, veja Explicação sobre referências.

Nota:

Callbacks registrados com funções como call_user_func() e call_user_func_array() não serão chamados caso haja uma exceção não-pega lançada em um callback anterior.

add a note add a note

User Contributed Notes 10 notes

up
159
Riikka K
1 year ago
A note on differences when calling callbacks as "variable functions" without the use of call_user_func() (e.g. "<?php $callback = 'printf'; $callback('Hello World!') ?>"):

- Using the name of a function as string has worked since at least 4.3.0
- Calling anonymous functions and invokable objects has worked since 5.3.0
- Using the array structure [$object, 'method'] has worked since 5.4.0

Note, however, that the following are not supported when calling callbacks as variable functions, even though they are supported by call_user_func():

- Calling static class methods via strings such as 'foo::doStuff'
- Calling parent method using the [$object, 'parent::method'] array structure

All of these cases are correctly recognized as callbacks by the 'callable' type hint, however. Thus, the following code will produce an error "Fatal error: Call to undefined function foo::doStuff() in /tmp/code.php on line 4":

<?php
class foo {
    static function
callIt(callable $callback) {
       
$callback();
    }
   
    static function
doStuff() {
        echo
"Hello World!";
    }
}

foo::callIt('foo::doStuff');
?>

The code would work fine, if we replaced the '$callback()' with 'call_user_func($callback)' or if we used the array ['foo', 'doStuff'] as the callback instead.
up
186
edanschwartz at gmail dot com
1 year ago
You can use 'self::methodName' as a callable, but this is dangerous. Consider this example:

<?php
class Foo {
    public static function
doAwesomeThings() {
       
FunctionCaller::callIt('self::someAwesomeMethod');
    }

    public static function
someAwesomeMethod() {
       
// fantastic code goes here.
   
}
}

class
FunctionCaller {
    public static function
callIt(callable $func) {
       
call_user_func($func);
    }
}

Foo::doAwesomeThings();
?>

This results in an error:
Warning: class 'FunctionCaller' does not have a method 'someAwesomeMethod'.

For this reason you should always use the full class name:
<?php
FunctionCaller
::callIt('Foo::someAwesomeMethod');
?>

I believe this is because there is no way for FunctionCaller to know that the string 'self' at one point referred to to `Foo`.
up
193
steve at mrclay dot org
4 years ago
Performance note: The callable type hint, like is_callable(), will trigger an autoload of the class if the value looks like a static method callback.
up
177
computrius at gmail dot com
3 years ago
When specifying a call back in array notation (ie. array($this, "myfunc") ) the method can be private if called from inside the class, but if you call it from outside you'll get a warning:

<?php

class mc {
   public function
go(array $arr) {
      
array_walk($arr, array($this, "walkIt"));
   }

   private function
walkIt($val) {
       echo
$val . "<br />";
   }

    public function
export() {
        return array(
$this, 'walkIt');
    }
}

$data = array(1,2,3,4);

$m = new mc;
$m->go($data); // valid

array_walk($data, $m->export()); // will generate warning

?>

Output:
1<br />2<br />3<br />4<br />
Warning: array_walk() expects parameter 2 to be a valid callback, cannot access private method mc::walkIt() in /in/tfh7f on line 22
up
198
andrewbessa at gmail dot com
4 years ago
You can also use the $this variable to specify a callback:

<?php
class MyClass {

    public
$property = 'Hello World!';

    public function
MyMethod()
    {
       
call_user_func(array($this, 'myCallbackMethod'));
    }

    public function
MyCallbackMethod()
    {
        echo
$this->property;
    }

}
?>
up
173
metamarkers at gmail dot com
3 years ago
you can pass an object as a callable if its class defines the __invoke() magic method..
up
158
Yzmir Ramirez
2 years ago
> As of PHP 5.2.3, it is also possible to pass 'ClassName::methodName'

You can also use 'self::methodName'.  This works in PHP 5.2.12 for me.
up
61
mariano dot REMOVE dot perez dot rodriguez at gmail dot com
1 year ago
I needed a function that would determine the type of callable being passed, and, eventually,
normalized it to some extent. Here's what I came up with:

<?php

/**
* The callable types and normalizations are given in the table below:
*
*  Callable                        | Normalization                   | Type
* ---------------------------------+---------------------------------+--------------
*  function (...) use (...) {...}  | function (...) use (...) {...}  | 'closure'
*  $object                         | $object                         | 'invocable'
*  "function"                      | "function"                      | 'function'
*  "class::method"                 | ["class", "method"]             | 'static'
*  ["class", "parent::method"]     | ["parent of class", "method"]   | 'static'
*  ["class", "self::method"]       | ["class", "method"]             | 'static'
*  ["class", "method"]             | ["class", "method"]             | 'static'
*  [$object, "parent::method"]     | [$object, "parent::method"]     | 'object'
*  [$object, "self::method"]       | [$object, "method"]             | 'object'
*  [$object, "method"]             | [$object, "method"]             | 'object'
* ---------------------------------+---------------------------------+--------------
*  other callable                  | idem                            | 'unknown'
* ---------------------------------+---------------------------------+--------------
*  not a callable                  | null                            | false
*
* If the "strict" parameter is set to true, additional checks are
* performed, in particular:
*  - when a callable string of the form "class::method" or a callable array
*    of the form ["class", "method"] is given, the method must be a static one,
*  - when a callable array of the form [$object, "method"] is given, the
*    method must be a non-static one.
*
*/
function callableType($callable, $strict = true, callable& $norm = null) {
  if (!
is_callable($callable)) {
    switch (
true) {
      case
is_object($callable):
       
$norm = $callable;
        return
'Closure' === get_class($callable) ? 'closure' : 'invocable';
      case
is_string($callable):
       
$m    = null;
        if (
preg_match('~^(?<class>[a-z_][a-z0-9_]*)::(?<method>[a-z_][a-z0-9_]*)$~i', $callable, $m)) {
          list(
$left, $right) = [$m['class'], $m['method']];
          if (!
$strict || (new \ReflectionMethod($left, $right))->isStatic()) {
           
$norm = [$left, $right];
            return
'static';
          }
        } else {
         
$norm = $callable;
          return
'function';
        }
        break;
      case
is_array($callable):
       
$m = null;
        if (
preg_match('~^(:?(?<reference>self|parent)::)?(?<method>[a-z_][a-z0-9_]*)$~i', $callable[1], $m)) {
          if (
is_string($callable[0])) {
            if (
'parent' === strtolower($m['reference'])) {
              list(
$left, $right) = [get_parent_class($callable[0]), $m['method']];
            } else {
              list(
$left, $right) = [$callable[0], $m['method']];
            }
            if (!
$strict || (new \ReflectionMethod($left, $right))->isStatic()) {
             
$norm = [$left, $right];
              return
'static';
            }
          } else {
            if (
'self' === strtolower($m['reference'])) {
              list(
$left, $right) = [$callable[0], $m['method']];
            } else {
              list(
$left, $right) = $callable;
            }
            if (!
$strict || !(new \ReflectionMethod($left, $right))->isStatic()) {
             
$norm = [$left, $right];
              return
'object';
            }
          }
        }
        break;
    }
   
$norm = $callable;
    return
'unknown';
  }
 
$norm = null;
  return
false;
}

?>

Hope someone else finds it useful.
up
4
pawel dot tadeusz dot niedzielski at gmail dot com
7 months ago
@edanschwartz at gmail dot com

You can use ::class property to always indicate the class you're in when using static methods:

<?php
class Foo {
    public static function
doAwesomeThings() {
       
FunctionCaller::callIt(self::class . '::someAwesomeMethod');
    }

    public static function
someAwesomeMethod() {
       
// fantastic code goes here.
   
}
}

class
FunctionCaller {
    public static function
callIt(callable $func) {
       
call_user_func($func);
    }
}

Foo::doAwesomeThings();
?>
up
2
bradyn at NOSPAM dot bradynpoulsen dot com
7 months ago
When trying to make a callable from a function name located in a namespace, you MUST give the fully qualified function name (regardless of the current namespace or use statements).

<?php

namespace MyNamespace;

function
doSomethingFancy($arg1)
{
   
// do something...
}

$values = [1, 2, 3];

array_map('doSomethingFancy', $values);
// array_map() expects parameter 1 to be a valid callback, function 'doSomethingFancy' not found or invalid function name

array_map('MyNamespace\doSomethingFancy', $values);
// => [..., ..., ...]
To Top