Code Fix

上級

クロージャの use($var) は値をコピーする(参照ではない)

無名関数でuse($var)と書くと、その時点の$varの値がコピーされてクロージャに閉じ込められます。クロージャの中で変更しても、外側の変数には影響しません。外側の変数と連動させたい場合はuse(&$var)と参照で渡す必要があります。

なぜエラーが出ないのか

出力: 5
(エラーなし)
PHPは何も報告しません。文法として正しいためです
出力: 5
実際の挙動 — 期待した結果と食い違っている箇所
見つけ方
エラーが出ないので、出力を目で確かめるか、コードを目で追うしかありません。この種の誤りが最も発見が遅れます

このエラーが出る典型パターン

パターン1

 1  <?php
 2  $total = 0;
 3  $add = function($n) use ($total    ) {
                                  ^
 4      $total += $n;
 5      return $total;
 6  };
 7  echo $add(5);
 8  echo "\n" . $total;
出力: 5 0期待: 5 5 (エラーにはなりません)

use ($total)は定義時点の値をコピーするだけです。クロージャの中で加算しても外側の$totalには影響しません。外側と連動させるにはuse (&$total)と参照で渡す必要があります。

直し方: $total を &$total にします。

この問題を解いてみる →

広告
広告スロット(未設定)

パターン2

 1  <?php
 2  $counter = 0;
 3  $increment = function() use ($counter    ) {
                                       ^
 4      $counter++;
 5      return $counter;
 6  };
 7  echo $increment();
 8  echo $increment();
 9  echo "\n" . $counter;
出力: 11 0期待: 12 2 (エラーにはなりません)

use ($counter)はコピーされた値をクロージャの中だけで増やし続けるため、毎回1からではなく前回の呼び出し結果を引き継ぎますが、外側の$counterは0のままです。&を付けて参照で渡すと外側とも同期します。

直し方: $counter を &$counter にします。

この問題を解いてみる →

パターン3

 1  <?php
 2  $balance = 100;
 3  $withdraw = function($n) use ($balance    ) {
                                        ^
 4      $balance -= $n;
 5      return $balance;
 6  };
 7  echo $withdraw(30);
 8  echo "\n" . $balance;
出力: 70 100期待: 70 70 (エラーにはなりません)

use ($balance)は値のコピーなので、クロージャの中で引き算しても外側の$balanceは減りません。連動させるには&$balanceのように参照で渡します。

直し方: $balance を &$balance にします。

この問題を解いてみる →

パターン4

 1  <?php
 2  $score = 0;
 3  $addPoint = function($n) use ($score    ) {
                                       ^
 4      $score += $n;
 5      return $score;
 6  };
 7  echo $addPoint(10);
 8  echo "\n" . $score;
出力: 10 0期待: 10 10 (エラーにはなりません)

use ($score)は定義時点の値をコピーするだけです。クロージャの中で加算しても外側の$scoreには影響しません。

直し方: $score を &$score にします。

この問題を解いてみる →

パターン5

 1  <?php
 2  $stock = 50;
 3  $sell = function($n) use ($stock    ) {
                                   ^
 4      $stock -= $n;
 5      return $stock;
 6  };
 7  echo $sell(10);
 8  echo "\n" . $stock;
出力: 40 50期待: 40 40 (エラーにはなりません)

use ($stock)は値のコピーなので、クロージャの中で引き算しても外側の$stockは減りません。

直し方: $stock を &$stock にします。

この問題を解いてみる →

よくある誤解

「クロージャの中と外は同じ変数を共有しているはず」という思い込みは誤りです。useは既定では値のコピーで、クロージャの中の変更は外側の変数に反映されません。

実務での勘所

PHP 7.4で追加されたアロー関数(fn($x) => ...)は、useを書かなくても外側の変数を自動的に(値として)取り込んでくれます。ただしこれはuse($var)を省略できる書き方というだけで、値渡しであることは通常のクロージャと同じです。外側の変数と連動させたい(参照渡しにしたい)場合は、アロー関数では実現できず、従来通りfunction() use (&$var) { ... }という書き方が必要です。

演習をはじめる

関連するエラー

広告
広告スロット(未設定)