日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:52010
  • 待審:67
  • 小程序:12
  • 文章:1106242
  • 會(huì)員:784

介紹

php 8.4 將于 2024 年 11 月發(fā)布,并將帶來(lái)一個(gè)很酷的新功能:屬性掛鉤。

在本文中,我們將了解什么是屬性掛鉤以及如何在 php 8.4 項(xiàng)目中使用它們。

順便說(shuō)一句,您可能還有興趣查看我的另一篇文章,其中向您展示了 php 8.4 中添加的新數(shù)組函數(shù)。

什么是 php 屬性掛鉤?

屬性掛鉤允許您為類屬性定義自定義 getter 和 setter 邏輯,而無(wú)需編寫單獨(dú)的 getter 和 setter 方法。這意味著您可以直接在屬性聲明中定義邏輯,這樣您就可以直接訪問(wèn)屬性(例如 $user->firstname),而不必記住調(diào)用方法(例如 $user->getfirstname() 和 $user->setfirstname()) .

您可以在 https://wiki.php.net/rfc/property-hooks 查看此功能的 rfc

如果您是 laravel 開(kāi)發(fā)人員,當(dāng)您閱讀本文時(shí),您可能會(huì)注意到鉤子看起來(lái)與 laravel 模型中的訪問(wèn)器和修改器非常相似。

我非常喜歡屬性掛鉤功能的外觀,我想當(dāng) php 8.4 發(fā)布時(shí)我將在我的項(xiàng)目中使用它。

要了解屬性掛鉤的工作原理,讓我們看一些示例用法。

“獲取”鉤子

您可以定義一個(gè) get 鉤子,每當(dāng)您嘗試訪問(wèn)屬性時(shí)都會(huì)調(diào)用該鉤子。

例如,假設(shè)您有一個(gè)簡(jiǎn)單的 user 類,它在構(gòu)造函數(shù)中接受名字和姓氏。您可能想要定義一個(gè) fullname 屬性,將名字和姓氏連接在一起。為此,您可以為 fullname 屬性定義一個(gè) 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

登錄后復(fù)制

在上面的示例中,我們可以看到我們?yōu)?fullname 屬性定義了一個(gè) get 鉤子,該鉤子返回一個(gè)通過(guò)將firstname和lastname屬性連接在一起計(jì)算得出的值。我們也可以使用類似于箭頭函數(shù)的語(yǔ)法來(lái)進(jìn)一步清理它:

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

登錄后復(fù)制

類型兼容性

需要注意的是,getter 的返回值必須與屬性的類型兼容。

如果未啟用嚴(yán)格類型,則該值將根據(jù)屬性類型進(jìn)行類型轉(zhuǎn)換。例如,如果從聲明為字符串的屬性返回整數(shù),則該整數(shù)將轉(zhuǎn)換為字符串:

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"

登錄后復(fù)制

在上面的例子中,即使我們指定了 123 作為要返回的整數(shù),但“123”還是以字符串形式返回,因?yàn)樵搶傩允亲址?/p>

我們可以添加declare(strict_types=1);像這樣添加到代碼頂部以啟用嚴(yán)格的類型檢查:

declare(strict_types=1);

class user
{
    public string $fullname {
        get {
            return 123;
        }
    }

    public function __construct(
        public readonly string $firstname,
        public readonly string $lastname,
    ) {
        //
    }
}

登錄后復(fù)制

現(xiàn)在這會(huì)導(dǎo)致拋出錯(cuò)誤,因?yàn)榉祷刂凳钦麛?shù),但屬性是字符串:

fatal error: uncaught typeerror: user::$fullname::get(): return value must be of type string, int returned

登錄后復(fù)制

“設(shè)置”鉤子

php 8.4 屬性鉤子還允許您定義集合鉤子。每當(dāng)您嘗試設(shè)置屬性時(shí)都會(huì)調(diào)用此函數(shù)。

您可以為 set hook 在兩種單獨(dú)的語(yǔ)法之間進(jìn)行選擇:

