PHP 8.5.0 RC4 available for testing

Callables

Un callable est une référence à une fonction ou une méthode qui est passée à une autre fonction en tant qu'argument. Elles sont représentées avec la déclaration de type callable.

<?php
function foo(callable $callback) {
$callback();
}
?>

Certaines fonctions acceptent des fonctions de rappel en tant que paramètre, par exemple array_map(), usort(), ou preg_replace_callback().

Création de callables

Un callable est un type qui représente quelque chose qui peut être invoqué. Les callables peuvent être passés en tant qu'arguments aux fonctions ou méthodes qui attendent un paramètre callback ou ils peuvent être invoqués directement. Le type callable ne peut pas être utilisé comme déclaration de type pour les propriétés. A la place, utilisez une déclaration de type Closure.

Les callables peuvent être crées de plusieurs façons différentes :

Un object Closure peut être créer en utilisant la syntaxe function anonyme, la syntaxe function fléchée, la syntaxe callable de première classe, ou la méthode Closure::fromCallable().

Note: La syntaxe callable de première classe est disponible uniquement à partir de PHP 8.1.0.

Exemple #1 Exemple de callback utilisant une Closure

<?php
// Utilisant la syntaxe de fonction anonyme
$double1 = function ($a) {
return
$a * 2;
};

// Utilisant la syntaxe callable de première classe
function double_function($a) {
return
$a * 2;
}
$double2 = double_function(...);

// Utilisant la syntaxe de fonction fléchée
$double3 = fn($a) => $a * 2;

// Utilisant la méthode Closure::fromCallable
$double4 = Closure::fromCallable('double_function');

// Utilisant la closure comme callback pour
// doubler la taille de chaque élément dans notre range
$new_numbers = array_map($double1, range(1, 5));
print
implode(' ', $new_numbers) . PHP_EOL;

$new_numbers = array_map($double2, range(1, 5));
print
implode(' ', $new_numbers) . PHP_EOL;

$new_numbers = array_map($double3, range(1, 5));
print
implode(' ', $new_numbers) . PHP_EOL;

$new_numbers = array_map($double4, range(1, 5));
print
implode(' ', $new_numbers);

?>

Résultat de l'exemple ci-dessus en PHP 8.1 :

2 4 6 8 10
2 4 6 8 10
2 4 6 8 10
2 4 6 8 10

Un callable peut également être une string contenant le nom d'une fonction ou une méthode statique. N'importe quelle fonction intégrée ou définie par l'utilisateur peut être utilisée, à l'exception des constructions de langage telles que : array(), echo, empty(), eval(), isset(), list(), print ou unset().

Les méthodes de classes statiques peuvent être utilisées sans instancier un object de cette classe en créant soit un tableau avec le nom de la classe à l'index 0 et le nom de la méthode à l'index 1, ou en utilisant la syntaxe spéciale avec l'opérateur de résolution de portée ::, comme dans 'ClassName::methodName'.

Une méthode d'un object instancié peut être un callable lorsqu'elle est fournie sous forme de tableau avec le object à l'index 0 et le nom de la méthode à l'index 1.

La principale différence entre un objet Closure et le type callable type est que l'objet Closure est indépendant du scope et peut tous le temps est invoqué, alors qu'un type callable peut être dépendant du scope et ne peut être invoqué directement. Closure est la méthode préférée pour créer des callables.

Note: Tandis que les objets Closure sont liés au scope où ils sont créés, les callables référencant des méthodes de classes sous forme de chaînes de caractères ou de tableaux sont résolus dans le scope où ils sont appelés. Pour créer un callable à partir d'une méthode privée ou protégée, qui peut ensuite être invoqué depuis l'extérieur de la classe, utilisez Closure::fromCallable() ou la syntaxe callable de première classe

PHP permet la création de callables qui peuvent être utilisés en tant qu'argument de callback mais qui ne peuvent pas être appelés directement. Il s'agit de callables dépendants du contexte qui référencent une méthode de classe dans la hiérarchie d'héritage d'une classe, par exemple 'parent::method' ou ["static", "method"].

Note: Depuis PHP 8.2.0, les callables dépendants du contexte sont dépréciés. Supprimez la dépendance au contexte en remplaçant 'parent::method' par parent::class . '::method' ou utilisez la syntaxe callable de première classe.

Exemple #2 Appel de différents types de callables avec call_user_function()

<?php

// Un exemple de fonction de rappel
function my_callback_function() {
echo
'hello world!', PHP_EOL;
}

// Un exemple de méthode de rappel
class MyClass {
static function
myCallbackMethod() {
echo
'Hello World!', PHP_EOL;
}
}

// Type 1: Callback simple
call_user_func('my_callback_function');

// Type 2: Appel de méthode de classe statique
call_user_func(['MyClass', 'myCallbackMethod']);

// Type 3: Appel de méthode d'objet
$obj = new MyClass();
call_user_func([$obj, 'myCallbackMethod']);

// Type 4: Appel de méthode de classe statique
call_user_func('MyClass::myCallbackMethod');

