2023年1月31日

PHP 8.2 發布,動態屬性要被廢棄了!迎來更完整的型別系統,新增唯讀類別

2022 年 12 月 8 號 PHP 8.2 發布了,這是 PHP 8 的第二次重大更新。從 2020 年開始,PHP 維持著每年更新一個次版本的進度,2020 年 11 月的 8.0,2021 年 11 月推出了 8.1。

作為最熱門的 PHP 框架,Laravel 9 及 10 都支援 PHP 8.2,如果還在使用 Laravel 8 及以前的版本,最多只支援到 8.1。

那我們話不多說,趕緊來看一下 PHP 8.2 有哪些重大的調整吧。

PHP 8.2 重大變動

Readonly classes 唯讀類別

PHP 8.1 的時候,PHP 推出了唯讀的屬性,而 8.2 則進化成唯讀類別。

一個類別被標記為唯讀時,它的所有屬性都會自動變成唯讀屬性 (Readonly Properties),而該類別將沒辦法動態地新增屬性。

readonly class Book
{
    public string $title;

    public function __construct(string $title)
    {
        $this->title = $title;
    }
}

$book = new Book('Clean Code');
$book->title = 'Refactor';

會跳出錯誤訊息:

 Cannot modify readonly property Book::$title 

如果我們想要動態新增屬性也會被阻擋,例如我們動態的指派 author 到範例的 book 物件中:

$book->author = 'Robert C. Martin';

錯誤訊息:

Cannot create dynamic property Book::$author

DNF Types

DNF,全名 Disjunctive Normal Form,中文有點拗口我就不硬翻了。總之就是一種正規形式,用來將許多交集(AND)做聯集(OR),也就是 OR of ANDs。

範例:

  • (A&B)|C
  • A|(B&C)|D
  • (A&B)|(C&D)

在 8.0 的時候,PHP 新增了聯集 Union Type,在 8.1 則新增了交集 Intersection Type。支援 DNF 後,在定義 PHP 的 function 參數型別,有了更多的彈性。

這項更新有助於我們在定義 function 時,更精準地宣告傳入的型別,或回傳的型別(return type)。

unit type 單值型別

PHP 8.2 可將 null、false、true 分別視為獨立的型別。

過去在 PHP 8.0 中,null 雖然可以作為 union type 的一部分,但卻沒辦法單獨存在。

至於 true 或 false,過去只能使用 bool來表示型別,但有些 function 雖然回傳型別宣告成 bool, 但它其實並不回傳 true, 只會回傳 false,這種時候使用 bool 型別就不夠精準。

因此 PHP 8.2 允許 null、false、true 作為一個獨立的型別。這些型別因為只包含一種值,所以稱為 unit type。

官網範例:

class Falsy  
{  
  public function alwaysFalse(): false {}   
  public function alwaysTrue(): true {}   
  public function alwaysNull(): null {}  
}
unit type 是 PHP 8.0 新增的功能,可以將兩個不同的類別聯合起來,作為一個參數的型別。例如 function foo(string|int $boo),代表這個 $boo可以是字串也可以是數字。

複習 8.0 版的更新→這邊

在 traits 中的常數

一個實用且簡單的新功能,可以在 Trait 中宣告常數。

官網範例如下。不過要特別注意 Trait 的常數沒辦法直接被讀取,必須透過使用它的類別來做使用。

trait Foo  
{  
  public const CONSTANT = 1;  
}  
  
class Bar 
{  
  use Foo;  
}  
  
var_dump(Bar::CONSTANT); // 1  
var_dump(Foo::CONSTANT); // Error

棄用動態屬性

PHP 中非常方便但也非常容易造成糞 Code 的動態屬性終於要被拿掉了。

在 8.2 中,使用動態屬性會有警告,根據 RFC 文件,可能在 PHP 9 會正式移除,屆時使用動態屬性則會拋出錯誤。但魔術方法 __get 及 __set 目前並不受這個改變影響。

class Book  
{  
  public $title;  
}  
  
$book = new Book();  
$book->author = 'Robert C. Martin'; 
// Deprecated notice

小結論

可以觀察到,相較於 8.0 及 8.1,PHP 8.2 的變動其實沒有很多,但也可以感受到 PHP 8.2 很努力在完善 PHP 的型別系統,讓 PHP 程式可以更穩健更可靠。

而棄用動態屬性也是一個很大的壯舉,可以終結動態屬性的濫用,讓 PHP 程式更不容易出錯,也更好除錯。

不過這讓我想到 Laravel,它在 Model 大量地使用了動態屬性,雖然 Laravel 都是透過魔術方法 __get 及 __set 來完成,雖然魔術方法並不在這次調整的影響範圍,但是不是也代表未來 Laravel 會改變設計的邏輯?這點值得觀察。

複習 8.1 更新了什麼→這邊

參考連結

PHP 8.2 Released!

更多關於 null type 的討論