検索
連載

PHPにおける変数のスコープと静的変数――「バグの温床」としないための使い方Web業界で働くためのPHP入門(10)(2/2 ページ)

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

Share
Tweet
LINE
Hatena
前のページへ |       

関数内でグローバル変数を利用するには

 さて、話をグローバル変数とローカル変数に戻します。先ほど、ローカル変数が関数外では認識できないことを解説しました。今度は逆に、関数内からグローバル変数を利用できるかどうかを調べてみましょう。

関数は関数内で完結している

 リスト1のuseUndefinedVariable.phpを少し改造した下記のuseGlobalVariable.phpを作成し、実行してください。

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

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

Notice: Undefined variable: list in C:\xampp\htdocs\phplesson\chap10\useGlobalVariable.php on line 5
Warning: Invalid argument supplied for foreach() in C:\xampp\htdocs\phplesson\chap10\useGlobalVariable.php on line 5
配列の計算結果: 1

 リスト4では関数multiplyArray()を元通り、戻り値を利用するようにしています。そのために、8行目にreturnを追加し、12行目では戻り値を受け取る変数$ansを用意しています。

 また、12行目で呼び出している関数multiplyArray()では、この時点で存在するグローバル変数$listをそのまま関数内で利用してforeachでループしています。


図4 リスト4の処理イメージ

 これは、うまくいきそうに思いますが、実行結果の通り、エラーとなります。$listが認識されていないのです。このことから、関数内は関数内で完結し、グローバル変数はそのままでは認識できないような仕組みとなっていることが分かります。グローバル変数は、明示的に引数として渡す必要があります。

関数内でグローバル変数を扱う裏技

 しかし例外的に、関数内でグローバル変数を認識させる方法もあります。それが、キーワードglobalです。例を見てみましょう。リスト4のuseGlobalVariable.phpを少し改造した以下のuseGlobalVariable2.phpを作成し、実行してください。変わった部分は4行目が増えただけです。

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

 実行結果は下記の通りです。

配列の計算結果: 17280

 今度はエラーになりませんでした。4行目のキーワードglobalがポイントです。

 下記のように記述することで、関数内にグローバル変数を呼び込むことができ、それ以降、関数内でその変数を利用することが可能です。

global 変数;


 といっても、もしこの関数を利用する側で変数名を変更してしまった場合、簡単にエラーとなってしまいます。つまり、この方式もバグの温床となってしまいます。できる限りこの方式は避け、引数を利用するようにしましょう。

コラム「$GLOBALS配列」

 グローバル変数は、PHP内部ではスーパーグローバルである$GLOBALS配列で参照できるようになっています。そのため、リスト5の

global $list;
foreach($list as $value) {

の部分は

foreach($GLOBALS["list"] as $value) {

と記述できます。


静的変数

 変数をいろいろ紹介してきましたが、ここでもう1つ、「静的変数」というのを紹介しておきましょう。まずは、以下のuseStaticVariable.phpを作成し、実行してください。

<?php
function plus(int $value)
{
    $num = 0;
    $num += $value;
    print("<br>計算結果: ".$num);
}
 
print("plusを3で呼出");
plus(3);
print("<br>plusを5で呼出");
plus(5);
print("<br>plusを7で呼出");
plus(7);
リスト6 phplesson/chap10/useStaticVariable.php

 実行結果は下記の通りです。

plusを3で呼出
計算結果: 3
plusを5で呼出
計算結果: 5
plusを7で呼出
計算結果: 7

 ソースコード自体は今までの知識で読める内容だと思います。関数plus()は引数でもらった整数をローカル変数$num(初期値0)に足し合わせて表示するという単純なものです。実行部分では、その関数をそれぞれ違った引数で3回呼び出しています。実行結果から分かるように、関数呼び出しのたびに$numは初期値0で初期化された上で引数が足し合わされています。

 このプログラムをリスト7のuseStaticVariable2.phpのように変更してみます。変更された部分は、4行目の$numにstaticが付いただけです。

<?php
function plus(int $value)
{
    static $num = 0;
    $num += $value;
    print("<br>計算結果: ".$num);
}
print("plusを3で呼出");
plus(3);
print("<br>plusを5で呼出");
plus(5);
print("<br>plusを7で呼出");
plus(7);
リスト7 phplesson/chap10/useStaticVariable2.php

 実行結果は下記の通りです。

plusを3で呼出
計算結果: 3
plusを5で呼出
計算結果: 8
plusを7で呼出
計算結果: 15

 実行結果がかなり違います。何が起こったのでしょうか?

 1回目の呼び出しでは、$numの初期値0に引数の3が足し合わされて$numは3になっています。ここまではリスト6と同じですが、2回目では明らかに$numは3のまま引数の5が足し合わされています。3回目も同様です。この実行結果から分かるのは、関数が呼び出されるたびに$numが初期化されず、前回の値が残っているということです。これが「静的変数」です。

 通常、関数が呼び出されるたびに関数内の変数は初期化されますが、静的変数は初回のみ初期化され、その後、実行が終了するまで値を保持する変数です。これは、関数内の変数にキーワード「static」を付けます。

 これも、使い方を間違えるとバグの温床になりかねない変数ですので、慎重に利用するようにしてください。

 この静的変数の使いどころとしてよくあるのは、「再帰呼び出し」の局面です。再帰呼び出しとは、関数内で関数自身を自ら呼び出す手法のこと。呼び出しの回数制限を設けるときに利用します。例で見てみましょう。

 リスト8のuseStaticWithRecursiveFunctions.phpを作成し、実行してください。

<?php
function recursivePlus(int $value)
{
    static $count = 10;
    $num = $value + $count;
    print("<br>計算結果: ".$num);
    print("<br>countの値: ".$count);
    $count--;
    if($count != 0) {
        recursivePlus($num);
    }
}
 
print("recursivePlusを実行");
recursivePlus(3);
リスト8 phplesson/chap10/useStaticWithRecursiveFunctions.php

 実行結果は下記の通りです。

recursivePlusを実行
計算結果: 13
countの値: 10
計算結果: 22
countの値: 9
計算結果: 30
countの値: 8
計算結果: 37
countの値: 7
計算結果: 43
countの値: 6
計算結果: 48
countの値: 5
計算結果: 52
countの値: 4
計算結果: 55
countの値: 3
計算結果: 57
countの値: 2
計算結果: 58
countの値: 1

 実行部分では、15行目でrecursivePlus()関数を1回だけ呼び出しているにもかかわらず、実行結果を見ると関数が何回か呼び出されているように見えます。これは、10行目で自分自身を呼び出しているからです。これを、「再帰呼出」、あるいは「再帰関数」といいます。

 その際、単に自分自身を呼び出すと、無限に呼び出してしまうことになります。そこで、4行目に静的変数$countを用意し、関数が呼び出されるたびにデクリメントを行っています(8行目)。この$countが0になるまで再帰呼出を行うように9行目でifを用意しています。リスト8では$countの初期値が10ですので、10回だけ呼び出されるように回数制限を設けたことになります。

次回は関数の引数/戻り値の扱いついて

 次回は関数の引数や戻り値の扱いについて掘り下げていきます。

今回のサンプルコード

 今回のサンプルコードはこちらからダウンロードできます。

前のページへ |       

Copyright © ITmedia, Inc. All Rights Reserved.

ページトップに戻る