顯式定義要在屬性上設(shè)置的值
使用箭頭函數(shù)返回要在屬性上設(shè)置的值

讓我們看看這兩種方法。我們想象一下,當(dāng)在 user 類上設(shè)置名字和姓氏的首字母時(shí),我們想要將它們?cè)O(shè)置為大寫:

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

登錄后復(fù)制

正如我們?cè)谏厦娴氖纠兴吹降模覀優(yōu)閒irstname 屬性定義了一個(gè)set hook,在將名稱設(shè)置為屬性之前,該鉤子將名稱的第一個(gè)字母大寫。我們還為 lastname 屬性定義了一個(gè) set hook,它使用箭頭函數(shù)返回要在屬性上設(shè)置的值。

類型兼容性

如果屬性有類型聲明,那么它的 set hook 也必須有兼容的類型集。下面的示例將返回錯(cuò)誤,因?yàn)?firstname 的 set hook 沒(méi)有類型聲明,但屬性本身有 string 的類型聲明:

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;
    }
}

登錄后復(fù)制

嘗試運(yùn)行上面的代碼將導(dǎo)致拋出以下錯(cuò)誤:

fatal error: type of parameter $name of hook user::$firstname::set must be compatible with property type

登錄后復(fù)制

一起使用“get”和“set”鉤子

您不限于單獨(dú)使用 get 和 set 掛鉤。您可以在同一房產(chǎn)中一起使用它們。

舉個(gè)簡(jiǎn)單的例子。我們假設(shè)我們的 user 類有一個(gè) fullname 屬性。當(dāng)我們?cè)O(shè)置屬性時(shí),我們會(huì)將全名分為名字和姓氏。我知道這是一種幼稚的方法,并且有更好的解決方案,但這純粹是為了舉例來(lái)突出顯示掛鉤屬性。

代碼可能看起來(lái)像這樣:

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

登錄后復(fù)制

在上面的代碼中,我們定義了一個(gè) fullname 屬性,它同時(shí)具有 get 和 set 鉤子。 get 掛鉤通過(guò)將名字和姓氏連接在一起來(lái)返回全名。 set 鉤子將全名拆分為名字和姓氏,并將它們?cè)O(shè)置在各自的屬性上。

您可能還注意到,我們沒(méi)有為 fullname 屬性本身設(shè)置值。相反,如果我們需要讀取 fullname 屬性的值,則會(huì)調(diào)用 get 掛鉤以根據(jù)名字和姓氏屬性構(gòu)建全名。我這樣做是為了強(qiáng)調(diào),您可以擁有一個(gè)不直接設(shè)置值的屬性,而是根據(jù)其他屬性計(jì)算該值。

在升級(jí)屬性上使用屬性掛鉤

屬性掛鉤的一個(gè)很酷的功能是您還可以將它們與構(gòu)造函數(shù)提升的屬性一起使用。

讓我們看一個(gè)不使用提升屬性的類的示例,然后看看使用提升屬性時(shí)它會(huì)是什么樣子。

我們的用戶類可能看起來(lái)像這樣:

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;
    }
}

登錄后復(fù)制

我們可以將firstname和lastname屬性提升到構(gòu)造函數(shù)中,并直接在屬性上定義它們的設(shè)置邏輯:

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);
        }
    ) {
        //
    }
}  

登錄后復(fù)制

只寫掛鉤屬性

如果您使用 setter 定義了一個(gè)掛鉤屬性,但實(shí)際上并未在該屬性上設(shè)置值,則該屬性將是只寫的。這意味著你無(wú)法讀取屬性的值,只能設(shè)置它。

讓我們采用前面示例中的 user 類,并通過(guò)刪除 get 掛鉤將 fullname 屬性修改為只寫:

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!

登錄后復(fù)制

如果我們運(yùn)行上面的代碼,我們會(huì)在嘗試訪問(wèn) fullname 屬性時(shí)看到拋出以下錯(cuò)誤:

