Callbacks / Callables
From; https://www.php.net/manual/en/language.types.callable.php
Callbacks / Callables
Callbacks can be denoted by the callable type declaration.
Some functions like call_user_func() or usort() accept user-defined callback
functions as a parameter. Callback functions can not only be simple
functions, but also object methods, including static class methods.
Passing
A PHP function is passed by its name as a string. Any built-in or user-defined
function can be used, except language constructs such as: array(), echo,
empty(), eval(), exit(), isset(), list(), print or unset().
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.
Static class methods can also be passed without instantiating an object of
that class by either, passing the class name instead of an object at index
0, or passing 'ClassName::methodName'.
Apart from common user-defined function, anonymous functions and arrow
functions can also be passed to a callback parameter.
Note:
As of PHP 8.1.0, anonymous functions can also be created using the first class
callable syntax.
Generally, any object implementing __invoke() can also be passed to a
callback parameter.
Example #1
Callback function examples
<?php
function my_callback_function() // AN EXAMPLE CALLBACK FUNCTION
{
echo 'hello world!';
}
class MyClass // AN EXAMPLE CALLBACK METHOD
{
static function myCallbackMethod()
{
echo 'Hello World!';
}
}
call_user_func( 'my_callback_function' ); // TYPE 1: SIMPLE CALLBACK
// TYPE 2: STATIC CLASS METHOD CALL
call_user_func( array( 'MyClass', 'myCallbackMethod'));
$obj = new MyClass(); // TYPE 3: OBJECT METHOD CALL
call_user_func( array( $obj, 'myCallbackMethod'));
// TYPE 4: STATIC CLASS METHOD CALL
call_user_func( 'MyClass::myCallbackMethod');
class A // TYPE 5: RELATIVE STATIC CLASS METHOD CALL
{
public static function who()
{
echo "A\n";
}
}
class B extends A
{
public static function who()
{
echo "B\n";
}
}
call_user_func( array( 'B', 'parent::who'));// A, DEPRECATED AS OF PHP 8.2.0
// TYPE 6: OBJECTS IMPLEMENTING __INVOKE CAN BE USED AS CALLABLES
class C
{
public function __invoke($name)
{
echo 'Hello ', $name, "\n";
}
}
$c = new C();
call_user_func($c, 'PHP!');
?>
Example #2 Callback example using a Closure
<?php
$double = function( $a ) // OUR CLOSURE
{
return $a * 2;
};
$numbers = range( 1, 5 ); // THIS IS OUR RANGE OF NUMBERS
// USE CLOSURE AS A CALLBACK HERE TO DOUBLE SIZE OF EACH ELEMENT IN RANGE
$new_numbers = array_map($double, $numbers);
print implode( ' ', $new_numbers);
?>
The above example will output:
2 4 6 8 10
Note:
Callbacks registered with functions such as call_user_func() and
call_user_func_array() will not be called if there is an uncaught exception
thrown in a previous callback.
User Contributed Notes 19 notes
andrewbessa at gmail dot com 10 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;
}
}
?>
steve at mrclay dot org 10 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.
Riikka K 8 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.
computrius at gmail dot com 9 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 . "
";
}
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*lt;br />2*lt;br />3*lt;br />4*lt;br />
Warning: array_walk() expects parameter 2 to be a valid callback, cannot
access private method mc::walkIt() in /in/tfh7f on line 22
edanschwartz at gmail dot com 8 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`.
metamarkers at gmail dot com 9 years ago
you can pass an object as a callable if its class defines the __invoke()
magic method..
Yzmir Ramirez 9 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.
mariano dot REMOVE dot perez dot rodriguez
at gmail dot com 7 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( '~^(?[a-z_][a-z0-9_]*)
:: (?[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('~^(:?(?self|parent)::)?(?[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.
InvisibleSmiley 2 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).
bradyn at NOSPAM dot bradynpoulsen dot com 7 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);
// => [..., ..., ...]
chechomancr at hotmail dot com 1 year ago
In PHP >= 8.0 callIt function with optional parameters.
<?php
declare( strict_types = 1 );
class Example
{
public static function
someFunction( string $value, string $a_parameter
, int $another_parameter): string
{
//TODO: Apply the required filter
return( "$value $a_parameter $another_parameter" );
}
public static function callIt(string $callback, ...$parameters): callable
{
return( static fn(mixed $value = null) => self::$callback( $value
, ...$parameters ));
}
}
?>
How to use:
<?php
declare(strict_types = 1);
$args = [ 'information' => ['filter' => FILTER_CALLBACK,
'options' => Example::callIt('someFunction', 'world', 2022)], ];
$unfiltered_data = ['information' => 'Hello'];
$filtered_data = filter_var_array( $unfiltered_data, $args );
if( false === $filtered_data )
{
echo "Error...", PHP_EOL;
}
echo var_export( $filtered_data, true );
?>
Result: array ( 'information' => 'Hello world 2022' )
gulaschsuppe2 at gmail dot com 3 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
?>
ssi-anik 1 year ago
From the note of https://www.php.net/manual/en/langu
age.types.callable.php#109073
seems like the method name in `call_user_func*` is CASE-INSENSITIVE.
Daniel Klein 5 years ago
You can use "self::method_name", "static::method_name" and
"parent::method_name" in callables:
Results:
Array
(
[0] => 1: 42
[1] => 2: 42
[2] => 3: 42
)
Array
(
[0] => 1: 42
[1] => 2: 42
[2] => 3: 42
)
Array
(
[0] => 1: 123
[1] => 2: 123
[2] => 3: 123
)
Array
(
[0] => 1: Zaphod Beeblebrox
[1] => 2: Zaphod Beeblebrox
[2] => 3: Zaphod Beeblebrox
)
"self::" uses the same class as the called method, "static::" uses the same
class as the called class, and "parent::" (not shown) uses the parent class,
or
generates a warning if there is no parent.
pawel dot tadeusz dot niedzielski at gmail dot com 7 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();
?>
chris dot rutledge at gmail dot com 4 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)
?>
InvisibleSmiley 4 months ago
Note that the callable type declaration cannot be used with intersection
types introduced in PHP 8.1:
interface foo {}
function bar (callable&foo $param) {}
will report:
Fatal error: Type callable cannot be part of an intersection type
See https://wiki.php.net/rfc/pure-intersection-types#supported_types
whysteepy at gmail dot com 6 years ago
Another Appearance of Callbacks! Here is one way of them - methods of an instantiated object can be callable and implemented as variable functions without php's default functions that can call user-defined callback functions.
class Test
{
protected $items = array();
public function __construct()
{
$this->items[] = array($this, 'callBackOne');
$this->items[] = array($this, 'callBackTwo');
}
public function callBackOne()
{
echo __METHOD__ . ' has been called as a callback.';
}
public function callBackTwo()
{
echo __METHOD__ . ' has been called as a callback.';
}
public function getItems()
{
return $this->items;
}
}
$o = new Test();
$itemLists = $o->getItems();
foreach($itemLists as $itemList )
{
// call each one as a variable function
echo 'pre>';
print_r( $itemList() );
echo '*lt;/pre>';
}
// Outputs the following
// Test::callBackOne has been called as a callback.
// Test::callBackTwo has been called as a callback.
Dan J 5 years ago
You can avoid repeating a long namespace for classes in callable arrays by
making use of the "use" operator and the special "::class" constant.
Documentation of use operator:
http://php.net/manual/en/language.namespaces.importing.php
Documentation of ::class constant:
http://php.net/manual/en/language.oop5.constants.php
&klt;?php
// Library file with namespace My\Library\Namespace
require 'MyLibrary.php';
// Alias for SortingClass
use \My\Library\Namespace\SortingClass;
// Callable array referring to SortingClass::SortFunction
$callable = [SortingClass::class, 'SortFunction'];
$values = [3, 1, 2];
usort( $values, $callable );