PHP 手冊
前一頁 | 章 12. 各類變數 | 下一頁

# 變數範圍

變數的範圍只是在其所定義的空間內存在。在大部份的情況下，PHP 的變數只有一個單一的範圍。這單一的範圍也包含了以 include 和 require 方式引入的檔案。例如：

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

例子中，變數 $a 也存在于被包含入的程式 `b.inc` 中。但是在使用者自訂的函數中，一個區域性的函數範圍將被引用。任何在函數內使用的變數在預設的情況下只局限於該函數的範圍內。例如：

`<?php $a = 1; /* global scope */ function Test() { echo $a; /* reference to local scope variable */ } Test(); ?>`

這個程式不會輸出任何東西，因為 echo 述句使用了本區域版本的 $a 變數，而該本區域變數並未曾被分配一個值。你會發現這和 C 語言的做法不同，因為 C 的全域變數是自動的提供給各函數，除非在本域定義中指明撤銷。此做法可能會引致一些問題，如使用者不慎地更改了全域變數的值。在 PHP 中，全域變數在本域使用前必須先宣佈為全域。例子：

`<?php $a = 1; $b = 2; function Sum() { global $a, $b; $b = $a + $b; } Sum(); echo $b; ?>`

上述例子將會輸出 "3"。在函數內宣佈 $a 及 $b 為全域後，所有涉及該兩個變數的使用將自動指向全域的版本。PHP 沒有限定一個函數可以使用的全域變數的數量。

第二種存取全域範圍中的變數方法是使用一個由 PHP 特別定義的陳列：$GLOBALS。前面的例子可以重寫為：

`<?php $a = 1; $b = 2; function Sum() { $GLOBALS["b"] = $GLOBALS["a"] + $GLOBALS["b"]; } Sum(); echo $b; ?>`

$GLOBALS 是一個關聯陳列 (associative array)，全域變數的名即為索引鍵，而該變數的內容即為陳列的值。有否留意到 $GLOBALS 在任何一個範圍都出現？那是因為 $GLOBALS 是一個superglobal。這是一個示範 superglobals 強大功能的例子：

`<?php function test_global() { // Most predefined variables aren't "super" and require // 'global' to be available to the functions local scope. global $HTTP_POST_VARS; print $HTTP_POST_VARS['name']; // Superglobals are available in any scope and do // not require 'global'. Superglobals are available // as of PHP 4.1.0 print $_POST['name']; } ?>`

變數範圍的另一個重要功能為靜態變數。靜態變數只在本域函數範圍內存在，但是當程式執行離開此範圍時，它並不會喪失它的值。看看下面的例子：

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

此函數沒有什麼用處，因每一次執行它時，它將 $a 設為 0 然後列印出 "0"。$a++ 增加了 $a 的值，但並沒有實際用途因為一旦離開了該函數，變數 $a 也隨之消失。要設計一個有用的、不會丟失當前計數的計數函數，我們可以將 $a宣佈為靜態：

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

現在，每當函數 Test() 被呼叫時，它會列印出 $a 的值，然後加上一。

靜態變數也提供一種處理遞迴函數的方法。遞迴函數是一種呼叫自己的函數。編寫遞迴函數時必須留意，因為若編寫錯誤，它有可能會無定限地遞迴。您必須確定足夠的方式來終止遞迴。下列簡單的函數將遞迴地數到 10，利用靜態變數 $count 來斷定什麼時候停止：

`<?php function Test() { static $count = 0; $count++; echo $count; if ($count < 10) { Test (); } $count--; } ?>`

驅動 PHP 4 的 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)
    }

上述例子示範了在指派一個參照給予一個靜態變數後，當您第二次呼叫 &get_instance_ref() 函數時，它是不會記住之前的值的。

前一頁 | 內容 | 下一頁
預設的變數 | 上一層 | 可變變數