fatal error: uncaught error: property user::$fullname is write-only

登錄后復(fù)制

只讀掛鉤屬性

同樣,屬性也可以是只讀的。

例如,假設(shè)我們只希望從firstname 和lastname 屬性生成fullname 屬性。我們不想允許直接設(shè)置 fullname 屬性。我們可以通過(guò)從 fullname 屬性中刪除 set 鉤子來(lái)實(shí)現(xiàn)這一點(diǎn):

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!
    }
}

登錄后復(fù)制

如果我們嘗試運(yùn)行上面的代碼,則會(huì)拋出以下錯(cuò)誤,因?yàn)槲覀冊(cè)噲D直接設(shè)置 fullname 屬性:

uncaught error: property user::$fullname is read-only

登錄后復(fù)制

使用“readonly”關(guān)鍵字

即使我們的 php 類具有掛鉤屬性,您仍然可以將它們?cè)O(shè)置為只讀。例如,我們可能想讓 user 類只讀:

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;
    }
}

登錄后復(fù)制

但是,hook 屬性不能直接使用 readonly 關(guān)鍵字。例如,這個(gè)類將是無(wú)效的:

class user
{
    public readonly string $fullname {
        get => $this->firstname.' '.$this->lastname;
    }

    public function __construct(
        string $firstname,
        string $lastname,
    ) {
        $this->firstname = $firstname;
        $this->lastname = $lastname;
    }
}

登錄后復(fù)制

上面的代碼會(huì)拋出以下錯(cuò)誤:

fatal error: hooked properties cannot be readonly

登錄后復(fù)制

property”魔法常數(shù)

在 php 8.4 中,引入了一個(gè)名為 __property__ 的新魔法常量。該常量可用于引用屬性掛鉤內(nèi)的屬性名稱。

讓我們看一個(gè)例子:

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;
    }
}

登錄后復(fù)制

在上面的代碼中,我們可以看到在lastname屬性的setter中使用__property__將會(huì)輸出屬性名稱lastname。然而,還值得注意的是,嘗試使用此常量來(lái)嘗試設(shè)置屬性值將觸發(fā)錯(cuò)誤:

fatal error: uncaught error: must not write to virtual property user::$lastname

登錄后復(fù)制

有一個(gè)關(guān)于 __property__ 魔法常量的方便用例示例,您可以在 github 上查看:https://github.com/crell/php-rfcs/blob/master/property-hooks/examples.md。

接口中的掛鉤屬性

php 8.4 還允許您在接口中定義可公開(kāi)訪問(wèn)的掛鉤屬性。如果您想強(qiáng)制類使用鉤子實(shí)現(xiàn)某些屬性,這會(huì)很有用。

讓我們看一下聲明了掛鉤屬性的示例接口:

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; }
}

登錄后復(fù)制

在上面的接口中,我們定義任何實(shí)現(xiàn) nameable 接口的類都必須具有:

至少可公開(kāi)獲取的 fullname 屬性。這可以通過(guò)定義 get hook 或根本不定義 hook 來(lái)實(shí)現(xiàn)。
至少可公開(kāi)獲取的firstname 屬性。
至少可公開(kāi)設(shè)置的姓氏屬性。這可以通過(guò)定義具有設(shè)置鉤子的屬性或根本不定義鉤子來(lái)實(shí)現(xiàn)。但如果該類是只讀的,那么該屬性必須有一個(gè)設(shè)置的鉤子。

這個(gè)實(shí)現(xiàn) nameable 接口的類是有效的:

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;
    }
}

登錄后復(fù)制

上面的類是有效的,因?yàn)?fullname 屬性有一個(gè) get 鉤子來(lái)匹配接口定義。 firstname 屬性只有一個(gè) set hook,但仍然可以公開(kāi)訪問(wèn),因此它滿足條件。 lastname 屬性沒(méi)有 get 掛鉤,但它是可公開(kāi)設(shè)置的,因此它滿足條件。

