hack的類型檢查器有三種模式:嚴格(strict)、局部(partial)和耦合(decl)。這些模式都基于一個個單獨的檔案,不同模式下的單獨檔案可以無縫地進行對接。每個檔案可以單獨聲明它的類型檢查模式,文法就是在檔案的第一行使用一個雙斜線的注釋。如下面的代碼所示:
<?hh // strict
如果檔案的第一行沒有上述類似的注釋資訊(比如第一行就是一個<?hh),将預設使用局部模式。
這三種模式間有很多的不同之處,通過對這些差異點的學習,我們可以了解類型檢查器的很多特性。下面是對每種模式的具體說明。
嚴格模式: <?hh // strict
嚴格模式最重要的特性就是,所有命名的函數(以及方法)都必須有傳回類型和參數類型标注,所有的屬性也必須進行類型标注。換句話說,任何可能出現類型标注的地方都必須進行标注。但是有如下例外:
閉包不需要對參數類型和傳回類型進行标注。
構造函數和析構函數不需要傳回類型标注,非要讓它們傳回些什麼也說不通。
在嚴格模式下有三條主要的限制:
使用任何在hack檔案中沒有定義過的命名實體注2都會引發錯誤。這就是說,嚴格模式下的hack代碼無法調用php代碼。但是嚴格模式下面的hack代碼可以調用局部模式或者耦合模式的hack代碼。
頂層檔案中的大多數代碼将會導緻錯誤。require系列語句是允許的注3,因為它們用于定義命名實體注4。
使用引用指派(比如$a=&$b),或者定義了“通過引用傳回值或者引用接收參數”的函數或方法,都會引發錯誤。
還有一些其他更小的不同點,我們将會進行補充。
為了充分利用類型檢查器的便利性,我們應該緻力于盡可能地寫嚴格模式的hack代碼。嚴格模式的hack是個健全的系統,這就意味着,如果你的代碼100%是在嚴格模式,那麼你在代碼運作時絕無可能引發類型錯誤的。這是一個非常強大的保證,你越接近這點越好。
局部模式: <?hh
局部模式放寬了嚴格模式的種種限制,它可以做它能夠做的任何類型檢查,但是它不要求代碼裡面必須寫所有的類型标注。此外,
如果你正在使用函數或者類,類型檢查器不能在hack檔案中發現的話,這并不會引發錯誤。類型檢查器會寬大地認為這些丢失的實體都定義在一個php檔案中,具體可以檢視1.4.3節的内容。
頂層代碼是允許的,但是不會檢查類型。是以為了最小化不執行類型檢查的代碼數量,在理想的情況下,你應該把所有的頂層代碼包裝進某個函數,然後在你唯一的頂層代碼語句中調用上述函數。下面是個相關的例子,最好不要這樣做:
更好的做法是把main函數的定義放到一個嚴格模式的檔案中。
引用是允許的,但是類型檢查器本質上假裝引用的相關代碼不存在,并不會試圖去規範它們的行為。在本例中,代碼的最後一行後面類型檢查器仍然會認為$a是個整型,盡管事實上它是一個字元串類型。
$a = 10;
$b = &$a;
$b = 'not an int';
簡單來說,可以在局部模式下使用引用,但是這破壞了類型安全,是以你應該盡量避免使用它們。
甚至對于一個從頭開始建立的新hack項目來說,這裡仍然有局部模式的檔案需求。在任何的腳本或者web應用中,總是需要有一定數量的頂層代碼的存在,它們将作為入口點代碼存在。是以你将至少需要一個局部模式的hack檔案。你需要使用局部模式來獲得超級全局變量,比如$_get、$_post和$argv,我們将在1.5.1節對此展開更詳細的闡述。
耦合模式: <?hh // decl
在耦合模式中,代碼是不會被檢查類型的。類型檢查器做的所有事情就是讀取和檢索所有的函數簽名以及定義在檔案中的類。(在耦合模式下仍然會觸發錯誤,比如無效的類型标注文法。)
耦合模式存在的目的在于,當遷移一個已經存在的php代碼庫到hack代碼時,提供一個過渡的功能。它為php和其他hack代碼模式提供了過渡的墊腳石。轉化一個php檔案到耦合模式的hack檔案是非常容易的,并且相對于保留為php檔案而言,也具有顯著的益處。首先,關于調用php代碼的類型檢查非常寬松。 (可以參見1.4.3節), 但是如果調用耦合模式的hack代碼将會更加嚴格地進行類型檢查。其次,嚴格模式的hack代碼是根本無法調用php代碼的,但是它可以調用耦合模式的hack代碼。
如果你正在編寫一個全新的、100%純正的hack代碼庫,那麼你不應該使用耦合模式。