下面為何不可以
function done() { require_once 'vendor/autoload.php'; use Qiniu\Auth; use Qiniu\Storage\UploadManager; #省略 }
?>
必須寫成
require_once 'vendor/autoload.php';use Qiniu\Auth;use Qiniu\Storage\UploadManager;function done() { #省略 }
?>
回複內容:
下面為何不可以
function done() { require_once 'vendor/autoload.php'; use Qiniu\Auth; use Qiniu\Storage\UploadManager; #省略 }
?>
必須寫成
require_once 'vendor/autoload.php';use Qiniu\Auth;use Qiniu\Storage\UploadManager;function done() { #省略 }
?>
語言結構使然,如果你瞭解PHP命名空間相關性質我想你是不會這麼問的。
use
的作用僅僅是用簡短名稱替代長名稱,或者是用別名替代本名,是一個沒有語義和實效的“文法糖”。
所以消滅use
的運行時開銷是一個非常合理的選擇。因此php規定use
在解析階段(parse)就被處理。
和運行時才現場執行(相當於語句)的echo
、require
等不一樣,use
語言結構是在解析(parse)階段預先掃描、提早處理的。以上是前提。
而解析操作本身,非常的單純,僅僅是從頭推到尾,識別一個個的語言關鍵字,並確保文法規則不被違反。我們可以做一個簡單的實驗:
我不知道紫媽會不會用我的臉滾鍵盤,但我知道php肯定不會讓我過解析——
你說第 5 行永遠都不會執行?解析器根本不知道,也不關心。
但對於一個花括弧括住的範圍(scope)而言,事情就變得複雜了。因為一個小範圍的執行順序很可能是亂的——可以回頭、可以通過調用來亂跳等等。例如:
namespace NS1;class ClassName { }function f() { return new ClassName();}for ($i=0; $i<2; $i++) { $a = new ClassName(); $b = f(); use NS2\ClassName; $c = new ClassName();}
如果我們認為use
會影響它後邊的所有內容,那麼此時$a
和$b
的指派陳述式到底在不在use
的後邊?
按照語義,第1次迴圈不在,第2次迴圈在,也就是說同一行會產生兩種不同的語義。
但解析器不可能理解,也不可能維護得了這種邏輯。實現這種邏輯,必然產生一個運行時的開銷(因為要介入程式運行當時才能確定的狀態),而這是use
的設計本意要避免的。
所以use
只能擺在檔案的最外層範圍中。只有這個範圍的範圍是一線平推,不可能回退,也不可能出現跳轉。
試分析以下use
真正的作用範圍,就可以看到邏輯中,處處都是為了方便解析器處理而設計的:
從use
出現的行開始(簡單的開始規則)
見到namespace
結束(簡單的終止規則)
見到檔案尾結束(解析器的運行不能跨檔案)
事實上和嚴謹設計、環環相扣的語言特性不同,很多的文法糖都並沒有太多的道理可講。
能像use
這樣,從最初的設計目的,從而推匯出其設計必然限制的文法糖,其實挺少的。
對於文法糖,死記、活用、理解原理但別想太多,這才是我們作為語言使用者的營生之道。