讓我們更新 user 類以強(qiáng)制執(zhí)行 fullname 屬性的 get 和 set 掛鉤:

interface nameable
{
    public string $fullname { get; set; }

    public string $firstname { get; }

    public string $lastname { set; }
}

登錄后復(fù)制

我們的 user 類將不再滿足 fullname 屬性的條件,因?yàn)樗鼪](méi)有定義 set hook。這會(huì)導(dǎo)致拋出以下錯(cuò)誤:

fatal error: class user contains 1 abstract methods and must therefore be declared abstract or implement the remaining methods (nameable::$fullname::set)

登錄后復(fù)制

抽象類中的掛鉤屬性

與接口類似,你也可以在抽象類中定義鉤子屬性。如果您想提供一個(gè)定義子類必須實(shí)現(xiàn)的掛鉤屬性的基類,這可能很有用。您還可以在抽象類中定義鉤子,并在子類中覆蓋它們。

例如,讓我們創(chuàng)建一個(gè) model 抽象類,定義一個(gè)必須由子類實(shí)現(xiàn)的 name 屬性:

abstract class model
{
    abstract public string $fullname {
        get => $this->firstname.' '.$this->lastname;
        set;
    }

    abstract public string $firstname { get; }

    abstract public string $lastname { set; }
}

登錄后復(fù)制

在上面的抽象類中,我們定義任何擴(kuò)展 model 類的類都必須具有:

至少可公開(kāi)獲取和設(shè)置的 fullname 屬性。這可以通過(guò)定義 get 和 set 鉤子或根本不定義鉤子來(lái)實(shí)現(xiàn)。我們還在抽象類中定義了 fullname 屬性的 get 鉤子,因此我們不需要在子類中定義它,但如果需要,可以覆蓋它。
至少可公開(kāi)獲取的firstname 屬性。這可以通過(guò)定義 get hook 或根本不定義 hook 來(lái)實(shí)現(xiàn)。
至少可公開(kāi)設(shè)置的姓氏屬性。這可以通過(guò)定義具有設(shè)置鉤子的屬性或根本不定義鉤子來(lái)實(shí)現(xiàn)。但如果該類是只讀的,那么該屬性必須有一個(gè)設(shè)置的鉤子。

然后我們可以創(chuàng)建一個(gè)擴(kuò)展 model 類的 user 類:

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;
    }
}

登錄后復(fù)制

結(jié)論

希望本文能讓您深入了解 php 8.4 屬性掛鉤的工作原理以及如何在 php 項(xiàng)目中使用它們。

如果這個(gè)功能一開(kāi)始看起來(lái)有點(diǎn)令人困惑,我也不會(huì)太擔(dān)心。當(dāng)我第一次看到它時(shí),我也有點(diǎn)困惑(特別是它們?nèi)绾闻c接口和抽象類一起工作)。但一旦你開(kāi)始修補(bǔ)它們,你很快就會(huì)掌握它的竅門。

我很高興看到這個(gè)功能將如何在野外使用,我期待著 php 8.4 發(fā)布時(shí)在我的項(xiàng)目中使用它。

如果您喜歡閱讀這篇文章,您可能有興趣查看我的 220 多頁(yè)電子書(shū)“battle ready laravel”,其中更深入地涵蓋了類似的主題。

或者,您可能想查看我的另一本 440 多頁(yè)電子書(shū)“consuming apis in laravel”,它教您如何使用 laravel 來(lái)使用來(lái)自其他服務(wù)的 api。

如果您有興趣在我每次發(fā)布新帖子時(shí)獲得更新,請(qǐng)隨時(shí)訂閱我的時(shí)事通訊。

繼續(xù)創(chuàng)造精彩的東西! ?

分享到:
標(biāo)簽:PHP 屬性
用戶無(wú)頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 52010

    網(wǎng)站

  • 12

    小程序

  • 1106242

    文章

  • 784

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過(guò)答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫(kù),初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定