PHPでクロージャのuse地獄を回避する裏ワザ

# Before

```php:PHP7.0+
(function () {
    $name = 'John';
    (function () use ($name) {
        $greet = 'Hello';
        (function () use ($name, $greet) {
            echo "{$greet}, {$name}\n";
        })(); 
    })();
})();
```

```php:PHP5.3+
call_user_func(function () {
    $name = 'John';
    call_user_func(function () use ($name) {
        $greet = 'Hello';
        call_user_func(function () use ($name, $greet) {
            echo "{$greet}, {$name}\n";
        });
    });
});
```


# After

PHP7.0以降は `new class{}` で無名クラスが使えます．全てのクロージャを無名クラスに属させることで，`$this`を通じた共有が実現できます．`call()` は第1引数で `$this` を指定することが出来ます．

```php:PHP7.0+
(function () {
    $this->name = 'John';
    (function () {
        $this->greet = 'Hello';
        (function () {
            echo "{$this->greet}, {$this->name}\n";
        })();
    })();
})->call(new class{});
```

```php:PHP7.0+
(function () {
    $this->name = 'John';
    (function () {
        $this->greet = 'Hello';
        (function () {
            echo "{$this->greet}, {$this->name}\n";
        })();
    })();
})->bindTo(new class{})();
```

```php:PHP7.0+
\Closure::bind(function () {
    $this->name = 'John';
    (function () {
        $this->greet = 'Hello';
        (function () {
            echo "{$this->greet}, {$this->name}\n";
        })();
    })();
}, new class{})();
```

PHP5.4でも `new \stdClass` とすることで無名クラスの代わりに使えます．`call()`もPHP7.0以降の機能ですが，`bind()`と`__invoke()`を組み合わせることで解決できます．`call_user_func()`を使っても構いません．

```php:PHP5.4+
\Closure::bind(function () {
    $this->name = 'John';
    call_user_func(function () {
        $this->greet = 'Hello';
        call_user_func(function () {
            echo "{$this->greet}, {$this->name}\n";
        });
    });
}, new \stdClass)->__invoke();
```

```php:PHP5.4+
call_user_func(\Closure::bind(function () {
    $this->name = 'John';
    call_user_func(function () {
        $this->greet = 'Hello';
        call_user_func(function () {
            echo "{$this->greet}, {$this->name}\n";
        });
    });
}, new \stdClass));
```

# 備考

## クラスメソッド編

クラスメソッドで本来の`$this`も参照しつつ…という場合も無理矢理ですが対応可能です．

プロパティを簡潔に初期化するために`(object)[]`という記法を使っていますが，これは`stdClass`にキャストされます．**`stdClass`に対して`call()`は使用出来ない**ため，PHP7.0以降でもPHP5.4のときと同様に`bindTo()()`で代用する必要があることに注意してください．

```php:PHP7.0+
<?php

class Klass {
    
    private $greet = 'Hello';
    
    public function hello() {
        (function () {
            $this->name = 'John';
            (function () {
                (function () {
                    echo "{$this->self->greet}, {$this->name}\n";
                })();
            })();
        })->bindTo((object)['self' => $this])();
    }
    
}

(new Klass)->hello();
```

```php:PHP5.4+
<?php

class Klass {
    
    private $greet = 'Hello';
    
    public function hello() {
        \Closure::bind(function () {
            $this->name = 'John';
            call_user_func(function () {
                call_user_func(function () {
                    echo "{$this->self->greet}, {$this->name}\n";
                });
            });
        }, (object)['self' => $this])->__invoke();
    }
    
}

(new Klass)->hello();
```

## 別のアプローチ

持ち回したい変数を全てあらかじめ定義したオブジェクトまたは配列に入れるようにし，最小限の`use`で済ませる，というのもアリです．現実的にはこちらのほうが周囲の理解を得やすいかもしれません．

```php:PHP7.0+&#32;オブジェクト
$s = new class{};
(function () use ($s) {
    $s->name = 'John';
    (function () use ($s) {
        $s->greet = 'Hello';
        (function () use ($s) {
            echo "{$s->greet}, {$s->name}\n";
        })(); 
    })();
})();
```

```php:PHP7.0+&#32;配列（配列の場合は参照で渡さないと書き換えが出来ないので注意）
$s = [];
(function () use (&$s) {
    $s['name'] = 'John';
    (function () use (&$s) {
        $s['greet'] = 'Hello';
        (function () use (&$s) {
            echo "{$s['greet']}, {$s['name']}\n";
        })(); 
    })();
})();
```

## 非同期処理ライブラリとの併用

関連投稿: [「ReactPHP？時代はRecoilPHPだ！」](http://qiita.com/mpyw/items/f448a4c582c78ddb9365)

この書き方が真価を発揮するのはReactPHPやRecoilPHPといった，非同期処理のためのライブラリを使う場合です．クロージャが頻繁に登場するので`use`地獄に陥りやすいです．この裏ワザを使うとかなり気持ちよく書けますね．

```php:PHP7.0+&#32;ReactPHP
<?php

require 'vendor/autoload.php';
$GLOBALS['loop'] = React\EventLoop\Factory::create();

function plusOneLater($a) {
    $deferred = new React\Promise\Deferred();
    $GLOBALS['loop']->addTimer(1, function () use ($deferred, $a) {
        $deferred->resolve($a + 1);
    });
    return $deferred->promise();
}

(function () {
    $this->greet = 'Hello';
    React\Promise\all([
        plusOneLater(0)->then(function ($x) { echo "$this->greet, $x\n"; return plusOneLater($x); })
                       ->then(function ($y) { echo "$this->greet, $y\n"; return plusOneLater($y); })
                       ->then(function ($z) { echo "$this->greet, $z\n"; }),
        plusOneLater(3)->then(function ($x) { echo "$this->greet, $x\n"; return plusOneLater($x); })
                       ->then(function ($y) { echo "$this->greet, $y\n"; return plusOneLater($y); })
                       ->then(function ($z) { echo "$this->greet, $z\n"; }),
    ])->then(function () { echo "All done !!\n"; });
})->call(new class{});

$GLOBALS['loop']->run();
```

```php:PHP7.0+&#32;RecoilPHP
<?php

require 'vendor/autoload.php';

function plusOneLater($a) {
    yield Recoil\Recoil::sleep(1);
    return $a + 1;
}

Recoil\Recoil::run((function () {
    $this->greet = "Hello";
    yield Recoil\Recoil::all([
        (function () {
            $x = yield plusOneLater(0); 
            echo "$this->greet, $x\n";
            $y = yield plusOneLater($x);
            echo "$this->greet, $y\n";
            $z = yield plusOneLater($y);
            echo "$this->greet, $z\n";
        })(),
        (function () {
            $x = yield plusOneLater(3);
            echo "$this->greet, $x\n";
            $y = yield plusOneLater($x);
            echo "$this->greet, $y\n";
            $z = yield plusOneLater($y);
            echo "$this->greet, $z\n";
        })(),
    ]);
    echo "All done !!\n";
})->bindTo(new class{}));
```