次にクロージャについて。
クロージャとは、関数Aの宣言内で、関数Aの返り値として無名関数Bを返す。
そして外側関数Aを実行した返り値(無名関数)を変数に代入する。つまりラムダ関数。
クロージャは、変数の値として使用することもできます。 PHP は、そのような記述があると自動的に内部クラスClosure のインスタンスに変換します。
具体例として
https://qiita.com/tricogimmick/items/3e344486658e3cfbd407
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
こんな感じ。内側の赤文字部分がクロージャ。
グローバルシンボルテーブル「$counter」が無名関数を指していて、無名関数内の関数シンボルテーブル$countが、無名関数外(create_counter関数内)の$countを指しているので、create_counter関数実行終了後も$countの値は消えない、ということか?
<?php
// 基本的なショッピングカートで、追加した商品の一覧や各商品の
// 数量を表示します。カート内の商品の合計金額を計算するメソッド
// では、クロージャをコールバックとして使用します。
class Cart
{
const PRICE_BUTTER = 1.00;
const PRICE_MILK = 3.00;
const PRICE_EGGS = 6.95;
protected $products = array();
public function add($product, $quantity)
{
$this->products[$product] = $quantity;
}
public function getQuantity($product)
{
return isset($this->products[$product]) ? $this->products[$product] :
FALSE;
}
public function getTotal($tax)
{
$total = 0.00;
$callback =
function ($quantity, $product) use ($tax, &$total)
{
$pricePerItem = constant(__CLASS__ . “::PRICE_” .
strtoupper($product));
$total += ($pricePerItem * $quantity) * ($tax + 1.0);
};
array_walk($this->products, $callback);
return round($total, 2);
}
}
$my_cart = new Cart;
// カートに商品を追加します
$my_cart->add(‘butter’, 1);
$my_cart->add(‘milk’, 3);
$my_cart->add(‘eggs’, 6);
// 合計に消費税 5% を付加した金額を表示します
print $my_cart->getTotal(0.05) . “\n”;
// 結果は 54.29 です
?>
クロージャの定義をこのページの最初に記述したものだとすると↑のカートの例の無名関数はクロージャではないような気がするが、ラムダ関数(無名関数を変数に代入して、
変数名();
として無名関数を実行する)ではある。
https://qiita.com/tricogimmick/items/3e344486658e3cfbd407
のカウンタの例との共通点は、ラムダ関数を実行して、無名関数の外の変数が状態を保持することで計算結果(カウント回数や金額合計)が求められる、ということ。
カートの例はarray_walk関数があるのでわかりにくいが、結局array_walk実行中にラムダ関数$callbackが、$productsの要素数分繰り返されている。