クロージャの 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 にします。
よくある誤解
「クロージャの中と外は同じ変数を共有しているはず」という思い込みは誤りです。useは既定では値のコピーで、クロージャの中の変更は外側の変数に反映されません。
まとめ
クロージャの use($var) は値をコピーする(参照ではない)は上級でつまずきやすい項目です。上の3パターンを実際に手で直すと、エラーメッセージのどこを読めばよいかが掴めます。