PHP中坑人不償命的作用域

一. 變量定義 

<?php
    function foo() {
        return 1;
    }
    if(!$a = foo()) print 'x';

等價於:

<?php
    function foo() {
        return 1;
    }
    $a = foo();
    if(!$a) print 'x';


二. 全局和局部
PHP裏面只有全局作用域和函數作用域,沒有塊作用域例如,循環語句內定義的變量,其作用域認爲是外部的,即循環體結束時依然有效

<?php
$a = 1;
for($j=1; $j<5; $j++) {
    $a = $a + $j;
}

echo $a . PHP_EOL; //打印出6
echo $j . PHP_EOL; //打印出5

--------------------------------------------
如果在函數中將變量設置爲global,則表示接下來該變量改爲取全局的那個

<?php
$a = 1;
function F() {
    $a = 2;       //局部變量屏蔽全局變量    
    echo "$a"; //使用本函數內的局部變量$a
    echo PHP_EOL;
    global $a; //接下來都是使用全局變量$a
    echo "$a"; 
}
F();

使用global關鍵字

<?php
$x = 10;
$y = 20;
function test() {
    global $x,$y; //表示使用全局變量$x和$y
    $y = $x + $y;
}
test();
echo $y; //輸出30

-----------------------------------

<?php
 $var = 1;
 function foo() {
     global $var;
     unset($var);               // unset local $a, the global $a is still there.
     //var_dump($var);            // Undefined variable: var
     var_dump($GLOBALS['var']); 
}
foo();
var_dump($var);

將上面那個和這下面這個代碼進行對比:

<?php
 $var = 1;
 function bar() {
     global $var;
     unset($GLOBALS['var']);    // 解除全局的$a, 局部的$a依然在
     var_dump($var);            
     //var_dump($GLOBALS['var']); // Undefined index: var
}
foo();
//var_dump($var);                // Undefined variable: var

 'unset($var);'類似於C語言中 'var = NULL;'(假設var是個指針變量), 不同於 'free(var);' 

-------------------------------------

<?php
function F() {
    $a = 0;
    $b = 1;
    $GLOBALS['a'] = &$b; // 注意點(1)方括號內是'a',寫成"a"或'$a'都會報錯 (2)$b前面有&符號
    echo "$a" . PHP_EOL;
    echo "$GLOBALS[a]" . PHP_EOL; //相當於輸出$b
    $b = 2;
    echo "$GLOBALS[a]" . PHP_EOL; //相當於輸出$b
}
F();
echo "$a" . PHP_EOL;  //在之前曾經定義了超級全局變量$GLOBALS['a']

GLOBALS['variable'] 和 global關鍵字 的作用是不一樣的

<?php
function F() {
    $GLOBALS['a'] = 1; //改爲"global $a = 1;"就會報錯,因爲函數外並沒有定義$a
}
F();
echo "$a" . PHP_EOL;  //因爲F()函數中定義了$GLOBALS['a']

三. 靜態變量
基類實例共享的靜態成員變量,與子類實例共享的靜態成員變量,兩者互不影響

<?php
class Base {
    function test($delta = 0) {
        static $v = 0;
        $v += $delta;
        return $v;
    }
}

class Derived extends Base {}

$base1 = new Base();
$base2 = new Base();
$derived1 = new Derived();
$derived2 = new Derived();

$base1->test(3);
$base2->test(4);
$derived1->test(5);
$derived2->test(6);

var_dump([ $base1->test(), $base2->test(), $derived1->test(), $derived2->test() ]);

結果是 array(4) { [0]=> int(7) [1]=> int(7) [2]=> int(11) [3]=> int(11) }

基類 $base1和$base2共享同一個靜態變量$v,子類 derived1和$derived2 共享另外的同一個靜態變量 $v 
----------------------------------------------
在一個函數中如果多次聲明靜態變量,以最後一次爲準

<?php 
function Test(){ 
    static $count = 0;
    echo $count;
    static $count = 1;
    echo $count;
    static $count = 2; 
    echo $count; 
 }

 Test();

結果是輸出三個222
------------------------------------------------------
PHP中的靜態變量只能使用"self::"或者"類名::"訪問,不能使用"$this"訪問;反之,類的成員變量未聲明爲const及static時,只能使用"$this"訪問,不能用"::"訪問

<?php
class Test_Class {
   static $a = 0;
   public function ReturnVar(){
     return $this->a;  //$this->a改爲self::$a才能編譯通過
   }
}

$b = new Test_Class();
echo $b->ReturnVar();

實例可以調用靜態方法,但是靜態方法中不可以有$this

<?php
class F {
    public static function Var() {
        echo "Var";
    }
}
$b = new F();
echo $b->Var() . PHP_EOL; //輸出Var

-------------------------------------

<?php
class example {
    public static $s = 'unchanged';
    public function set() {
        $s = 'a';
        echo $this::$s; //換成$this->s則報錯
        echo PHP_EOL;
        echo $s;
    }
}

$o = new example;
$o->set();

四. 文件包含
include()函數,與調用者是同一個作用域。如果在函數中使用include(),則被包含文件會把函數的作用域當作是被包含文件的全局作用域

<?php
$foo = 'aaa';
$bar = include('include.php');
echo($foo.' / '.$bar);

include.php代碼: 

<?php
 $foo = 'bbb';
 return $foo;

輸出結果是 bbb / bbb,而不是 aaa / bbb
-------------------------------------
// b.php

<?php
$b = "something";
function b() {
    global $b;
    $b = "something new";
}
b();
echo $b;

直接運行b.php的輸出結果是"something new"。那如果運行下面的a.php,當然a.php和b.php在同一目錄下。

// a.php

<?php 
function a() { 
    include("b.php"); 
}
a();

本意是想輸出"something new",但實際上卻是輸出"something"。
把include的文件內容代入,相當於下面的代碼:

<?php
function a() { 
    $b = "something";
    function b() {
        global $b;
        $b = "something new";
    }
    b();
    echo $b; 
}
a();

第5行的$b取的是整個a.php文件中的全局$b,而不是第3行定義的$b,所以並沒有改變第3行的$b的值,於是第9行執行echo $b時,輸出的是"something"。

解決方法,將b.php中的"$b = something;"前加入一行"global $b;",就會輸出"something new"了。

使用require() 、require_once()、 include_once()的效果,跟include()的一樣。
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章