2024年的PHP 8.4:Property Hooks 帶來的全新體驗

更新於 2024/12/19閱讀時間約 16 分鐘

「嘿,你有聽說 PHP 8.4 的新特性了嗎?」同事David一臉興奮地問我。

我轉過頭,手上還拿著極韻白,「你是說 Property Hooks?就是那個讓我們在存取屬性時可以加上 get/set 邏輯的東西嗎?」

「對啊!」David笑得合不攏嘴,「終於不用再為了每個小驗證寫滿 GetterSetter 方法了,也不用 __get() __set() 那麼大刀闊斧的攔截全部undefined屬性。」

如果你曾經為了在程式中控制屬性存取邏輯而煩惱不已,又或者討厭為每個屬性寫一堆 getXxx()、setXxx(),那麼 PHP 8.4 的 Property Hooks 絕對是你想要的。這個新特性讓我們可以直接在屬性宣告中對「存取行為」加入特定邏輯,同時兼顧程式碼的乾淨度與可讀性。

接下來,我們用最直觀、易懂的方式,帶你體驗 Property Hooks 的世界!

先別急,我們會先從「傳統的Getter/Setter 寫法」開始比較,一步步看出有了 Property Hooks 後的差異性。

沒有 Property Hooks 時的傳統寫法

在 PHP 8.4 之前,若你想在屬性賦值或讀取時做點小動作(例如:寫入時驗證、讀取時格式化),往往需要這樣的模式:

class User {

private string $name;

public function __construct(string $name) {

$this->setName($name);
}

public function getName(): string {

// 讀取時可能要處理一些邏輯

// 例如轉成大寫
return strtoupper($this->name);
}

public function setName(string $value): void {

// 寫入前先驗證
if (strlen($value) < 2) {

throw new InvalidArgumentException("Name is too short!");
}

$this->name = $value;
}
}

$user = new User("Tom");
echo $user->getName(); // "TOM"
$user->setName("A"); // 丟出例外

這段程式碼有什麼問題?

  • 我們為了控制屬性的存取,增加了 Getter/Setter 方法,程式碼變長了。
  • 寫的時候還好,日後維護或增加屬性時可能很煩,一個屬性兩個方法,多了 10 個屬性,就是 20 個方法。

再看看另一種「魔術方法」的做法( __get() 與 __set()):

class User {

private string $name;

public function __construct(string $name) {

$this->name = $name;
}

public function __get($prop) {

if ($prop === 'name') {

// 路徑一:讀取 name
return strtoupper($this->name);
}

throw new Exception("Property $prop not found");
}

public function __set($prop, $value) {

if ($prop === 'name') {

if (strlen($value) < 2) {

throw new InvalidArgumentException("Name too short!");
}

$this->name = $value;

return;
}

throw new Exception("Property $prop not found");
}
}

這魔術方法的寫法,雖然不需要每個屬性分別寫 getXxx() 和 setXxx(),但實務上卻是大砲打小鳥。

它攔截所有未定義的屬性存取,是個「一刀切」的做法,不僅難以維護,也讓 IDE 或靜態分析工具難以推斷屬性實際存在與否。

有了 Property Hooks 後的世界

現在,看看 PHP 8.4 的 Property Hooks:

class User {

public string $name {

get => strtoupper($this->name); // 讀取時自動轉成大寫

set (string $value) {

if (strlen($value) < 2) {

throw new InvalidArgumentException("Name too short!");
}

$this->name = $value;
}
}

public function __construct(string $name) {

$this->name = $name; // 寫入時會觸發 set 掛鉤
}
}

$user = new User("Tom");
echo $user->name; // 読取時觸發 get 掛鉤,輸出 "TOM"
$user->name = "A"; // 寫入觸發 set 掛鉤,因長度不足拋出例外

在這裡,我們直接在屬性宣告中利用 { } 區塊,定義 get 與 set 的邏輯。

