コールバック / Callable
コールバックは、callable 型で表されます。
call_user_func() や usort() 等の関数は、ユーザーが定義するコールバック関数を引数として受け入れます。 コールバック関数は、単純な関数だけでなく、オブジェクトのメソッド あるいはクラスのstaticメソッドであってもかまいません。
受け渡し
PHP 関数はその名前を単に文字列として渡します。 どのようなビルトインまたはユーザー定義の関数も渡すことができます。 ただし、 array(), echo, empty(), eval(), exit(), isset(), list(), print あるいは unset() といった言語構造はコールバックとしては使えないことに注意しましょう。
オブジェクトのインスタンスを渡すには配列を使います。 配列の 0 番目の要素にオブジェクトを、 そして 1 番目の要素にメソッド名を指定します。 protected メソッドや private メソッドは、クラスの内部からはアクセスできます。
static メソッドの場合、オブジェクトのインスタンスは不要です。
0 番目の要素として、オブジェクトのかわりにクラス名を指定します。
'ClassName::methodName'
形式で指定することもできます。
一般的なユーザー定義関数とは異なり、 無名関数 と アロー関数 もパラメータとして渡せます。
注意:
PHP 8.1.0 以降では、 無名関数を作るための記法として、第一級callableを生成する記法 も使えます。
一般的には、 __invoke() を実装した任意のオブジェクトもパラメータとして渡せます。
例1 コールバック関数の例
<?php
// コールバック関数の例
function my_callback_function() {
echo 'hello world!';
}
// コールバックメソッドの例
class MyClass {
static function myCallbackMethod() {
echo 'Hello World!';
}
}
// タイプ 1: 単純なコールバック
call_user_func('my_callback_function');
// タイプ 2: staticメソッドのコール
call_user_func(array('MyClass', 'myCallbackMethod'));
// タイプ 3: オブジェクトメソッドのコール
$obj = new MyClass();
call_user_func(array($obj, 'myCallbackMethod'));
// タイプ 4: staticメソッドのコール
call_user_func('MyClass::myCallbackMethod');
// タイプ 5: 相対指定によるstaticメソッドのコール
class A {
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, 但し PHP 8.2.0 以降は非推奨
// タイプ 6: __invoke を実装したオブジェクトを callable として用いる
class C {
public function __invoke($name) {
echo 'Hello ', $name, "\n";
}
}
$c = new C();
call_user_func($c, 'PHP!');
?>
例2 クロージャを使ったコールバックの例
<?php
// クロージャ
$double = function($a) {
return $a * 2;
};
// 数値の範囲
$numbers = range(1, 5);
// ここでクロージャをコールバックとして使用し、
// 指定した範囲の各要素の二倍の値を計算します
$new_numbers = array_map($double, $numbers);
print implode(' ', $new_numbers);
?>
上の例の出力は以下となります。
2 4 6 8 10
注意:
call_user_func() や call_user_func_array() で登録されたコールバックは、 前のコールバックからスローされた例外がキャッチされていない場合はコールされません。
User Contributed Notes 12 notes
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;
}
}
?>
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
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.
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.
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`.
you can pass an object as a callable if its class defines the __invoke() magic method..
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.
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).
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);
// => [..., ..., ...]
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
?>
@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();
?>
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)
?>