// Type 5: Appel de méthode de classe statique en utilisant le mot-clé ::class
call_user_func([MyClass::class, 'myCallbackMethod']);

// Type 6: Appel de méthode de classe statique relative
class A {
public static function
who() {
echo
'A', PHP_EOL;
}
}

class
B extends A {
public static function
who() {
echo
'B', PHP_EOL;
}
}

call_user_func(['B', 'parent::who']); // deprecated as of PHP 8.2.0

// Type 7: Les objets implémentant __invoke peuvent être utilisés en tant que callables
class C {
public function
__invoke($name) {
echo
'Hello ', $name;
}
}

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

L'exemple ci-dessus va afficher :

hello world!
Hello World!
Hello World!
Hello World!
Hello World!

Deprecated: Callables of the form ["B", "parent::who"] are deprecated in script on line 41
A
Hello PHP!

Note:

Notez que les fonctions de rappel enregistrées avec des fonctions comme call_user_func() et call_user_func_array() ne seront pas appelées si une exception n'est pas interceptée alors qu'elle a été lancée dans une précédente fonction de rappel.

add a note

User Contributed Notes 12 notes

up
280
andrewbessa at gmail dot com
13 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
207
steve at mrclay dot org
13 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
193
computrius at gmail dot com
12 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
184
Riikka K
10 years 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
185
edanschwartz at gmail dot com
10 years 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
175
metamarkers at gmail dot com
12 years ago
you can pass an object as a callable if its class defines the __invoke() magic method..
up
118
mariano dot REMOVE dot perez dot rodriguez at gmail dot com
10 years 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
24
bradyn at NOSPAM dot bradynpoulsen dot com
9 years 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);
// => [..., ..., ...]
up
11
InvisibleSmiley
4 years ago
If you pass a callable method to a function with a callable type declaration, the error message is misleading:

<?php
class X {
protected function
foo(): void {}
}

function
bar(callable $c) {}

$x = new X;
$c = [$x, 'foo'];
bar($c);
?>

Error message will be something like "Argument #1 ($c) must be of type callable, array given" while the actual problem here is only the visibility of method "foo". All you need to do is changing it to public (or use a different approach, e.g. with a Closure).
up
6
gulaschsuppe2 at gmail dot com
6 years ago
I tried many possible ways of calling functions by function name directly and assigned to a variable on 3v4l. Not mentioned yet, it is possible to use an array as a caller, at least since PHP 7.1.25. The following script contains all the information I gained:

<?php

// Call function via function name:
// Basics:
// A function can also be called by using its string name:
function callbackFunc() {
echo
'Hello World';
}

'callbackFunc'(); // Hello World

// A function can also be called if its name is assigned to a variable:
function callbackFunc() {
echo
'Hello World';
}

$funcName = 'callbackFunc';
$funcName(); // Hello World

// Static class method:
// It is also possible to call a public static class method via 'ClassName::functioName' notation:
class A {
public static function
callbackMethod() {
echo
"Hello World\n";
}
}
'A::callbackMethod'(); // Hello World

$funcName = 'A::callbackMethod';
$funcName(); // Hello World

// Non static class method:
// It is also possible to call non static class methods by creating an array which first element is the object the method should be called on and the second element is the non static method to be called. The array can directly be used as a caller:
class A {
private
$prop = "Hello World\n";

public function
callbackMethod() {
echo
$this->prop;
}
}

$a = new A;
[
$a, 'callbackMethod']();
$funcCallArr = [$a, 'callbackMethod'];
$funcCallArr();

// Of course this also works inside the class with '$this':
class A {
private function
privCallback() {
echo
'Private';
}

public function
privCallbackCaller($funcName) {
[
$this, $funcName]();
}
}

(new
A)->privCallbackCaller('privCallback'); // Private

?>
up
2
pawel dot tadeusz dot niedzielski at gmail dot com
9 years 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
0
chris dot rutledge at gmail dot com
6 years ago
Having read this line in the manual above,

"A method of an instantiated object is passed as an array containing an object at index 0 and the method name at index 1. Accessing protected and private methods from within a class is allowed."

I decided to do some testing to see if I could access private methods using the call_user_func methods. Thankfully not, but for completeness here is my test which also covers using static and object contexts

<?php
class foo {

public static
$isInstance = false;

public function
__construct() {
self::$isInstance = true;
}

public function
bar() {
var_dump(self::$isInstance);
echo
__METHOD__;
}

private function
baz() {
var_dump(self::$isInstance);
echo
__METHOD__;
}

public function
qux() {
$this->baz();
}

public function
quux() {
self::baz();
}
}

call_user_func(['foo','bar']); //fase, foo:bar

call_user_func(['foo','baz']); //warning, cannot access private method

call_user_func(['foo','quux']); //false, foo::baz

call_user_func(['foo','qux']); //fatal, Using $this when not in object context

$foo = new foo;

call_user_func([$foo,'bar']); //true, foo::bar
call_user_func([$foo,'baz']); //warning, cannot access private method
call_user_func([$foo,'qux']); //true, foo::baz

call_user_func(['foo','bar']); //true, foo::bar (static call, yet $isInstance is true)

?>
To Top