検索
連載
Web業界で働くためのPHP入門(10):

PHPにおける変数のスコープと静的変数――「バグの温床」としないための使い方 (1/2)

オープンソースのWeb開発向けスクリプト言語「PHP」の文法を一から学ぶための入門連載。今回は、PHPの変数のスコープについて解説します。

PC用表示 関連情報
Share
Tweet
g+
LINE
Hatena

変数のスコープ

 オープンソースのWeb開発向けスクリプト言語「PHP」の文法を一から学ぶための入門連載「Web業界で働くためのPHP入門」。

 今回のテーマは変数の「スコープ」です。今まで何げなく使っていたPHPの変数ですが、実は「生存できる範囲」があります。この変数が生存できる範囲のことを変数の「スコープ」といいます。

関数を利用した際の処理の流れをイメージしよう

 少し例を見てみましょう。リスト1のuseUndefinedVariable.phpを作成し、実行してください。このuseUndefinedVariable.phpは、前回作成したuseUserDefinedFunction.phpを少し改造したものです。

<?php
function multiplyArray(array $array)
{
    $num = 1;
    foreach($array as $value) {
        $num *= $value;
    }
}
 
$list = [5, 4, 8, 6, 2, 9];
multiplyArray($list);
print("配列の計算結果: ".$num);
リスト1 phplesson/chap10/useUndefinedVariable.php

 実行結果は下記のようにエラーになります。

Notice: Undefined variable: num in C:\xampp\htdocs\phplesson\chap10\useUndefinedVariable.php on line 12
配列の計算結果:

 エラー内容では、12行目の変数numが未定義、となっています。リスト1を単に眺めているだけだと、4行目で$numが宣言されているので「未定義」というエラーは不思議に思うかもしれません。これは、変数の「スコープ」が原因です。それを理解するために、まず、リスト1の処理の順番を追ってみましょう。図1を見てください。


図1 リスト1の処理の流れ

 リスト1は、コードは2行目から始まっていますが、2〜8行目は関数を定義している部分です。そのため、このプログラムが実行される際、実際は10行目から順に実行されます。10行目が実行され、11行目が実行されるときに関数の内部が実行されます。その部分はソースコードでは4〜7行目です。この関数内の処理が終了して初めて11行目の処理が終了します。その後、12行目が処理されます。

 このように見ていくと、関数内の変数$numの扱いが少し違って見えてくると思います。

 $numは関数内で宣言された変数です。このように関数内で宣言された変数のことを「ローカル変数」といいます。そして、PHPでは、ローカル変数は、その関数内でしか生存できないというルールがあります。つまり、ローカル変数のスコープは関数内のみ、ということであり、これを「ローカルスコープ」といいます。

 一方、関数外で宣言された変数、リスト1では$listが該当しますが、これを「グローバル変数」といい、関数外で実行中は生存できるスコープ(グローバルスコープ)を持っています。

 そして、PHPではローカルスコープとグローバルスコープは相互に行き来ができないようになっています。


図2 リスト1の各スコープ

関数内の結果の利用は戻り値で

 図2からも分かるように、ローカル変数である$numをグローバルスコープで利用しようとしたのがリスト1のエラーの原因です。関数内での計算結果を関数の呼び出し元で利用する場合は、前回解説したように、戻り値を利用するようにしましょう。

コラム「スーパーグローバル」

 先述のように、ローカル変数はローカルスコープのみ、グローバル変数はグローバルスコープのみで参照できるようになっています。しかし、ローカルスコープでもグローバルスコープでも、その両方で参照できる特別な変数があり、それを「スーパーグローバル」といいます。第8回のPHPの「定義済みの変数」で紹介した、$_GETや$_POSTは代表的なスーパーグローバルです。


require/includeされたファイルの処理

 では、外部ファイルをrequire/includeした場合はどのようになるのでしょうか。以下の例で確認してみましょう。まず、include先ファイルを作成します。

<?php
$num++;
print("<br>今のnumの値: ".$num);
リスト2 phplesson/chap10/increment.php

 次に、このファイルを利用するファイルを作成しましょう。

<?php
$num = 1;
print("include1回目");
include("increment.php");
print("<br>include2回目");
include("increment.php");
print("<br>include3回目");
include("increment.php");
リスト3 phplesson/chap10/useIncrement.php

 useIncrement.phpの方を実行してください。実行結果は下記の通りです。

include1回目
今のnumの値: 2
include2回目
今のnumの値: 3
include3回目
今のnumの値: 4

 まず、リスト2を見てください。未宣言の変数$numをいきなりインクリメントしています。実際、increment.phpを直接実行した場合は「Undefined variable: num」のエラーとなります。ところが、リスト3でこのファイルをincludeした場合はエラーにならず実行できます。前回のコラムにも書きましたが、requireもincludeもその位置に外部ファイルを読み込み、実行する処理です。前回では関数定義のみが記述されたファイルでしたので、イメージしにくかったかもしれませんが、リスト3のように実行可能なファイルを読み込んでも構いません。その場合の処理は図3のようになります。


図3 リスト3の実際の処理イメージ

 つまり、外部ファイルに書かれていても、実際に実行される際は以下のようなソースコードと同等となります。

<?php
$num = 1;
print("include1回目");
$num++;
print("<br>今のnumの値: ".$num);
print("<br>include2回目");
$num++;
print("<br>今のnumの値: ".$num);
print("<br>include3回目");
$num++;
print("<br>今のnumの値: ".$num);
リスト3が実行される実際のソースコード

 PHPでは、require/includeされたファイルは、実行時にソースコードが埋め込まれ、結合されるイメージを持ってください。こうすると、リスト2では未定義の変数$numが利用できることを分かってもらえると思います。

 といっても、リスト2とリスト3のようにrequire/include先に実行処理を記述し、しかも変数を共用するのはプログラムの可読性が非常に下がり、バグの温床となりがちです。やはり、別ファイルに記述するのはあくまで関数のみとし、実行処理は1つのファイルにまとめるように心掛けましょう。

       | 次のページへ

Copyright© 2017 ITmedia, Inc. All Rights Reserved.

ページトップに戻る