コールバック / Callable

callable は、 別の関数への引数として渡される関数またはメソッドを参照するものです。 これらは、callable 型を宣言することで表されます。

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

関数によっては、コールバック関数を引数として受け入れるものがあります。 例として array_map, usort, preg_replace_callback が挙げられます。

callable の作成

callable は、呼び出し可能な何かを表す型です。 コールバック関数をパラメータとして期待する関数や、 メソッドの引数として渡すことができますし、 直接呼び出すこともできます。 callable 型は、 クラスのプロパティの型宣言には使えません。 代わりに、Closure を型宣言に使ってください。

callable は、複数の異なる方法で作成できます:

  • Closure オブジェクト
  • 関数またはメソッドの名前を含む文字列
  • 配列。クラス名または object をインデックス 0 に、メソッド名をインデックス 1 に含みます。
  • __invoke() マジックメソッドを実装した object

Closure オブジェクトは、 無名関数アロー関数第一級callableを生成する記法、 もしくは Closure::fromCallable メソッドを使って作成できます。

注意: 第一級callableを生成する記法 は、PHP 8.1.0 以降でのみ利用可能です。

例1 Closure を使ったコールバックの例

<?php
// Using anonymous function syntax
$double1 = function ($a) {
    return $a * 2;
};

// Using first-class callable syntax
function double_function($a) {
    return $a * 2;
}
$double2 = double_function(...);

// Using arrow function syntax
$double3 = fn($a) => $a * 2;

// Using Closure::fromCallable
$double4 = Closure::fromCallable('double_function');

// Use the closure as a callback here to
// double the size of each element in our 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);

?>

上の例の PHP 8.1 での出力は、このようになります。:

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

callable は、 関数名や static メソッドを含む文字列でも表現できます。 ビルトイン関数、またはユーザー定義関数も使えます。 但し、以下のような言語構造は除きます: array, echo, empty, eval, isset, list, print, unset

static メソッドは、クラスの object 型をインスタンス化せずに使えます。 クラス名をインデックス 0 に、 メソッド名をインデックス 1 に配置した配列を作成するか、 'ClassName::methodName' のような、 スコープ解決演算子 :: を使った特殊構文が使えます。

インスタンス化された object のメソッドは、object をインデックス 0 に、 メソッド名をインデックス 1 に配置した配列を作成することで、 callable にできます。

Closure オブジェクトと callable 型の主な違いは、 Closure オブジェクトがスコープに依存せず常に呼び出せるのに対し、 callable はスコープに依存する場合があり、 直接呼び出せない可能性がある点です。 Closure が、callable を作成する望ましい方法です。

注意: Closure オブジェクトは、 それが作成されたスコープにバインドされますが、 クラスメソッドを文字列または配列で参照する callable は、 それらが呼び出されたスコープの内部で解決されます。 callable を private または protected メソッドから作成し、 クラスのスコープ外から呼び出せるようにするには、 Closure::fromCallable または、 第一級callableを生成する記法 を使います。

PHP は、コールバック引数として使えるものの、 直接は呼び出せない callable を作成できます。 これらはコンテキスト依存の callable であり、 例えば 'parent::method'["static", "method"] の形で、クラスの継承階層におけるクラスメソッドを参照します。

注意: PHP 8.2.0 以降では、 コンテキスト依存の callable は推奨されなくなりました。 'parent::method'parent::class . '::method' 形式に置き換えることでコンテキスト依存を除去するか、 第一級callableを生成する記法 を使ってください。

例2 call_user_function を使って、様々なタイプの callable を呼び出す

<?php

// An example callback function
function my_callback_function() {
    echo 'hello world!', PHP_EOL;
}

// An example callback method
class MyClass {
    static function myCallbackMethod() {
        echo 'Hello World!', PHP_EOL;
    }
}

// Type 1: Simple callback
call_user_func('my_callback_function');

// Type 2: Static class method call
call_user_func(['MyClass', 'myCallbackMethod']);

// Type 3: Object method call
$obj = new MyClass();
call_user_func([$obj, 'myCallbackMethod']);

// Type 4: Static class method call
call_user_func('MyClass::myCallbackMethod');

// Type 5: Static class method call using ::class keyword
call_user_func([MyClass::class, 'myCallbackMethod']);

// Type 6: Relative static class method call
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: Objects implementing __invoke can be used as callables
class C {
    public function __invoke($name) {
        echo 'Hello ', $name;
    }
}

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

上の例の出力は以下となります。

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!

注意:

call_user_funccall_user_func_array で登録されたコールバックは、 前のコールバックからスローされた例外がキャッチされていない場合はコールされません。