変数のスコープ

変数のスコープは、その変数が定義されたコンテキストです。ほとんどの PHP 変数は、スコープを1つだけ有しています。このスコープの範囲は、 includeやrequireにより読みこまれたファイルも含みます。例えば、

<?php
$a
= 1;
include
'b.inc';
?>

この例で、変数$aはインクルードされた b.inc スクリプトの中でも利用可能です。しかし、 ユーザー定義の関数の中では変数の有効範囲はローカル関数の中となりま す。関数の中で使用された変数はデフォルトで有効範囲が関数内部に制限 されます。例えば、

<?php
$a
= 1; /* グローバルスコープ */

function Test() {
    echo
$a; /* ローカルスコープ変数の参照 */
}

Test();
?>

このスクリプトは、出力を全く行いません。これは、echo 命令がローカ ル版の $a 変数を参照しているにもかかわらず、こ のスコープでは値が代入されていないからです。この動作は、特にローカ ルな定義で上書きしない限りグローバル変数が自動的に関数で使用可能で ある C 言語と少々異なっていると気がつかれるかもしれません。C言語の ような場合、グローバル変数を不注意で変更してしまうという問題を生じ る可能性があります。PHP では、グローバル変数は、関数の内部で使用す る場合、関数の内部でグローバルとして宣言する必要があります。

globalキーワード

まず、globalの使用例を示します。

例 12-2. globalの使用

<?php
$a
= 1;
$b = 2;

function
Sum()
{
    global
$a, $b;

    
$b = $a + $b;
}

Sum();
echo
$b;
?>

上のスクリプトは、"3" を出力します。関数の内部で $a$b をグローバル宣言を行うことにより、両変数への参照は、グローバル変数 の方を参照することになります。ある関数により操作できるグローバル変 数の数は無制限です。

グローバルスコープから変数をアクセスする2番目の方法は、PHPが定義す る配列$GLOBALSを使用することです。先の例は、次 のように書き換えることができます。

例 12-3. globalのかわりに$GLOBALSを使用する

<?php
$a
= 1;
$b = 2;

function
Sum()
{
    
$GLOBALS['b'] = $GLOBALS['a'] + $GLOBALS['b'];
}

Sum();
echo
$b;
?>

配列$GLOBALSは連想配列であり、グローバル変数の 名前がキー、その変数の内容が配列要素の値となっています。 $GLOBALSスーパーグローバル であるため、$GLOBALSは全てのスコープに存在します。 以下にスーパーグローバルの効果を示す例を示します。

例 12-4. スーパーグローバルとスコープの例

<?php
function test_global()
{
    
// ほとんどの定義済み変数は"スーパー"ではなく、関数内の
    // ローカルスコープで有効とするには'global'をコールする必要があります。
    
global $HTTP_POST_VARS;
    
    echo
$HTTP_POST_VARS['name'];
    
    
// スーパーグローバルはどのスコープでも有効であり
    // 'global'をコールする必要がありません。
    // スーパーグローバルはPHP4.1.0以降で利用できます。
    // HTTP_POST_VARS は今や非推奨とされています。
    
echo $_POST['name'];
}
?>

静的変数の使用

変数のスコープに関する別の重要な機能は、静的 (static) 変数です。静的変数はローカル関数スコープのみに 存在しますが、プログラム実行がこのスコープの外で行われるようになっ てもその値を失わないません。次の例を見てください。

例 12-5. 静的変数が必要な場面の例

<?php
function Test()
{
    
$a = 0;
    echo
$a;
    
$a++;
}
?>

この関数は、コールされる度に$a0にセットし、"0" を出力するのでほとん ど役にたちません。変数を1増やす $a++ は、関数から外に出ると変数 $aが消えてしまうために目的を達成しません。現在 のカウントの追跡ができるようにカウント関数を使用できるようにするた めには、変数$aをstaticとして宣言します。

例 12-6. 静的変数の使用例

<?php
function Test()
{
    static
$a = 0;
    echo
$a;
    
$a++;
}
?>

こうすると、Test() 関数がコールされる度に$aの値 を出力し、その値を増加させます。

static変数は、再帰関数を実現する1つの手段としても使用されます。再帰 関数は、自分自身をコールする関数です。再帰関数を書くときには、無限 に再帰を行う可能性があるため、注意する必要があります。適当な方法に より再帰を確実に終了させる必要があります。次の簡単な関数は、中止す るタイミングを知るためにstatic変数$countを用いて、 10 回まで再帰を行います。

例 12-7. 再帰関数での静的変数の使用

<?php
function Test()
{
    static
$count = 0;

    
$count++;
    echo
$count;
    if (
$count < 10) {
        
Test();
    }
    
$count--;
}
?>

注意: 静的変数は、上の例に見られるような方法で宣言されます。 式の結果を静的変数に代入しようとすると、パースエラーが 発生します。

例 12-8. 静的変数の宣言

<?php
function foo(){
    static
$int = 0;          // 正しい
    
static $int = 1+2;        // 間違い(式を代入しています)
    
static $int = sqrt(121);  // 間違い(同じく式を代入しています)

    
$int++;
    echo
$int;
}
?>

グローバル変数と静的変数のリファレンス

PHP4を駆動するZend Engine 1では、 リファレンス変数の修正子 static および global を実装しています。 例えば、関数スコープ内にglobal 命令により実際にインポートされた真のグローバル変数は、 実際にグローバル変数へのリファレンスを作成します。 これにより、以下の例が示すように予測できない動作を引き起こす可能性 があります。

<?php
function test_global_ref() {
    global
$obj;
    
$obj = &new stdclass;
}

function
test_global_noref() {
    global
$obj;
    
$obj = new stdclass;
}

test_global_ref();
var_dump($obj);
test_global_noref();
var_dump($obj);
?>

この例を実行すると以下の出力が結果として表示されます。

NULL
object(stdClass)(0) {
}

類似の動作がstatic命令にも適用されます。 リファレンスは静的に保存することができません。

<?php
function &get_instance_ref() {
    static
$obj;

    echo
'Static object: ';
    
var_dump($obj);
    if (!isset(
$obj)) {
        
// Assign a reference to the static variable
        
$obj = &new stdclass;
    }
    
$obj->property++;
    return
$obj;
}

function &
get_instance_noref() {
    static
$obj;

    echo
'Static object: ';
    
var_dump($obj);
    if (!isset(
$obj)) {
        
// Assign the object to the static variable
        
$obj = new stdclass;
    }
    
$obj->property++;
    return
$obj;
}

$obj1 = get_instance_ref();
$still_obj1 = get_instance_ref();
echo
"\n";
$obj2 = get_instance_noref();
$still_obj2 = get_instance_noref();
?>

この例を実行すると以下の出力となります。

Static object: NULL
Static object: NULL

Static object: NULL
Static object: object(stdClass)(1) {
  ["property"]=>
  int(1)
}

この例は、static変数にリファレンスを代入した時に &get_instance_ref()関数を2回目に コールした際に保持されていないことを示しています。