\Closure::bind

無名関数と __invoke() と Closureクラス で書いたClosureクラスですが、いくつか面白いメソッドがありまして。
その一つにbindメソッドがあります。ちぃと説明をしていきましょう。

まず前提として、以下のコードを見てみます。

<?php

class Hoge
{
    private $data;
}

//
$obj = new Hoge();
var_dump($obj);
$obj->data = 'test';

「Fatal error: Uncaught Error: Cannot access private property」で怒られました当たり前ですね。
端的には「なにプライベートに首つっこんどんねん」という感じで、この挙動自体は「十二分に必要で、これでエラーが出てくれないと色々と困る」もの、でございます。

……が、フレームワークやらcoreライブラリやらで、ごくまれに「基本はprivateにしたいんだけどここからだけは外から変数とか弄りたいんだ」的なニーズが、稀に、あったりします。

そんなときに「これができる」的な方法がPHPにも1つ2つあったりしますが、そのうちの一つに \Closure::bind があります。

前段階として、ちぃとコードを変更してみます。動かないのは一緒なんですけどね。

<?php

class Hoge
{
    private $data;
}

//
$obj = new Hoge();
var_dump($obj);
//$obj->data = 'test'; // Fatal error: Uncaught Error: Cannot access private property

//
$obj2 = function($obj)  {
    $obj->data = 'test';
};

// 実行
$obj2($obj);

//
var_dump($obj);

$obj2は「無名関数」ですね。まぁやってることは前述のコードと一緒です。
このコードをベースに「\Closure::bind を使ったコード」に変換してみます。

<?php

class Hoge
{
    private $data;
}

//
$obj = new Hoge();
var_dump($obj);
//$obj->data = 'test'; // Fatal error: Uncaught Error: Cannot access private property

//
$obj2 = \Closure::bind(function($obj)  {
    $obj->data = 'test';
}, NULL, Hoge::class);

// 実行
$obj2($obj);

//
var_dump($obj);

$obj2は「Closure インスタンス」です。
そのため、関数的にcallすると動かすことができます。で、動く内容は「bindで渡された無名関数」。書いてある通り「引数で与えられた$objのプロパティを変更する」です。

普通に書くとNGなのですが、今回、bindの第二引数で「Hoge::class」と入れてるため、$obj2を動かすときは「まるで Hoge class内で動いているかのような感じでいい感じによろ (o´・Υ・)ノ 」という風に動きます。

その結果として「$obj2はHogeクラスの仲間だから、private変数だって触ってOKだよ!!」となり、結果として「エラーもなく、処理を続行させることができる」ようになります。

この手法、乱用すると本気で混乱しか起きないのですが。
「使うべきところで最小限使う」と、劇薬のように強い作用で妙薬になる、事もあるので。

加減と用法容量に気を付けて、適切に用いたい技術でございます。

この記事が気に入ったらサポートをしてみませんか?