談談 PHP 中的類型約束

起點

衆所周知,PHP 是弱類型語言,與其餘強類型語言項目,在這方面會有不少的坑,可是已經發展到 PHP 7 以後,PHP 也對類型約束有了所指,而且在許多流行框架中被大量使用好比Laravel,由於這確確實實在軟件開發過程當中不管是運行,仍是 IDE 的代碼提示都能爲咱們帶來極大的便利,下面就一步步來看看 PHP 中的類型約束。php

早期的約束

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

  • 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 以前,若是咱們要作類型約束,那麼就必須用到這些,這些方法對參數進行復雜的判斷,並處理錯誤返回給調用者。git

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

  • 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);

後話

類型驗證不但有利於咱們的程序在運行過程當中所獲得和返回的參數都是徹底符合預期的而且還有另外一個好處,那就是開發工具中的類型提示。oop

有時候可能會到一個狀況,某個方法傳遞了一個參數爲對象,裏面有一些方法,可是 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開發者應該永遠銘記

相關文章
相關標籤/搜索