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 にします。

この問題を解いてみる →

よくある誤解

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

まとめ

クロージャの use($var) は値をコピーする(参照ではない)は上級でつまずきやすい項目です。上の3パターンを実際に手で直すと、エラーメッセージのどこを読めばよいかが掴めます。

演習をはじめる

関連するエラー

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