1. 程式人生 > >PHP中引用的詳解

PHP中引用的詳解

1. PHP中引用的特性

PHP中引用意味著用不同的名字訪問同一個變數內容,引用不是C的指標(C語言中的指標裡面儲存的是變數的內容,在記憶體中存放的地址),是變數的另外一個別名或者對映。注意在 PHP 中,變數名和變數內容是不一樣的,因此同樣的內容可以有不同的名字。最接近的比喻是 Unix 的檔名和檔案本身――變數名是目錄條目,而變數內容則是檔案本身。引用可以被看作是 Unix 檔案系統中的緊密連線或者wins的快捷方式。

2.變數的引用

PHP 的引用允許用兩個變數來指向同一個內容。

<?php  
     $a = " ABC " ;  
     $b   =& $a ;  
     echo   $a ; // 這裡輸出:ABC  
     echo   $b ; // 這裡輸出:ABC  
     $b = " EFG " ;  
     echo   $a ; // 這裡$a的值變為EFG 所以輸出EFG  
     echo   $b ; // 這裡輸出EFG  
?>

這意味著 $a 和 $b 指向了同一個變數。

注: $a 和$b 在這裡是完全相同的,這並不是$a 指向了$b 或者相反,而是$a 和$b 指向了同一個地方。

3 物件的引用(物件的賦值傳遞)

在PHP5中兩者對映到同一物件,不需要使用引用符。

<?php  
class fooclass{  
    public $att ;   
}  
$a = new fooclass();  
$b = $a;   
$a->att = 1;  
$b->att = 2;  
echo 'a obj:',($a->att),'<br>';  
echo 'b obj:',($b->att);  
/** 
 * 結果: 
 * a obj:2 
   b obj:2 
 */

在PHP5中 物件的賦值是個引用的過程。上列中

$a = new fooclass();  
$b = $a;  

4.函式的引用傳遞(傳址呼叫)

引用傳遞的定義必須在函式定義中體現,在函式使用中,不要寫成foo(&$a)的樣子,否則會報"Call-time pass-by-reference過時"的警報。

<?php  
function  foo( & $a ){  
     $a = $a + 100 ;  
}  
$b = 1 ;  
echo   $b ; // 輸出1  
foo( $b );// 這裡$b傳遞給函式的其實是$b的變數內容所處的記憶體地址,通過在函式裡改變$a的值 就可以改變$b的值了  
echo   " <br> " ;  
echo   $b ; // 輸出101

 

但是在函式“call_user_func_array”中,若要引用傳參,就得需要 & 符號,如下程式碼所示:

<?php
function  test( & $b ){  
     $b ++ ;  
}  
$c = 0 ;  
call_user_func_array ( 'test' , array ( & $c ));  
echo   $c ;

 5 函式的引用返回

<? php  
function   & test()  
{  
     static   $b = 0 ; // 申明一個靜態變數  
     $b = $b + 1 ;  
     echo   $b ;  
     return   $b ;  
}  
$a = test(); // 這條語句會輸出 $b的值 為1  
$a = 5 ;  
$a = test(); // 這條語句會輸出 $b的值 為2  
$a =& test(); // 這條語句會輸出 $b的值 為3  
$a = 5 ;  
$a = test(); // 這條語句會輸出 $b的值 為6  
?>

通過這種方式$a=test();得到的其實不是函式的引用返回,這跟普通的函式呼叫沒有區別,只是將函式的值賦給$a而已, 而$a做任何改變 都不會影響到函式中的$b。
而通過$a=&test()方式呼叫函式呢, 他的作用是 將return $b中的 $b變數的記憶體地址與$a變數的記憶體地址 指向了同一個地方
即產生了相當於這樣的效果($a=&$b;) 所以改變$a的值 也同時改變了$b的值 所以在執行了

$a=&test();
$a=5;

以後,$b的值變為了5。

注意:

和引數傳遞不同,這裡必須在兩個地方都用 & 符號:定義和使用時都需要加&符號。

& test()指出返回的是一個引用,而不是通常的一個拷貝。
$a =& test()同樣也指出 $a 是作為引用的繫結,而不是通常的賦值。

這裡是為了讓大家理解函式的引用返回才使用靜態變數的,其實函式的引用返回多用在物件中:

<?php  
/** 
 * This is the way how we  use  pointer to access variable inside the  class . 
 */  
