Posted at

PHPでクロージャーを使う

More than 5 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; };
}
}