関数の内部から、グローバルスコープの変数へアクセスすることはできない。
まずは、このコードをご覧ください。
$a = 0;
function hoge () {
$a++; // Undefined variable: a
}
hoge();
echo $a; // 0
期待した動作としては、関数の中で変数$a
の値をインクリメントして、1増えた値を出力してくれるものでした。
どうして期待とは異なった動作をしたのでしょうか。
それは、関数の中と外で変数のスコープが異なることに原因があります。
通常、関数の外側で定義された変数はグローバルスコープとなり、関数の内部で定義された変数は、ローカルスコープとなります。
(ちなみに、PHPにブロックスコープはありません)
// グローバルスコープ
$a = 0;
function hoge () {
// ローカルスコープ
$a++;
}
//グローバルスコープ
hoge();
echo $a;
しかし、特に他の言語を学んだことのある方なら、グローバルスコープとローカルスコープの関係について、このようなイメージをお持ちではないでしょうか?
グローバルスコープからローカルスコープへのアクセスは禁止されているけれど、ローカルスコープはグローバルスコープの一部なので、グローバル変数も使用可能、といった感じです。
しかし、実際にはこんなだいたいこんなイメージだと思ってください。
そもそも、お互いに離れているので、通常はアクセスすることができないといった感じです。
通常は、引数として渡してやるのが一般的です。
$a = 0;
function hoge ($a) {
$a++;
}
hoge();
echo $a; // 0
エラーはでなくなりましたが、以前出力される値は0のままです。
これは、関数に変数を渡すときは、変数そのものを受け渡しているのではなく、変数の中身をコピーして渡している(値渡し)からです。
変数そのものを渡す(参照渡し)ときには、&
をつけてあげます。
$a = 0;
function hoge (&$a) {
$a++; // グローバルスコープの変数と同じもの
}
hoge();
echo $a; // 1
また、あまり使われませんが変数の前にglobal
と宣言することで、引数として渡さなくても関数の内部からグローバル変数へアクセスすることが可能です。
$a = 0;
function hoge () {
global $a; // 明示的にグローバルな変数にアクセスする
$a++;
}
hoge();
echo $a; // 1
話は少々横道にそれますが、PHPにはスーパーグローバル変数というものがあります。
スーパーとついているだけあって、この変数はグローバルスコープだろうがローカルスコープだろうが、どんな場所でもアクセスすることが可能です。
$a = 0;
function hoge () {
// これはグローバルスコープに現在定義されているすべての変数への参照を含む連想配列
$GLOBALS['a']++;
// これらもグローバススコープの一種
$_GET['address'];
$_POST['address'];
}
hoge();
echo $a; // 1
無名関数の内部から外の変数を使う
無名関数の内部で、親のスコープから変数を引き継ぐ場合には、use
を使います。
<?php
$array = ['APPLE', 'LEMON', 'BANANA'];
$case_insensitive = true;
$red_fruits = array_filter($array, function($value) use($case_insensitive) {
if ($case_insensitive) {
$value = mb_strtolower($value);
}
return $value === 'apple';
});
print_r($red_fruits);
// Array ( [0] => APPLE )
これは、先程の例のように、グローバルスコープの変数にアクセスする場合とは、違うことに注意しましょう。
<?php
$array = ['APPLE', 'LEMON', 'BANANA'];
$case_insensitive = true;
function func($array) {
$case_insensitive = false;
$red_fruits = array_filter($array, function($value) use($case_insensitive) {
if ($case_insensitive) {
$value = mb_strtolower($value);
}
return $value === 'apple'
});
print_r($red_fruits);
}
func($array);
// Array ( )
無名関数を関数の中に閉じ込めてあげると、先程とはことなり、そのスコープ内の変数を取り込んでいることがわかります。
グローバルスコープの変数は使われておりません。