ツイート
シェア
LINEで送る
B! はてぶでブックマーク
Pocketでブックマーク
RSSフィード

PHP, コールバックの記法。ぱっと見、関数を実行してることに気づかない。

php
イラストダウンロードサイト【イラストAC】
の画像をもとに加工しています。

コールバックとは、関数名の文字列から関数を実行する際の関数名文字列のことです。

PHPではstring型と配列型で指定するんですが、内部ではcallable型というコールバック専用の型に変換される。

見た目は文字列や配列に '()' を付けて関数が実行されるのがコールバックの不思議なところ。

コールバックとは? その使い方

ふつう関数は、あらかじめ処理を定義しておき、関数コールのルールにしたがって処理を行います。

// 関数の定義
function test($val1, $val2)
{
    return $val1 + $val2;
}

// 関数コール(実行)
$result = test(3, 5);

// 関数の実行結果出力
var_dump($result);
実行結果
int(8)

一方、関数名さえ分かっていれば、関数のパラメータや戻り値で指定でき、関数の実行は受け取った側で行うこともできます。

『どの関数を実行するのか?』と、『どのタイミングで関数を実行するのか?』を分離できるんですね?

このキーとなる関数名のことをコールバックといい、コールバックで実行される関数のことをコールバック関数といいます。

またコールバックは、実行する関数と実行するタイミングを分離する機能全体のことを呼ぶこともあります。

コールバックの指定方法

コールバックの指定方法はかんたんです。関数名さえ分かればいいので文字列で指定する。

// コールバック関数の定義
function test_callback($val1, $val2)
{
    return $val1 * $val2;
}

// コールバックを受け取って実行する関数
function test($val1, $val2, $callback)
{
    // コールバックを実行して結果を返す
    return $callback($val1, $val2);
}

// コールバックを実行する関数コール
$callback = 'test_callback';
$result = test(3, 5, $callback);

var_dump($result);
実行結果
int(15)

これで関数指定と実行するタイミングが分離されてることがよく分かります。

test() のパラメータで渡す関数名を変えれば、test() の実行結果も変わる。

ちなみに、コールバック関数の関数名は、'callback' や 'callable' を付けるようになってます。コーディングルールというより、プログラミングの慣習に近い。

プログラマが読みやすいようにコールバック専用の関数で命名します。もちろん、通常の関数名でコールバックを使ってもかまいません。

パラメータで渡さずにコールバックで関数を実行する方法

コールバックを関数のパラメータでわたすと『へ~。そうなんだ。』と納得できますが、次のコードではどうでしょう。

/*
 * コールバック関数の実行
 */

// 関数名を変数に入れてから実行
$callback = 'test_callback';
$result = $callback(3, 5);

var_dump($result);

// 変数に入れず直接文字列から実行
$result = 'test_callback'(3, 5);

var_dump($result);
実行結果
int(15)
int(15)

ぱっと見、これで関数が実行されるとは思いません。とくに多言語からPHPに参入した、ある程度プログラミング経験のある人はとくに。

でも、パラメータで渡してできるんだから、そうだろうね? の仕様ではあります。

