かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

PHP array_map でコールバック外にある変数を使いたい

JavaScript だと map などのコールバック関数の外にある変数にアクセスするのは言語仕様上問題がないが、PHP ではスコープ外となってしまった

JavaScript
const val = '☆';
const array = ['ハナ', 'シオリ', 'ルリ', 'ビート'];
const result = array.map((value) => `${val} ${value}`);
console.log(result);
// => ['☆ ハナ', '☆ シオリ', '☆ ルリ', '☆ ビート']
PHP
<?php
$val = '☆';
$array = ['ハナ', 'シオリ', 'ルリ', 'ビート'];
$result = array_map(function($value) {
  return "{$val} {$value}";
}, $array);
var_dump($result);
// array(4) {
//   [0] => " ハナ"
//   [1] => " シオリ"
//   [2] => " ルリ"
//   [3] => " ビート"
// }

=> Warning: Undefined variable $val in Standard input code on line 5
array_map のコールバック関数内から外にある $val にアクセスできない

use を使ってコールバック関数 (無記名関数) に変数を渡せる

Closures may also inherit variables from the parent scope. Any such variables must be passed to the use language construct. As of PHP 7.1, these variables must not include superglobals, $this, or variables with the same name as a parameter. A return type declaration of the function has to be placed after the use clause.
cf. PHP: Anonymous functions - Manual

<?php
$val = '☆';
$array = ['ハナ', 'シオリ', 'ルリ', 'ビート'];
$result = array_map(function($value) use($val) {
  return "{$val} {$value}";
}, $array);
var_dump($result);
// array(4) {
//   [0] => "☆ ハナ"
//   [1] => "☆ シオリ"
//   [2] => "☆ ルリ"
//   [3] => "☆ ビート"
// }

複数の変数を渡したい場合

use() にカンマ区切りで変数を記述すればOK

<?php
$prefix = '☆';
$suffix = '!';
$array = ['ハナ', 'シオリ', 'ルリ', 'ビート'];
$result = array_map(function($value) use($prefix, $suffix) {
  return "{$prefix} {$value} {$suffix}";
}, $array);
var_dump($result);
// array(4) {
//   [0] => "☆ ハナ !"
//   [1] => "☆ シオリ !"
//   [2] => "☆ ルリ !"
//   [3] => "☆ ビート !"
// }

use() で渡した変数は値渡しになる

コールバック関数内で変更してもスコープ外の変数に影響を与えない

<?php
$my_array = [[]];
$prefix = '☆';
$array = ['ハナ', 'シオリ', 'ルリ', 'ビート'];
$result = array_map(function($value) use($prefix, $my_array) {
  $my_array[0][] = $value;
  $prefix = '♡';
  return "{$prefix} {$value}";
}, $array);
var_dump($result);
// array(4) {
//   [0] => "♡ ハナ"
//   [1] => "♡ シオリ"
//   [2] => "♡ ルリ"
//   [3] => "♡ ビート"
// }

// コールバック関数の中での変更は反映されない
var_dump($my_array);
// array(1) {
//   [0] => array(0) {
// }
echo $prefix;
// ☆

コールバック関数内でスコープ外の変数を更新したい場合は & を付けて参照渡しにすればOK

$my_array を更新したい場合

<?php
  $my_array = [[]];
  $prefix = '☆';
  $array = ['ハナ', 'シオリ', 'ルリ', 'ビート'];
- $result = array_map(function($value) use($prefix, $my_array) {
+ $result = array_map(function($value) use($prefix, &$my_array) {
    $my_array[0][] = $value;
    $prefix = '♡';
    return "{$prefix} {$value}";
  }, $array);

👇

<?php 
$my_array = [[]];
$prefix = '☆';
$array = ['ハナ', 'シオリ', 'ルリ', 'ビート'];
$result = array_map(function($value) use($prefix, &$my_array) {
  $my_array[0][] = $value;
  $prefix = '♡';
  return "{$prefix} {$value}";
}, $array);

var_dump($my_array);
// array(1) {
//   [0]=>
//   array(4) {
//     [0]=>
//     string(6) "ハナ"
//     [1]=>
//     string(9) "シオリ"
//     [2]=>
//     string(6) "ルリ"
//     [3]=>
//     string(9) "ビート"
//   }
// }
echo $prefix;
// ☆

おわり


[参考]

アイカツ! 10th 3rd month のライブめちゃんこ良かった…