PHP の無名関数はクロージャとも呼ばれ、関数をオブジェクト的に扱えます。
PHP: 無名関数 – Manual
これを使うと次の様に関数が生成されるタイミングと実行されるタイミングをずらすことができます。
<?php
class Greeter
{
protected $func;
public $msg = 'Hello';
public function __construct(){
$this->func = function(){ // newした時点でプロパティfuncの中身が固定されます
echo $this->msg;
};
}
public function run(){
($this->func)();// ($this->func)としないとクロージャの実行でなくメソッドの呼び出しになります。
}
}
$greeter = new Greeter();// new した時点では run() メソッドで Hello を echo します。
$greeter->msg = 'こんにちは';
$greeter->run();// こんにちは
これだけではあまり実用的ではないですが、題にある様に後から条件を付け足す時に便利です。例えば次の様な条件分岐が必要なクエリビルダです。
class MemberOrGroupQueryBuilder
{
protected $query;
protected const FROM_MEMBER_TABLE = 'members';
protected const FROM_GROUP_TABLE = 'groups';
protected string $from;
protected array $where = [];
public function __construct()
{
$this->query = \DB::query();// Laravelのクエリビルダ
}
/**
* FROM 句対象を members にする
* @return $this
*/
public function fromMember(): self
{
$this->from = self::FROM_MEMBER_TABLE;
return $this;
}
/**
* FROM 句対象を groups にする
* @return $this
*/
public function fromGroup(): self
{
$this->from = self::FROM_GROUP_TABLE;
return $this;
}
/**
* 名前を指定する WHERE 句を作る
* @param string $name
* @return $this
*/
public function whereName(string $name): self
{
// from 対象によって where するカラム名を変更
$this->where[] = function () use ($name) {
if ($this->from === self::FROM_MEMBER_TABLE) {
$this->query->where('member_name', $name);
} elseif ($this->from === self::FROM_GROUP_TABLE) {
$this->query->where('group_name', $name);
}
};
return $this;
}
public function get()
{
$this->query->from($this->from);
foreach ($this->where as $where) {
$where(); // $this->query に where 句を追加
}
return $this->query->get();
}
}
(new MemberOrGroupQueryBuilder())
->whereName('hoge') // from 指定前に where 句を置いても平気
->fromGroup()
->fromMember()// 後から from を書き換えてもセーフ
->get(); // select * from `members` where `member_name` = hoge が実行
この様に呼び出し側が呼び出し対象のことを知らずに任意のメソッドを任意の順に呼び出すことができ、呼び出し側にとって非常に便利です。
例は FROM 対象が異なるが似た呼び出しをしたいということなので 2 クラス + 1 インターフェースで作った方がいい問題ですが、遅延で条件分岐する方法自体は知っておくと何かと便利です。