Closureクラスは無名関数(クロージャ)、アロー関数の型として使用します。
そのオブジェクトは変数に代入して 『$test()』のように変数に '()' を付けて関数を実行しますが、もうひとつ、Closure::call()を使っても実行可能。
同じ無名関数の実行なんですが、根本的なちがいがある。
バインドするクラスオブジェクト指定とクロージャの実行を同時に行う
Closure::call() の特長は、バインドするクラスオブジェクトを指定するのと同時に、クロージャの実行まですることです。
Closureインスタンスには、バインドという機能があり、クロージャとクラスオブジェクト(インスタンス)をつなげることを言います。
クロージャ($closure)は本来、クラスオブジェクトとは何の関係もないので、$this->val は意味不明のはずです。
しかし、call()メソッドでクラスオブジェクトを指定することで、このクロージャ内の$thisはそのクラスオブジェクトを指します。
だから処理が正常に行われるんですね?
ちなみに、このクロージャではアロー関数を使ってます。アロー関数ってなんだ? と思う人はこちらをどうぞ。
バインドのリセットは不可
Closure::bind() や Closure::bindTo() は、クラスオブジェクトのnull指定でバインドのリセットができるんですが、call()メソッドはできません。
さっきのコードを使ってみましょう。
バインドのリセットとは、つなぐクラスオブジェクトを未指定にすることです。
call()メソッドは、それがコールされるまでバインドされてない状態(リセット状態)なので、たとえ処理ができたとしても、$thisは未定義変数になります。
ノンバインドでクロージャを実行するにはシンプルに '()' でできます。
これで $this が使えないことを確認しましょう。
クロージャ内で クラスオブジェクト参照をしなければ処理は正常に動きます。
() でのクロージャ実行はノンバインドでも可。
call()メソッドはバインド必須。
同じことを bind(), bindTo()メソッドを使ってやってみる
call()メソッドでしたことは、bind(), bindTo()メソッドを使って同じことができます。一気にサンプルコードを実行してみましょう。
call()は『bind() or bindTo() + ()でのクロージャ実行』をギュッとまとめたもの。
まとめて処理ができるのでよく使う方法です。
call() = bind() or bindTo() + ()でのクロージャの実行
また、このクロージャでは、クラスのprivateプロパティの参照・変更、privateメソッドの実行も可能です。
それについてはこちらをどうぞ。
あと、call()はクロージャの実行結果を返すので、バインドしたクロージャの使い回しはできません。
使い回すなら、バインドしたクロージャのオブジェクトを返す、bind(), bindTo() のほうがコードとしてスマートです。
クラスオブジェクトを使わずクロージャひとつでできるから。
staticな処理はできない
call()メソッドの最初のパラメータはクラスのオブジェクト(インスタンス)です。
したがってクロージャ内でクラス内でstaticなプロパティ、メソッドに関する処理はできません。
一方、bind(), bindTo()メソッドでは可能。
それについてはすでに書いたのでこちらをどうぞ。
クロージャはそのままに使うクラスの変更も可能
call()メソッドは、バインドするクラスの型がそれぞれちがっても、同じクロージャで別型のクラスオブジェクトをバインドして変更できます。
ただしクロージャ内の処理は、どのクラスの型でもエラーにならないように共通処理にする必要がある。
instanceof演算子を使って処理を分けてもいい。
PHP公式ドキュメント
これはcall()メソッド独自の仕様ではなく、bind(), bindTo()でも同じです。
Closureクラスのバインド機能の仕様。
PHP公式ドキュメント