関数名文字列のくくりをダブルクォーテーション(")でも実行可能です。かんたんすぎるのでサンプルコードは割愛。

クラスメソッドのコールバック

コールバックはクラスのメソッドでもできます。

class Test
{
    static public function static_callback($val1, $val2)
    {
        return $val1 + $val2;
    }

    public function instance_callback($val1, $val2)
    {
        return $val1 - $val2;
    }
}

// staticメソッドの実行
$result = 'Test::static_callback'(3, 5);
var_dump($result);

$result = ['Test', 'static_callback'](4, 6);
var_dump($result);

// インスタンスメソッドの実行
$test = new Test();
$result = [$test, 'instance_callback'](3, 5);
var_dump($result);
実行結果
int(8)
int(10)
int(-2)

クラスの場合は文字列で表現できないインスタンスがあるので、配列で指定します。

配列の1番目のデータがインスタンスで2番目がコールバック関数(メソッド)名。

staticの場合はインスタンスがないので、クラスの型名(クラス名)を文字列で指定します。

また、staticメソッドはすべて文字列で表現できるので、'クラス名::メソッド名' の書式を文字列で指定しても可能。

文字列・配列に見えるコールバックはcallable型

文字列や配列の後ろに '()' を付けたら関数が実行できるなんて不思議でしょう?

その正体はコールバックの型にあります。

コールバックの記述は文字列や配列ですが内部ではcallable型。

PHPでは、'()' を付けた文字列・配列はcallable型に変換されます。だから不思議な関数実行ができるんですね?

よくPHPの関数実行は test(), 'test'(), "test"() でできる、と説明されますが、クォーテーション(', ")付きの正体はコールバックです。

ここまでの説明を最初からしても理解は無理なので、たんに3つの方法で実行できるという説明で止まることも多い。

call_user_func() っていらないよね?

PHPにはコールバックから関数を実行する関数がいくつか用意されています。

でもコールバック関数は、コールバック文字列・配列に '()' を付ければ実行できるので使う理由がありません。

PHP4, 5で追加されたんですが、これはもう使わなくていいでしょう。PHP8でもまだ非推奨になってませんが、近い将来、無くなるんじゃないだろうか?

(個人的な勝手な予想で、そういう動きがあるということではない。)

意外とよく見る配列 + '()' の関数実行の記述

クォーテーションがあろうがなかろうが同じなので、関数コールで関数名をクォーテーションで囲う人はまずいないですが、配列 + '()' の記述はよく見ます。

これまでの説明が分かってないと、まず関数コールだとは思いません。ここがコールバックの一番気をつけるポイント。

この仕様が分かっていても、ほかのプログラム言語を使ってて、しばらくたってPHPに戻ってきたときに『これ、なんだったっけ?』と思ってしまうほど珍しい記述です。

関数やメソッド名がまちがってたらどうなる?

最後にコールバックで関数名がまちがってるときどうなるか? を見てみましょうか。

だれでも想像できる Undefined のエラーになるはず。

// 未定義の関数をコールバックで実行
$result = 'aaa'();
実行結果
PHP Fatal error:  Uncaught Error: Call to undefined function aaa() in /home/vagrant/php-test.php:2
Stack trace:
#0 {main}
  thrown in /home/vagrant/php-test.php on line 2
// 未定義のstaticメソッドの実行
$result = 'Test::static_callback_aaa'(3, 5);
実行結果
PHP Fatal error:  Uncaught Error: Call to undefined method Test::static_callback_aaa() in /home/vagrant/php-test.php:2
Stack trace:
#0 {main}
  thrown in /home/vagrant/php-test.php on line 2
// 未定義のstaticメソッドの実行
$result = ['Test', 'static_callback_aaa'](4, 6);
実行結果
PHP Fatal error:  Uncaught Error: Call to undefined method Test::static_callback_aaa() in /home/vagrant/php-test.php:2
Stack trace:
#0 {main}
  thrown in /home/vagrant/php-test.php on line 2

Error例外の発生なので、例外をキャッチすることでエラー時の回避が可能です。

インスタンスメソッドの場合、エラーメッセージに '::' が付いてるのはご愛嬌。もちろん、staticメソッドにしろというものではありません。

// 未定義のインスタンスメソッドの実行
$test = new Test();
$result = [$test, 'instance_callback_aaa'](3, 5);
実行結果
PHP Fatal error:  Uncaught Error: Call to undefined method Test::instance_callback_aaa() in /home/vagrant/php-test.php:3
Stack trace:
#0 {main}
  thrown in /home/vagrant/php-test.php on line 3

これは通常のメソッド実行時でも同じ。

$test = new Test();
$test->instance_callback_aaa(3, 5);
実行結果
PHP Fatal error:  Uncaught Error: Call to undefined method Test::instance_callback_aaa() in /home/vagrant/php-test.php:2
Stack trace:
#0 {main}
  thrown in /home/vagrant/php-test.php on line 2

コールバックと無名関数のちがい

関数のパラメータや戻り値に関数を渡す方法に無名関数があります。

関数を渡してあげて、実行のタイミングは受け取った側で自由に行う点で同じ。

'()' を付ける対象のデータの型がcallableかClosureクラスかのちがいだけ。

PHPの無名関数はClosureクラスのオブジェクトです。このクラスのインスタンスが有効なかぎり関数を実行できます。

サンプルコードで確認してみましょう。正直、類似性がありすぎて、どっちか片方だけでいいよ、と思うほど。

/**
 * シンプルなコールバックと無名関数の実行
 */

// コールバック関数の定義
function test_callback($val1, $val2)
{
    return $val1 * $val2;
}

// 無名関数の定義
$closure = function($val1, $val2)
{
    return $val1 * $val2;

};

// コールバックの実行
$result1 = 'test_callback'(3, 5);

// 無名関数の実行
$result2 = $closure(3, 5);

// 結果表示(どっちも同じことをしている。)
var_dump($result1);
var_dump($result2);

/**
 * コールバックと無名関数をパラメータで渡す
 */

function test($val1, $val2, $func)
{
    return $func($val1, $val2);
}

// コールバックを使った場合
$result1 = test(4, 5, 'test_callback');

// 無名関数を使った場合
$result2 = test(4, 5, $closure);

var_dump($result1);
var_dump($result2);
int(15)
int(15)
int(20)
int(20)

test() は、コールバックと無名関数のどちらを受け取っても処理ができます。両者の類似性がなせる技。

最新のコールバックは無名関数を使う

これまでコールバックの記法をつらつらと説明してきましたが、PHP8.1では新しい記法が登場しました。

第一級callableと呼ばれるものです。

この記法はコールバックよりもより分かりやすくシンプルになっていて、内部で無名関数を使います。

具体的にはこの記法の返す戻り値は、無名関数の戻り値と同じClosureクラスのオブジェクトになる。

この第一級callableでも従来のコールバックの記法が使えますが、コーディング文法の下位互換みたいなもの。

細かいところは別に任せますが(編集中)、シンプルに関数やメソッドの()を(...)にするだけでいいので、古い記法を使う理由がありません。

古い記法はそこまで覚える必要はない

今回の記法はあと5年くらい経てば、PHP8.0以下のバージョンでプログラミングしないかぎり、あまり見なくなるものです。

PHP8.1以降からプログラミングを始める人にとっては、今回のコールバックの記述は過去の遺物。

歴史好きじゃなければ、学習の優先度はそんなに高いものじゃありません。

使う必要性があるときに覚えればいいんじゃないかな?

プログラミングの覚え方って、これにかぎらずそうなんだけど。

PHP公式ドキュメント

型 - callable

前の投稿
PHP, Closureクラス, 外からクラス内の隠蔽したメソッドを実行できるんですけど?
PHP7.4, 関数なしでもできる配列のマージ。アンパック(スプレッド構文)
次の投稿
コメントを残す

*