介紹
PHP 8.4 將於 2024 年 11 月發布,並將帶來一個很酷的新功能:屬性掛鉤。
在本文中,我們將了解什麼是屬性掛鉤以及如何在 PHP 8.4 專案中使用它們。
順便說一句,您可能還有興趣查看我的另一篇文章,其中向您展示了 PHP 8.4 中添加的新數組函數。
什麼是 PHP 屬性掛鉤?
屬性掛鉤可讓您為類別屬性定義自訂 getter 和 setter 邏輯,而無需編寫單獨的 getter 和 setter 方法。這表示您可以直接在屬性宣告中定義邏輯,這樣您就可以直接存取屬性(例如$user->firstName),而不必記住呼叫方法(例如$user->getFirstName() 和$user- >setFirstName ()).
您可以在 https://wiki.php.net/rfc/property-hooks 查看此功能的 RFC
如果您是 Laravel 開發人員,當您閱讀本文時,您可能會注意到鉤子看起來與 Laravel 模型中的存取器和修改器非常相似。
我非常喜歡屬性掛鉤功能的外觀,我想當 PHP 8.4 發佈時我將在我的專案中使用它。
要了解屬性掛鉤的工作原理,讓我們來看一些範例用法。
「取得」鉤子
您可以定義一個 get 鉤子,每當您嘗試存取屬性時都會呼叫該鉤子。
例如,假設您有一個簡單的 User 類,它在建構函式中接受名字和姓氏。您可能想要定義一個 fullName 屬性,將名字和姓氏連接在一起。為此,您可以為 fullName 屬性定義一個 get 掛鉤:
readonly class User { public string $fullName { get { return $this->firstName.' '.$this->lastName; } } public function __construct( public readonly string $firstName, public readonly string $lastName ) { // } } $user = new User(firstName: 'ash', lastName: 'allen'); echo $user->firstName; // ash echo $user->lastName; // allen echo $user->fullName; // ash allen
在上面的範例中,我們可以看到我們為 fullName 屬性定義了一個 get 鉤子,該鉤子傳回一個透過將firstName和lastName屬性連接在一起計算得出的值。我們也可以使用類似箭頭函數的語法來進一步清理它:
readonly class User { public string $fullName { get => $this->firstName.' '.$this->lastName; } public function __construct( public readonly string $firstName, public readonly string $lastName, ) { // } } $user = new User(firstName: 'ash', lastName: 'allen'); echo $user->firstName; // ash echo $user->lastName; // allen echo $user->fullName; // ash allen
類型相容性
要注意的是,getter 傳回的值必須與屬性的型別相容。
如果未啟用嚴格類型,則該值將根據屬性類型進行類型調整。例如,如果從宣告為字串的屬性傳回整數,則該整數將轉換為字串:
declare(strict_types=1); class User { public string $fullName { get { return 123; } } public function __construct( public readonly string $firstName, public readonly string $lastName, ) { // } } $user = new User(firstName: 'ash', lastName: 'allen'); echo $user->fullName; // "123"
在上面的範例中,即使我們指定 123 為要傳回的整數,「123」也會作為字串傳回,因為該屬性是字串。
我們可以添加declare(strict_types=1);像這樣添加到程式碼頂部以啟用嚴格的類型檢查:
declare(strict_types=1); class User { public string $fullName { get { return 123; } } public function __construct( public readonly string $firstName, public readonly string $lastName, ) { // } }
現在這會導致拋出錯誤,因為傳回值是整數,但屬性是字串:
Fatal error: Uncaught TypeError: User::$fullName::get(): Return value must be of type string, int returned
「設定」鉤子
PHP 8.4 屬性掛鉤也允許您定義設定掛鉤。每當您嘗試設定屬性時都會呼叫此函數。
您可以為 set hook 在兩種單獨的語法之間進行選擇:
- 明確定義要在屬性上設定的值
- 使用箭頭函數傳回要在屬性上設定的值
讓我們來看看這兩種方法。我們想像一下,當在 User 類別上設定名字和姓氏的首字母時,我們想要將它們設為大寫:
declare(strict_types=1); class User { public string $firstName { // Explicitly set the property value set(string $name) { $this->firstName = ucfirst($name); } } public string $lastName { // Use an arrow function and return the value // you want to set on the property set(string $name) => ucfirst($name); } public function __construct( string $firstName, string $lastName ) { $this->firstName = $firstName; $this->lastName = $lastName; } } $user = new User(firstName: 'ash', lastName: 'allen'); echo $user->firstName; // Ash echo $user->lastName; // Allen
正如我們在上面的範例中所看到的,我們為firstName 屬性定義了一個set hook,該鉤子在將名稱的第一個字母設定到屬性上之前將其大寫。我們也為 lastName 屬性定義了一個 set hook,它使用箭頭函數傳回要在該屬性上設定的值。
類型相容性
如果屬性有型別聲明,那麼它的 set hook 也必須有相容的型別集。以下範例將傳回錯誤,因為firstName的set鉤子沒有類型聲明,但屬性本身有字串類型聲明:
class User { public string $firstName { set($name) => ucfirst($name); } public string $lastName { set(string $name) => ucfirst($name); } public function __construct( string $firstName, string $lastName ) { $this->firstName = $firstName; $this->lastName = $lastName; } }
嘗試執行上述程式碼將導致拋出以下錯誤:
Fatal error: Type of parameter $name of hook User::$firstName::set must be compatible with property type
一起使用“get”和“set”鉤子
您不限於單獨使用 get 和 set 掛鉤。您可以在同一房產中一起使用它們。
讓我們舉一個簡單的例子。我們假設我們的 User 類別有一個 fullName 屬性。當我們設定屬性時,我們會將全名分為名字和姓氏。我知道這是一種幼稚的方法,並且有更好的解決方案,但這純粹是為了舉例來突出顯示掛鉤屬性。
程式碼可能看起來像這樣:
declare(strict_types=1); class User { public string $fullName { // Dynamically build up the full name from // the first and last name get => $this->firstName.' '.$this->lastName; // Split the full name into first and last name and // then set them on their respective properties set(string $name) { $splitName = explode(' ', $name); $this->firstName = $splitName[0]; $this->lastName = $splitName[1]; } } public string $firstName { set(string $name) => $this->firstName = ucfirst($name); } public string $lastName { set(string $name) => $this->lastName = ucfirst($name); } public function __construct(string $fullName) { $this->fullName = $fullName; } } $user = new User(fullName: 'ash allen'); echo $user->firstName; // Ash echo $user->lastName; // Allen echo $user->fullName; // Ash Allen
In the code above, we've defined a fullName property that has both a get and set hook. The get hook returns the full name by concatenating the first and last name together. The set hook splits the full name into the first and last name and sets them on their respective properties.
You may have also noticed that we're not setting a value on the fullName property itself. Instead, if we need to read the value of the fullName property, the get hook will be called to build up the full name from the first and last name properties. I've done this to highlight that you can have a property that doesn't have a value set directly on it, but instead, the value is calculated from other properties.
Using Property Hooks on Promoted Properties
A cool feature of property hooks is that you can also use them with constructor promoted properties.
Let's check out an example of a class that isn't using promoted properties and then look at what it might look like using promoted properties.
Our User class might look like so:
readonly class User { public string $fullName { get => $this->firstName.' '.$this->lastName; } public string $firstName { set(string $name) => ucfirst($name); } public string $lastName { set(string $name) => ucfirst($name); } public function __construct( string $firstName, string $lastName, ) { $this->firstName = $firstName; $this->lastName = $lastName; } }
We could promote the firstName and lastName properties into the constructor and define their set logic directly on the property:
readonly class User { public string $fullName { get => $this->firstName.' '.$this->lastName; } public function __construct( public string $firstName { set (string $name) => ucfirst($name); }, public string $lastName { set (string $name) => ucfirst($name); } ) { // } }
Write-only Hooked Properties
If you define a hooked property with a setter that doesn't actually set a value on the property, then the property will be write-only. This means you can't read the value of the property, you can only set it.
Let's take our User class from the previous example and modify the fullName property to be write-only by removing the get hook:
declare(strict_types=1); class User { public string $fullName { // Define a setter that doesn't set a value // on the "fullName" property. This will // make it a write-only property. set(string $name) { $splitName = explode(' ', $name); $this->firstName = $splitName[0]; $this->lastName = $splitName[1]; } } public string $firstName { set(string $name) => $this->firstName = ucfirst($name); } public string $lastName { set(string $name) => $this->lastName = ucfirst($name); } public function __construct( string $fullName, ) { $this->fullName = $fullName; } } $user = new User('ash allen'); echo $user->fullName; // Will trigger an error!
If we were to run the code above, we'd see the following error being thrown when attempting to access the fullName property:
Fatal error: Uncaught Error: Property User::$fullName is write-only
Read-only Hooked Properties
Similarly, a property can be read-only.
For example, imagine we only ever want the fullName property to be generated from the firstName and lastName properties. We don't want to allow the fullName property to be set directly. We can achieve this by removing the set hook from the fullName property:
class User { public string $fullName { get { return $this->firstName.' '.$this->lastName; } } public function __construct( public readonly string $firstName, public readonly string $lastName, ) { $this->fullName = 'Invalid'; // Will trigger an error! } }
If we were to try and run the code above, the following error would be thrown because we're trying to set the fullName property directly:
Uncaught Error: Property User::$fullName is read-only
Using the "readonly" keyword
You can still make our PHP classes readonly even if they have hooked properties. For example, we may want to make the User class readonly:
readonly class User { public string $firstName { set(string $name) => ucfirst($name); } public string $lastName { set(string $name) => ucfirst($name); } public function __construct( string $firstName, string $lastName, ) { $this->firstName = $firstName; $this->lastName = $lastName; } }
However, a hooked property cannot use the readonly keyword directly. For example, this class would be invalid:
class User { public readonly string $fullName { get => $this->firstName.' '.$this->lastName; } public function __construct( string $firstName, string $lastName, ) { $this->firstName = $firstName; $this->lastName = $lastName; } }
The above code would throw the following error:
Fatal error: Hooked properties cannot be readonly
The "PROPERTY" Magic Constant
In PHP 8.4, a new magic constant called __PROPERTY__ has been introduced. This constant can be used to reference the property name within the property hook.
Let's take a look at an example:
class User { // ... public string $lastName { set(string $name) { echo __PROPERTY__; // lastName $this->{__PROPERTY__} = ucfirst($name); // Will trigger an error! } } public function __construct( string $firstName, string $lastName, ) { $this->firstName = $firstName; $this->lastName = $lastName; } }
In the code above, we can see that using __PROPERTY__ inside the lastName property's setter will output the property name lastName. However, it's also worth noting that trying to use this constant in an attempt to set the property value will trigger an error:
Fatal error: Uncaught Error: Must not write to virtual property User::$lastName
There's a handy use case example for the __PROPERTY__ magic constant that you can check out on GitHub: https://github.com/Crell/php-rfcs/blob/master/property-hooks/examples.md.
Hooked Properties in Interfaces
PHP 8.4 also allows you to define publicly accessible hooked properties in interfaces. This can be useful if you want to enforce that a class implements certain properties with hooks.
Let's take a look at an example interface with hooked properties declared:
interface Nameable { // Expects a public gettable 'fullName' property public string $fullName { get; } // Expects a public gettable 'firstName' property public string $firstName { get; } // Expects a public settable 'lastName' property public string $lastName { set; } }
In the interface above, we're defining that any classes implementing the Nameable interface must have:
- A fullName property that is at least publicly gettable. This can be achieved by defining a get hook or not defining a hook at all.
- A firstName property that is at least publicly gettable.
- A lastName property that is at least publicly settable. This can be achieved by defining a property which has a set hook or not defining a hook at all. But if the class is read-only then the property must have a set hook.
This class that implements the Nameable interface would be valid:
class User implements Nameable { public string $fullName { get => $this->firstName.' '.$this->lastName; } public string $firstName { set(string $name) => ucfirst($name); } public string $lastName; public function __construct( string $firstName, string $lastName, ) { $this->firstName = $firstName; $this->lastName = $lastName; } }
The class above would be valid because the fullName property has a get hook to match the interface definition. The firstName property only has a set hook, but is still publicly accessible so it satisfies the criteria. The lastName property doesn't have a get hook, but it is publicly settable so it satisfies the criteria.
Let's update our User class to enforce a get and set hook for the fullName property:
interface Nameable { public string $fullName { get; set; } public string $firstName { get; } public string $lastName { set; } }
Our User class would no longer satisfy the criteria for the fullName property because it doesn't have a set hook defined. It would cause the following error to be thrown:
Fatal error: Class User contains 1 abstract methods and must therefore be declared abstract or implement the remaining methods (Nameable::$fullName::set)
Hooked Properties in Abstract Classes
Similar to interfaces, you can also define hooked properties in abstract classes. This can be useful if you want to provide a base class that defines hooked properties that child classes must implement. You can also define the hooks in the abstract class and have them be overridden in the child classes.
For example, let's make a Model abstract class that defines a name property that must be implemented by child classes:
abstract class Model { abstract public string $fullName { get => $this->firstName.' '.$this->lastName; set; } abstract public string $firstName { get; } abstract public string $lastName { set; } }
In the abstract class above, we're defining that any classes that extend the Model class must have:
- A fullName property that is at least publicly gettable and settable. This can be achieved by defining a get and set hook or not defining a hook at all. We've also defined the get hook for the fullName property in the abstract class so we don't need to define it in the child classes, but it can be overridden if needed.
- A firstName property that is at least publicly gettable. This can be achieved by defining a get hook or not defining a hook at all.
- A lastName property that is at least publicly settable. This can be achieved by defining a property which has a set hook or not defining a hook at all. But if the class is read-only then the property must have a set hook.
We could then create a User class that extends the Model class:
class User extends Model { public string $fullName; public string $firstName { set(string $name) => ucfirst($name); } public string $lastName; public function __construct( string $firstName, string $lastName, ) { $this->firstName = $firstName; $this->lastName = $lastName; } }
Conclusion
Hopefully, this article should have given you an insight into how PHP 8.4 property hooks work and how you might be able to use them in your PHP projects.
I wouldn't worry too much if this feature seems a little confusing at first. When I first saw it, I was a little confused too (especially with how they work with interfaces and abstract classes). But once you start tinkering with them, you'll soon get the hang of it.
I'm excited to see how this feature will be used in the wild and I'm looking forward to using it in my projects when PHP 8.4 is released.
If you enjoyed reading this post, you might be interested in checking out my 220+ page ebook "Battle Ready Laravel" which covers similar topics in more depth.
Or, you might want to check out my other 440+ page ebook "Consuming APIs in Laravel" which teaches you how to use Laravel to consume APIs from other services.
If you're interested in getting updated each time I publish a new post, feel free to sign up for my newsletter.
Keep on building awesome stuff! ?
以上是PHP 屬性掛鉤的詳細內容。更多資訊請關注PHP中文網其他相關文章!

PHP用於構建動態網站,其核心功能包括:1.生成動態內容,通過與數據庫對接實時生成網頁;2.處理用戶交互和表單提交,驗證輸入並響應操作;3.管理會話和用戶認證,提供個性化體驗;4.優化性能和遵循最佳實踐,提升網站效率和安全性。

PHP在數據庫操作和服務器端邏輯處理中使用MySQLi和PDO擴展進行數據庫交互,並通過會話管理等功能處理服務器端邏輯。 1)使用MySQLi或PDO連接數據庫,執行SQL查詢。 2)通過會話管理等功能處理HTTP請求和用戶狀態。 3)使用事務確保數據庫操作的原子性。 4)防止SQL注入,使用異常處理和關閉連接來調試。 5)通過索引和緩存優化性能,編寫可讀性高的代碼並進行錯誤處理。

在PHP中使用預處理語句和PDO可以有效防範SQL注入攻擊。 1)使用PDO連接數據庫並設置錯誤模式。 2)通過prepare方法創建預處理語句,使用佔位符和execute方法傳遞數據。 3)處理查詢結果並確保代碼的安全性和性能。

PHP和Python各有優劣,選擇取決於項目需求和個人偏好。 1.PHP適合快速開發和維護大型Web應用。 2.Python在數據科學和機器學習領域佔據主導地位。

PHP在電子商務、內容管理系統和API開發中廣泛應用。 1)電子商務:用於購物車功能和支付處理。 2)內容管理系統:用於動態內容生成和用戶管理。 3)API開發:用於RESTfulAPI開發和API安全性。通過性能優化和最佳實踐,PHP應用的效率和可維護性得以提升。

PHP可以輕鬆創建互動網頁內容。 1)通過嵌入HTML動態生成內容,根據用戶輸入或數據庫數據實時展示。 2)處理表單提交並生成動態輸出,確保使用htmlspecialchars防XSS。 3)結合MySQL創建用戶註冊系統,使用password_hash和預處理語句增強安全性。掌握這些技巧將提升Web開發效率。

PHP和Python各有優勢,選擇依據項目需求。 1.PHP適合web開發,尤其快速開發和維護網站。 2.Python適用於數據科學、機器學習和人工智能,語法簡潔,適合初學者。

PHP仍然具有活力,其在現代編程領域中依然佔據重要地位。 1)PHP的簡單易學和強大社區支持使其在Web開發中廣泛應用;2)其靈活性和穩定性使其在處理Web表單、數據庫操作和文件處理等方面表現出色;3)PHP不斷進化和優化,適用於初學者和經驗豐富的開發者。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

Dreamweaver CS6
視覺化網頁開發工具

Safe Exam Browser
Safe Exam Browser是一個安全的瀏覽器環境,安全地進行線上考試。該軟體將任何電腦變成一個安全的工作站。它控制對任何實用工具的訪問,並防止學生使用未經授權的資源。

SublimeText3 Linux新版
SublimeText3 Linux最新版

MantisBT
Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

WebStorm Mac版
好用的JavaScript開發工具