談談 PHP 中的類型約束

起點

衆所周知,PHP 是所類型語言,與其他強類型語言項目,在這方面會有很多的坑,但是已經發展到 PHP 7 之後,PHP 也對類型約束有了所指,並且在許多流行框架中被大量使用比如Laravel,因爲這確確實實在軟件開發過程中無論是運行,還是 IDE 的代碼提示都能爲我們帶來極大的便利,下面就一步步來看看 PHP 中的類型約束。

早期的約束

雖然 PHP 是隱式轉換,但是在實際開發中也會存在一些無法轉換的窘境,當然這些問題我們在開發階段很容易發現,但是如果是一些動態的內容導致不可控就會呈現在用戶面前,也就是 BUG ,在 PHP 中有一批以 is_* 開頭的方法用來做一些簡單類型判斷(這其中一些方法也是新方法沒有翻譯的基本都是)。

  • is_array — 檢測變量是否是數組
  • is_bool — 檢測變量是否是布爾型
  • is_callable — 檢測參數是否爲合法的可調用結構
  • is_countable — Verify that the contents of a variable is a countable value
  • is_double — is_float 的別名
  • is_float — 檢測變量是否是浮點型
  • is_int — 檢測變量是否是整數
  • is_integer — is_int 的別名
  • is_iterable — Verify that the contents of a variable is an iterable value
  • is_long — is_int 的別名
  • is_null — 檢測變量是否爲 NULL
  • is_numeric — 檢測變量是否爲數字或數字字符串
  • is_object — 檢測變量是否是一個對象
  • is_real — is_float 的別名
  • is_resource — 檢測變量是否爲資源類型
  • is_scalar — 檢測變量是否是一個標量
  • is_string — 檢測變量是否是字符串
  • is_a — 如果對象屬於該類或該類是此對象的父類則返回 TRUE
  • is_subclass_of — 如果此對象是該類的子類,則返回 TRUE

在 PHP 5 之前,如果我們要做類型約束,那麼就必須用到這些,這些方法對參數進行復雜的判斷,並處理錯誤返回給調用者。

但是在 PHP 5 以來,在面向對象中,爲方法帶來了類型約束,然而這些都非常的雞肋,從文檔上可以看到。

  • PHP 5 支持 對象接口
  • PHP 5.1 支持 數組
  • PHP 5.4 支持匿名函數
  • 類型約束不能用於標量類型如 intstringTraits 也不允許。

在 PHP 5 中其實光是第一條,就夠大部分場景使用,但是也有一些知名問題,比如最後一條的 不支持標量類型 ,也就是說支持不是很全面,而且還有一種情況沒有考慮 那就 null 雖然 null 是一個特殊類型,但是有時候當數據不可控時也會出現,而且,在 PHP 5 階段,類型約束並沒有被很好的使用,或許是那個時候並不是那麼的重視,畢竟弱類型是 PHP 的一大特點,但也是致命傷,甚至很多時候被強類型語言牽着鼻子走。

PHP 7

PHP 7 相對於先前的PHP版本可謂是煥然一新。

比較扎眼的就是完善了對類型限制的支持,補上了之前的短缺,包括標量類型返回值類型,而且,在 PHP 7.1 中還加入了嚴格類型驗證

強制類型驗證

strict_types/declare()指令

  • 默認情況下,所有的PHP文件都處於弱類型校驗模式。新的declare指令,通過指定strict_types的值(1或者0),1表示嚴格類型校驗模式,作用於函數調用和返回語句;0表示弱類型校驗模式。
  • declare(strict_types=1)必須是文件的第一個語句。如果這個語句出現在文件的其他地方,將會產生一個編譯錯誤,塊模式是被明確禁止的。
  • 類似於encoding指令,但不同於ticks指令,strict_types指令隻影響指定使用的文件,不會影響被它包含(通過include等方式)進來的其他文件。該指令在運行時編譯,不能修改。它的運作方式,是在opcode中設置一個標誌位,讓函數調用和返回類型檢查符合類型約束。

舉個🌰

// 非嚴格模式
// 1️⃣
function testInt():int{
    return 0.01;
}
// 2️⃣
function testStr():string{
    return true;
}
// 3️⃣
function testBool():bool{
    return "1";
}
// 4️⃣
function testInt2():int{
    return "1string";
}

如你所見,上面的代碼 通通都沒有問題,都不會出現異常,甚至在部分 PHP 7.2 以下的版本中,4️⃣都是可以通過的。這是因爲 PHP 7 雖然有了嚴格類型驗證,但是默認情況下並沒有啓用,而是需要手動去啓用,如果手動設置啓用了之後,返回或者傳遞的參數不符合聲明的類型,那麼 PHP 會直接拋出一個 TypeError 錯誤,要求你去處理。啓用強制類型驗證 只需要在 PHP 文件的頂部加入以下代碼即可。

declare(strict_types=1);

後話

類型驗證不但有利於我們的程序在運行過程中所得到和返回的參數都是完全符合預期的並且還有另一個好處,那就是開發工具中的類型提示。

有時候可能會到一個情況,某個方法傳遞了一個參數爲對象,裏面有一些方法,但是 IDE 就是不提示。

interface UserInfo{
    getSex();
}
interface User{
    getUserInfo();
    getUserId();
    getUserName();
}
function getUserSex($user){
    // 你會發現 在這裏 IDE 並不能很好的給你提示代碼,和一些可以用的方法
    return $user->getUserInfo()->getSex();
}
class VipUser importants User(){
    // TODO .....
}
getUserSex(new VipUser());

這種情況下就 2 個解決方案了,如果你是項目,因爲自 PHP 5 開始就支持對象的類型聲明瞭,所以這裏就不是那麼擔心,直接聲明類型就好了。

function getUserSex(User $user){
    // 這裏就可以提示了 
    return $user->getUserInfo()->getSex();
}

當然 還有方法就是使用 PHPDoc,即註解方案,這個方案已經在 PSR-5 中,雖然還沒有完全通過,但是在 早期也有 PHPDoc 的一些 unofficial 的,而且主流 IDE 已經完全實現了,來協助我們提高開發體驗。

最後

總結一下,PHP 中接近完善的 類型約束,讓我們之前的一些不可能變成了可能,讓一些不可靠變的更加的可靠,降低了代碼中一些因爲類型約束而導致的問題,從源頭提升了在開發工具中的開發體驗 。

參考資料

PHP7類型提示:作爲PHP開發者應該永遠銘記

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