神奇之處在於:

  • 讀取 $user→name 時,自動走 get 區塊。
  • 寫入 $user→name = … 時,自動走 set 區塊。
  • 這個屬性本身就像帶有內建控制邏輯的「智慧屬性」。

看起來是不是清爽許多?

實際案例對比:沒有 Property Hooks vs. 有 Property Hooks

範例一:基本驗證

傳統做法:

class Product {

private int $price;

public function __construct(int $price) {

$this->setPrice($price);
}

public function setPrice(int $price) {

if ($price < 0) {

throw new InvalidArgumentException("Price can't be negative!");
}

$this->price = $price;
}

public function getPrice(): int {

return $this->price;
}
}

$product = new Product(100);
echo $product->getPrice(); // 100
$product->setPrice(-10); // Exception

使用 Property Hooks:

class Product {

public int $price {

set (int $value) {

if ($value < 0) {

throw new InvalidArgumentException("Price can't be negative!");
}

$this->price = $value;
}

// 未實作 get, 預設讀取直接取得 backing value
// 如果要在讀取時做點事,也可加上 get => ...;
}

public function __construct(int $price) {

$this->price = $price; // 觸發 set 區塊
}
}

$product = new Product(100);
echo $product->price; // 100 (直接讀取,未實作 get,則為預設讀取行為)
$product->price = -10; // Exception

程式碼明顯更精簡。

範例二:虛擬屬性 (Virtual Property)

Property Hooks 也能產生「虛擬屬性」,就像前端的 computed property。

所謂虛擬屬性,就是該屬性並沒有真正在物件中存儲資料,而是動態取得或寫入時處理其他屬性。

傳統作法 (無 Hooks):

class User {

private string $firstName;

private string $lastName;

public function __construct(string $first, string $last) {

$this->firstName = $first;
$this->lastName = $last;
}

public function getFullName(): string {

return $this->firstName.' '.$this->lastName;
}

public function setFullName(string $value): void {

[$f, $l] = explode(' ', $value, 2);

$this->firstName = $f;
$this->lastName = $l;
}
}

$user = new User("John", "Doe");
echo $user->getFullName(); // John Doe
$user->setFullName("Jane Smith");
echo $user->getFullName(); // Jane Smith

有 Hooks 之後:

class User {

private string $firstName;

private string $lastName;

public string $fullName {

get => $this->firstName.' '.$this->lastName;

set {

[$f, $l] = explode(' ', $value, 2);

$this->firstName = $f;
$this->lastName = $l;
}
}

public function __construct(string $first, string $last) {

$this->firstName = $first;
$this->lastName = $last;
}
}

$user = new User("John", "Doe");
echo $user->fullName; // John Doe (透過 get hooks 動態取得)
$user->fullName = "Jane Smith"; // set hooks 動態拆解名稱
echo $user->fullName; // Jane Smith

完全不需要額外的方法名稱,直接用 $user→fullName,存取像是正常屬性,卻暗藏邏輯!

Property Hooks 的其他優勢

  • 介面與抽象類別支援:PHP 8.4 也允許在介面或抽象類別中定義需要 get 或 set 的屬性契約,讓物件行為更一致。
  • 明確性:跟 __get() __set() 相比,Property Hooks 只對定義的屬性生效,不是大範圍的「隱形陷阱」。
  • 工具友善:靜態分析工具與 IDE 能更輕鬆解析屬性行為,因為掛鉤是明確定義在屬性上,而非動態攔截。

實務建議

使用 Property Hooks 不代表要棄守 Getter/Setter。在特定場合,傳統 Getter/Setter 仍有存在價值(例如需相容舊有程式碼或某些框架習慣)。但在新的專案中,如果你想讓屬性操作更自然、程式碼更精簡,同時又能確保寫入與讀取的規則一致,那 Property Hooks 絕對是新寵兒。

要注意的是,雖然看起來很方便,但別因此在每個屬性上都加一堆複雜邏輯。

保持適度、保持簡單,才是維護上的王道。

最後

