変数のスコープ

変数のスコープは、その変数が定義されたコンテキストです。ほとんどの 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の使用例を示します。

例1 global の使用

<?php
$a 
1;
$b 2;

function 
Sum() 
{
    global 
$a$b;

    
$b $a $b;


Sum();
echo 
$b;
?>

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

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

例2 globalのかわりに$GLOBALSを使用する

<?php
$a 
1;
$b 2;

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


Sum();
echo 
$b;
?>

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

例3 スーパーグローバルとスコープの例

<?php
function test_superglobal()
{
    echo 
$_POST['name'];
}
?>

注意:

global キーワードを関数の外部で使ってもエラーにはなりません。 そのファイルが関数の内部からインクルードされたときに使うことができます。

static 変数の使用

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

例4 static 変数が必要な場面の例

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

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

例5 static 変数の使用例

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

こうすると、$a は関数が最初にコールされたときにのみ初期化され、 test() 関数がコールされるたびに $a の値を出力してその値を増加させます。

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

例6 再帰関数でのstatic変数の使用

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

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

static 変数には、定数式の結果としての値を代入できますが、 関数呼び出しのような動的な式の結果を代入すると、パースエラーになります。

例7 static 変数の宣言

<?php
function foo(){
    static 
$int 0;          // 正しい
    
static $int 1+2;        // 正しい
    
static $int sqrt(121);  // 間違い(関数を使っています)

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

(オーバーライドされていない場合) 内部でstatic 変数を使ったメソッドも継承されます。 PHP 8.1.0 以降は、継承されたメソッドは、 親クラスのメソッドとstatic 変数を共有するようになりました。 つまり、メソッド内でのstatic 変数も、 static プロパティと同じ振る舞いになったということです。

例8 継承されたメソッドのstatic 変数を使う

<?php
class Foo {
    public static function 
counter() {
        static 
$counter 0;
        
$counter++;
        return 
$counter;
    }
}
class 
Bar extends Foo {}
var_dump(Foo::counter()); // int(1)
var_dump(Foo::counter()); // int(2)
var_dump(Bar::counter()); // int(3), PHP 8.1.0 より前のバージョンでは int(1)
var_dump(Bar::counter()); // int(4), PHP 8.1.0 より前のバージョンでは int(2)
?>

注意:

static 変数の宣言は、コンパイル時に解決されます。

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

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

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

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

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

上の例の出力は以下となります。

NULL
object(stdClass)#1 (0) {
}

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

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

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

function &
get_instance_noref() {
    static 
$obj;

    echo 
'Static object: ';
    
var_dump($obj);
    if (!isset(
$obj)) {
        
$new = new stdclass;
        
// Assign the object to the static variable
        
$obj $new;
    }
    if (!isset(
$obj->property)) {
        
$obj->property 1;
    } else {
        
$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)#3 (1) {
  ["property"]=>
  int(1)
}
+

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

関連キーワード:  変数, 関数, static, スコープ, 再帰, グローバル, 内部, 出力, GLOBALS, 宣言