class  talker{  
     private   $data   =   ' Hi ' ;  
     public   function   &get(){  
         return   $this -> data;  
    }  
    public   function  out(){  
         echo   $this -> data;  
    }    
}  
$aa   =   new  talker();  
$d   =   &$aa -> get();  
$aa -> out();  
$d   =   ' How ' ;  
$aa -> out();  
$d   =   ' Are ' ;  
$aa -> out();  
$d   =   ' You ' ;  
$aa -> out();  
//the output is  " Hi How Are You "

6 global 引用

當用 global $var 宣告一個變數時實際上建立了一個到全域性變數$_GLOBAL的引用。具體可以寫成如下語句:

<?php  
     $var=&$GLOBALS [ "var" ];  
?>

把  global $var; 當成是  $var =& $GLOBALS['var']; 的簡寫。從而將其它引用賦給  $var 只改變了本地變數的引用。

如果全域性變數賦值給另外另外一個變數$var_test,刪除這個$var_test,也不會 unset 全域性變數

<?php  
global $var;  
$var = "Example variable";  
$var_test =  &  $GLOBALS [ 'var' ];  
echo '$var_test is set to ',$var_test,'<br>'; //  
$var_test = 11;  
unset($var_test);  
echo '$GLOBALS[var] is set to ',$GLOBALS ['var'],'<br>';  
/** 
 * 輸出結果: 
$var_test is set to Example variable 
$GLOBALS[var] is set to 11 
 */

如果在一個函式內部給一個宣告為 global 的變數賦於一個引用,該引用只在函式內部可見。可以通過使用 $GLOBALS 陣列避免這一點。

<?php
$var1 = "Example variable";  
$var2 = "";  
function global_references($use_globals) {  
    global $var1, $var2;  
    if (!$use_globals) {  
        $var2 =& $var1; // visible only inside the function  
    } else {  
        $GLOBALS["var2"] =& $var1; // visible also in global context  
    }  
}  
global_references(false);  
echo "var2 is set to '$var2'", '<br>'; // var2 is set to ''  
global_references(true);  
echo "var2 is set to '$var2'"; // var2 is set to 'Example variable'
$var2 =&$var1; // visible only inside the function  
那麼實際上就是改變了$var2的對映關係,之前與$_GLOBAL['var2']的對映就斷開了,再也無法修改函式外部$var2的內容。此特性也同樣作用在用引用傳遞函式引數的例子中。

另外,需要注意在函式中unset($var2)不會真正釋放變數內容,可以看做只是斷開了函式內部$var2的對映而已。

7 . 取消引用 unset與=null

當你 unset 一個引用,只是斷開了變數名和變數內容之間的繫結。這並不意味著變數內容被銷燬了。例如:

<?php  
   $a   =   1 ;  
   $b   =&   $a ;  
   unset  ( $a );  
   echo $b; //輸出:1:  

使用unset($a)與$a=null的結果是不一樣的。如果該塊記憶體只有$a一個對映,那麼unset($a)與$a=null等價,該記憶體的引用計數變為0,被自動回收;如果該塊記憶體有$a和$b兩個對映,那麼unset($a)將導致$a=null且$b不變的情況,而$a=null會導致$a=$b=null的情況。
原因:某變數賦值為null,將導致該變數對應的記憶體塊的引用計數直接置為0,被自動回收。

8 . $this

在一個物件的方法中,$this 永遠是呼叫它的物件的引用。

9. PHP引用使用注意事項

很多人誤解Php中的引用跟C當中的指標一樣,事實上並非如此,而且很大差別。C語言中的指標除了在陣列傳遞過程中不用顯式申明外,其他都需要使用*進行定義,而php中對於地址的指向(類似指標)功能不是由使用者自己來實現的,是由Zend核心實現的,php中引用採用的是“引用計數、寫時拷貝”的原理,(寫時複製(Copy-on-Write,也縮寫為COW),顧名思義,就是在寫入時才真正複製一份記憶體進行修改。)

就是除非發生寫操作,指向同一個地址的變數或者物件是不會被拷貝的,比如下面的程式碼:
$a = array('a','c'...'n');
$b = $a;

如果程式僅執行到這裡,$b和$a是相同的,但是並沒有像C那樣,$a和$b佔用不同的記憶體空間,而是指向了同一塊記憶體,這就是php和c的差別,並不需要寫成$b=&$a才表示$b指向$a的記憶體,zend就已經幫你實現了引用,並且zend會非常智慧的幫你去判斷什麼時候該這樣處理,什麼時候不該這樣處理。