神秘的變數名特性

來源:互聯網
上載者:User

    在PHP語言中,變數都是儲存在雜湊表中,稱為變數符號表,其中變數名為雜湊表的鍵,變數名對應的容器zval的指標為雜湊表中的值。所有全域變數放在一張主符號表中(也就是數組$GLOBALS對應的雜湊表)。PHP語言有個特性,變數在命名時,$變數標識符後不能以數字開頭。例如我們在以下代碼:

<?php

$111= "my";

?>

會報如下錯誤:Parse error: syntax error, unexpected T_LNUMBER, expecting T_VARIABLE or '$' in...

 

從錯誤的描敘來看,這是一個語法錯誤,於是我們推論對變數名合法性的判斷應該是在編譯時間的文法分析階段。

為了證明觀點,我們可以試著在執行階段定義一個數字字元開頭的變數:

<?php

$a   = 111;

$$a = "my"; //以變數$a的值作為變數名

echo $$a;

var_dump($GLOBALS);

?>

運行之後發現不報錯,並且在全域符號表$GLOBALS中發現相關符號:

  ["a"]=>
  int(111)
  ["111"]=>
  string(2) "my"

 

這樣我們就定義了一個全數字字元命名的變數,似乎違背了PHP的規則,但是確實做到了。

(讀者可以試著輸出$GLOBALS["111"]的值,雖然有值,但是結果卻為NULL,這個是PHP中一個類型轉換的特性)

 

在這段代碼中,$$a = "my"這條語句具有多態性,只有當實際執行到這條語句的時候,我們才能確定變數名,PHP在文法分析階段無法知道這個變數名會是什麼,所以就不會報錯,在執行階段,PHP語言不判斷變數名的合法性,於是就產生了這樣一個叛逆的變數。

 

知道了這個特性之後,馬上會想到一個另外一個特殊的變數:$this

在類的方法中,$this關鍵字用來指向當前類的對象執行個體,如果對$this進行賦值操作,會發生什麼事情?

 <?php<br />class Person<br />{<br /> protected $_name = "phper";</p><p> protected $_age = 18;</p><p> public function getName()<br /> {<br /> $this = 123;<br /> return $this->_name;<br /> }</p><p> public function getAge()<br /> {<br /> return $this->_age;<br /> }<br />}</p><p>$p = new Person();<br />$p->getName();<br />?>

 

 執行代碼,報錯:

Fatal error: Cannot re-assign $this in...

 

PHP對$this變數做了一定保護措施,但是這個保護措施也不是完全的,我們利用前面的方法在執行期改變$this的值, 修改後的getName方法為:

public function getName()<br /> {<br /> $a = "this";<br /> $$a = 123;<br /> echo $this;<br /> return $this->_name;<br /> }

 

然後執行代碼,發現能夠順利執行,$this的值變成了123, 由此可以判斷對$this關鍵字的保護也僅限與文法分析階段。

 

當$this的值變成123之後,按理說$this->_name肯定會報錯的,但是居然代碼能正常執行,這個確實很讓人覺得不可思議。於是推斷

$this->_name這樣的引用方式和一般的物件變數引用方式在有差異,我們繼續對getName做修改:

public function getName()<br /> {<br /> $a = "this";<br /> $$a = 123;<br /> $b = 123;<br /> echo $this;<br /> echo $b;<br /> $this->getAge();<br /> $b->getAge();<br /> return $this->_name;<br /> }

 

這段代碼中,$this和$b的值都等於123,但是$this->getAge()可以順利執行,$b->getAge()卻報錯:

Fatal error: Call to a member function getAge() on a non-object...

 

這是一個詭異的問題,於是可以推斷$this->getAge()和$b->getAge()在編譯之後對應的op handler肯定有所差異,於是查看之。

 

通過vld查看op的資訊

$this->getAge()對應的為:

ZEND_INIT_METHOD_CALL    RES[  IS_UNUSED  ]         OP1[  IS_UNUSED  ] OP2[  IS_CONST (8142027) 'getAge' ]

 

$b->getAge()在對應的為:

ZEND_INIT_METHOD_CALL    RES[  IS_UNUSED  ]         OP1[  IS_CV !0 ]      OP2[ ,  IS_CONST (8142039) 'getAge' ]

 

雖然對應了同樣的op code,但是由於運算元的不同(前者不使用OP1,後者使用OP1, 且OP1的值!0表示$b),同一個op code對應同一類handler,然後根據運算元的類型確定到此類handler中的某一個handler。

 

$this->getAge()對應的handler處理為:ZEND_INIT_METHOD_CALL_SPEC_UNUSED_CONST_HANDLER

$b->getAge()    對應的handler處理為:ZEND_INIT_METHOD_CALL_SPEC_CV_CONST_HANDLER

 

這就是它們的差異了,在執行

$my = new Person();
$name = $my->getName();

 

調用getName的時候,getName()的範圍(scope)已經被設定成$name對象中,文法分析的時候對於getName中$this->getAge()這樣的調用時只是在當前範圍(scope)中調用getAge()函數,不會理會$this的具體值,而在$b->getAge()這樣的調用時,會關心$b所對應的值。

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.