Property Hooks 是 PHP 8.4 的亮點之一。它解決了以往實作屬性控制的繁瑣,讓程式碼更直觀。從前我們得寫好多 Getter/Setter,或使用魔術方法來處理存取邏輯;現在,我們可以直觀地將邏輯和屬性綁在一起,就像給屬性裝上小小的「智慧晶片」。

希望這篇文章透過大量的程式碼對比、淺顯易懂的解說,讓你對 Property Hooks 有更清晰的認識。下次在寫 PHP 8.4 的程式時,不妨試試看這項特性,相信你會愛上這種「所見即所得」的程式碼體驗!

加油,繼續探索 PHP 的新可能!

參考資料

https://wiki.php.net/rfc/property-hooks

https://laravel-news.com/php-8-4-0

https://laravel-news.com/php-property-hooks


這是一系列以軟體開發為主題的輕鬆分享,內容涵蓋了技術選擇、開發經驗、實戰應用等多方面的議題。無論是如何在眾多框架中做出選擇,還是如何應對技術轉移的挑戰,這裡有幽默、有趣的對話風格,將複雜的技術問題轉化為易懂的故事。
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
CodeIgniter 3 和 Laravel 是兩種不同的 PHP 框架,各有其特點和適用場景。CodeIgniter 3 是一個輕量級框架,Laravel 是一個功能強大的現代 PHP 框架,同樣都有Models的它們有什麼樣的差別呢?
本篇文章為Laravel初學者提供了一個指南,深入探討了Laravel的routes目錄下的功能。文章詳細描述了web.php和api.php的差異和使用情境,並簡要介紹了console.php和channels.php的功能。透過這篇文章,讀者可以更好地理解和利用Laravel的路由功能。
Laravel框架中的路由系統是其核心功能之一,它允許開發者輕鬆地定義應用程式的URL結構和相應的邏輯。本篇文章將深入探討Laravel路由的基本概念、進階功能以及實際應用。
在Laravel的MVC架構中,視圖(View)是呈現資料給使用者的關鍵部分。本篇深入探討了視圖的基本概念、建立與管理、與資料的互動方式,以及Blade模板引擎的應用。Blade提供了一套強大的工具,使動態視圖建立更為高效。
本篇深入探討了Model的基本定義、其Controller的關係,以及如何在Controller中使用Model進行CRUD操作。強調了Model的關聯方法,如hasMany,並透過範例程式碼展示了其實際應用。為初學者提供了一個清晰的Laravel入門路徑,同時也為有經驗的開發者提供了實用的參考。
Laravel的Model是資料和邏輯的核心連接,簡化資料庫操作。本指南著重於Model的基本屬性、方法和Eloquent ORM的使用。我們詳細探討了hasMany、hasOne和belongsTo這些關聯,它們基於外鍵確定資料間的關係。透過本文,您將深入了解如何有效地在Laravel中使用Mod
CodeIgniter 3 和 Laravel 是兩種不同的 PHP 框架,各有其特點和適用場景。CodeIgniter 3 是一個輕量級框架,Laravel 是一個功能強大的現代 PHP 框架,同樣都有Models的它們有什麼樣的差別呢?
本篇文章為Laravel初學者提供了一個指南,深入探討了Laravel的routes目錄下的功能。文章詳細描述了web.php和api.php的差異和使用情境,並簡要介紹了console.php和channels.php的功能。透過這篇文章,讀者可以更好地理解和利用Laravel的路由功能。
Laravel框架中的路由系統是其核心功能之一,它允許開發者輕鬆地定義應用程式的URL結構和相應的邏輯。本篇文章將深入探討Laravel路由的基本概念、進階功能以及實際應用。
在Laravel的MVC架構中,視圖(View)是呈現資料給使用者的關鍵部分。本篇深入探討了視圖的基本概念、建立與管理、與資料的互動方式,以及Blade模板引擎的應用。Blade提供了一套強大的工具,使動態視圖建立更為高效。
本篇深入探討了Model的基本定義、其Controller的關係,以及如何在Controller中使用Model進行CRUD操作。強調了Model的關聯方法,如hasMany,並透過範例程式碼展示了其實際應用。為初學者提供了一個清晰的Laravel入門路徑,同時也為有經驗的開發者提供了實用的參考。
Laravel的Model是資料和邏輯的核心連接,簡化資料庫操作。本指南著重於Model的基本屬性、方法和Eloquent ORM的使用。我們詳細探討了hasMany、hasOne和belongsTo這些關聯,它們基於外鍵確定資料間的關係。透過本文,您將深入了解如何有效地在Laravel中使用Mod
你可能也想看
Google News 追蹤
Thumbnail
*合作聲明與警語: 本文係由國泰世華銀行邀稿。 證券服務係由國泰世華銀行辦理共同行銷證券經紀開戶業務,定期定額(股)服務由國泰綜合證券提供。   剛出社會的時候,很常在各種 Podcast 或 YouTube 甚至是在朋友間聊天,都會聽到各種市場動態、理財話題,像是:聯準會降息或是近期哪些科
Thumbnail
在本章節中,我們探討了 PHP 中如何引用和管理套件。學習了如何使用 Composer 來安裝第三方套件,以及如何引用自定義模組。此外,我們還介紹了如何創建和使用自定義套件,並列舉了一些在 PHP 社群中常見且廣泛使用的套件和庫。通過掌握這些知識,開發者可以更有效地管理和利用各種資源。
Thumbnail
這一章節旨在介紹 PHP 中的物件導向編程(OOP)概念。通過詳細講解類別、建構子、訪問修飾符(公開、私有、受保護)、繼承、多型、封裝、介面、抽象類別、靜態類別、列舉、委派、Lambda 表達式、泛型和反射等概念,使讀者能夠理解和應用這些 OOP 技術來編寫更具結構性和可維護性的 PHP 代碼。
Thumbnail
在這一章中,我們探討了 PHP 中的函數,包括函數的基本結構、不同的函數定義方式(如函數聲明、函數表達式、箭頭函數和匿名函數)以及如何呼叫函數。我們還討論了函數的參數處理方式,包括單個參數、多個參數、預設參數值和剩餘參數。此外,我們還介紹了函數的返回值,包括返回單個值、返回物件和返回函數的情況。
Thumbnail
本章節旨在介紹如何在不同操作系統上安裝和配置PHP環境,並使用命令行工具進行基礎操作。此外,還介紹了使用Visual Studio Code進行PHP開發的步驟,包括安裝擴展和設置調試環境。
Thumbnail
在本節中,我們介紹了PHP的基本語法,包括如何在HTML中嵌入PHP代碼,PHP腳本的執行順序,以及多種註解方式。我們還學習了如何定義和使用變數,包括單個變數和多個變數的賦值方法。這些基礎知識將幫助你開始使用PHP進行Web開發。
Thumbnail
這個章節介紹了PHP的基本特性和應用領域,並且列舉了使用PHP的知名公司和網站。了解PHP的簡單易學、跨平台、嵌入HTML等特性,以及PHP在動態網站、電子商務、內容管理系統、社交媒體平台、數據庫管理和API開發中的應用。
Thumbnail
介紹工作後幾個常見的小問題,包括寫 Log 的好習慣、本地印出錯誤或過程、PHPCS 工具、變數儲存於設定檔、避免魔術數字、程式碼靜態分析與動態分析。
PHP(Hypertext Preprocessor)是一種流行的開源腳本語言,特別適用於網頁開發。它具有簡單的語法和易於理解的程式碼結構,並且支持多種數據庫系統和其他技術和框架集成。本文將介紹PHP的主要特點和廣泛應用性。
Thumbnail
前幾篇討論到各種裝飾器的用法,本文將介紹另外一種裝飾器,可以將方法轉換成屬性來使用。 property也可以動態的取出物件的值,隨著時間或其他運算改變所產生的值,讓我們繼續往下看更多介紹吧。
Thumbnail
PHP是一個功能豐富且靈活的語言,用於開發動態和互動性強的網站。本文介紹了PHP的主要功能和用途,以及它的優點和特性。不論你是一個初學者還是一個專業的開發人員,PHP都是一個值得學習和使用的強大工具。
Thumbnail
*合作聲明與警語: 本文係由國泰世華銀行邀稿。 證券服務係由國泰世華銀行辦理共同行銷證券經紀開戶業務,定期定額(股)服務由國泰綜合證券提供。   剛出社會的時候,很常在各種 Podcast 或 YouTube 甚至是在朋友間聊天,都會聽到各種市場動態、理財話題,像是:聯準會降息或是近期哪些科
Thumbnail
在本章節中,我們探討了 PHP 中如何引用和管理套件。學習了如何使用 Composer 來安裝第三方套件,以及如何引用自定義模組。此外,我們還介紹了如何創建和使用自定義套件,並列舉了一些在 PHP 社群中常見且廣泛使用的套件和庫。通過掌握這些知識,開發者可以更有效地管理和利用各種資源。
Thumbnail
這一章節旨在介紹 PHP 中的物件導向編程(OOP)概念。通過詳細講解類別、建構子、訪問修飾符(公開、私有、受保護)、繼承、多型、封裝、介面、抽象類別、靜態類別、列舉、委派、Lambda 表達式、泛型和反射等概念,使讀者能夠理解和應用這些 OOP 技術來編寫更具結構性和可維護性的 PHP 代碼。
Thumbnail
在這一章中,我們探討了 PHP 中的函數,包括函數的基本結構、不同的函數定義方式(如函數聲明、函數表達式、箭頭函數和匿名函數)以及如何呼叫函數。我們還討論了函數的參數處理方式,包括單個參數、多個參數、預設參數值和剩餘參數。此外,我們還介紹了函數的返回值,包括返回單個值、返回物件和返回函數的情況。
Thumbnail
本章節旨在介紹如何在不同操作系統上安裝和配置PHP環境,並使用命令行工具進行基礎操作。此外,還介紹了使用Visual Studio Code進行PHP開發的步驟,包括安裝擴展和設置調試環境。
Thumbnail
在本節中,我們介紹了PHP的基本語法,包括如何在HTML中嵌入PHP代碼,PHP腳本的執行順序,以及多種註解方式。我們還學習了如何定義和使用變數,包括單個變數和多個變數的賦值方法。這些基礎知識將幫助你開始使用PHP進行Web開發。
Thumbnail
這個章節介紹了PHP的基本特性和應用領域,並且列舉了使用PHP的知名公司和網站。了解PHP的簡單易學、跨平台、嵌入HTML等特性,以及PHP在動態網站、電子商務、內容管理系統、社交媒體平台、數據庫管理和API開發中的應用。
Thumbnail
介紹工作後幾個常見的小問題,包括寫 Log 的好習慣、本地印出錯誤或過程、PHPCS 工具、變數儲存於設定檔、避免魔術數字、程式碼靜態分析與動態分析。
PHP(Hypertext Preprocessor)是一種流行的開源腳本語言,特別適用於網頁開發。它具有簡單的語法和易於理解的程式碼結構,並且支持多種數據庫系統和其他技術和框架集成。本文將介紹PHP的主要特點和廣泛應用性。
Thumbnail
前幾篇討論到各種裝飾器的用法,本文將介紹另外一種裝飾器,可以將方法轉換成屬性來使用。 property也可以動態的取出物件的值,隨著時間或其他運算改變所產生的值,讓我們繼續往下看更多介紹吧。
Thumbnail
PHP是一個功能豐富且靈活的語言,用於開發動態和互動性強的網站。本文介紹了PHP的主要功能和用途,以及它的優點和特性。不論你是一個初學者還是一個專業的開發人員,PHP都是一個值得學習和使用的強大工具。