Forum und email

變數範圍

變數的範圍只是在其所定義的空間內存在。在大部份的情況下,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 是以參照的方式來實現 staticglobal 的。例如,一個真正的全域變數使用 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() 函數時,它是不會記住之前的值的。