PHP
基礎

PHPでクロージャーを使う

More than 3 years have passed since last update.

PHP再勉強中です。

PHP5.3で導入された無名関数はクロージャーとして使用することができるとのこと。

参考:Wikipedia : クロージャー

ただしPHPの場合、親のスコープから引き継ぐ変数はuseを利用して明示的に引き渡す必要があります。

クロージャーのサンプル

クロージャーを用いた単純なカウンターのコードは以下のようになります。

<?php
function create_counter()
{
    $count = 0;
    return function() use (&$count) {
        return ++$count;
    };
}

$counter = create_counter();
echo $counter() . PHP_EOL;    # => 1
echo $counter() . PHP_EOL;    # => 2
echo $counter() . PHP_EOL;    # => 3

create_counter()関数内で宣言されたローカル変数$countは、本来create_counter()関数を抜けた時点で消滅する筈ですが、create_counter()関数の戻り値として生成された無名関数にuseで引き渡されているために、create_counter()関数を抜けた後も存続することになります。
この時、無名関数内でカウンターとして$countをインクリメントするため、useには参照として渡す必要があります。(ここらへんちょっとめんどくさい)

このサンプルのような単純なケースの場合、静的変数を利用しても同様な処理は記述できますが、クロージャーを利用することでカウンターを保持する変数を関数内に閉じ込めることででき、また以下のように複数のカウンターを生成することも容易です。

$counter1 = create_counter();
$counter2 = create_counter();

echo $counter1() . PHP_EOL;    # => 1
echo $counter2() . PHP_EOL;    # => 1
echo $counter1() . PHP_EOL;    # => 2
echo $counter2() . PHP_EOL;    # => 2

クロージャーを返すメソッド

関数だけでなく、オブジェクトのメソッドもクロージャーを返すことができます。

<?php
class MyClass
{
    private $foo = "Foo";

    public function getFoo()
    {
        return function() { return $this->foo; };
    }
}

$obj = new MyClass();
$foo = $obj->getFoo();
echo $foo() . PHP_EOL;   # => "Foo"

PHP5.4以上であれば、メソッド内で生成された無名関数には暗黙的に$thisが引き渡されているので、無名関数内で使用することが可能です。
PHP5.3の場合、$thisを一度ほかの変数に代入後、その変数を無名関数にuseで明示的に引き渡す必要があります(筈)。

<?php
class MyClass
{
    private $foo = "Foo";   
    public function getFoo()
    {
        # $this を変数に代入して無名関数に引き渡す。
        $self = $this;
        return function() use (&$self) { return $self->foo; };
    }
}