Laravel 11 中的 PHP 泛型

DDD
發布: 2024-10-23 08:08:29
原創
408 人瀏覽過

PHP Generics in Laravel 11

如果您是Laravel 的Web 應用程式建構者,並且碰巧使用PHPStan 進行靜態程式碼分析,那麼當您升級到Laravel 11.x.

使用

PHPStan 全新安裝 Laravel 時,第一次執行 ./vendor/bin/phpstan 時會拋出以下錯誤:

 ------ -----------------------------------------------------------------------------------
  Line   app\Models\User.php
 ------ -----------------------------------------------------------------------------------
  13     Class App\Models\User uses generic trait
         Illuminate\Database\Eloquent\Factories\HasFactory but does not specify its types:
         TFactory
 ------ -----------------------------------------------------------------------------------
登入後複製
登入後複製
登入後複製
那麼改變了什麼?在 Laravel 11 中,HasFactory 特徵現在有一個

PHPDoc 和 @template 標籤,這是保留的泛型標籤之一。正如您可能已經猜到的,框架的許多部分都使用了泛型。

/**
 * @template TFactory of \Illuminate\Database\Eloquent\Factories\Factory
 */
trait HasFactory
{
    ...
}
登入後複製
登入後複製
登入後複製
雖然不建議這樣做,但只需將這些程式碼行新增到您的 phpstan.neon 檔案即可忽略此類錯誤:


parameters:
    ignoreErrors:
        -
            identifier: missingType.generics
登入後複製
登入後複製
但是,泛型並不難理解,所以讓我們開始吧!

什麼是泛型?

程式設計中的泛型是指允許您編寫可處理多種資料類型的程式碼的功能。您無需為每種資料類型編寫單獨的程式碼,而是可以編寫一段通用的程式碼,該程式碼可以在保持類型安全的同時對各種類型進行操作,這與使用混合或物件等通用類型不同。

採用

Laravel 10 中的 IlluminateDatabaseConcernsBuildsQueries::first 方法,它可以傳回 Model 的實例、通用物件、像 IlluminateDatabaseEloquentBuilder 一樣使用它的類別的實例或 null。

/**
 * Execute the query and get the first result.
 *
 * @param  array|string  $columns
 * @return \Illuminate\Database\Eloquent\Model|object|static|null
 */
public function first($columns = ['*'])
{
    return $this->take(1)->get($columns)->first();
}
登入後複製
登入後複製
泛型語法

PHP 中不支援泛型作為一等公民,為了擁有它們,我們使用

PHPDocs 標籤 @template、@template-covariant、@template-contravariant、@extends、@implements 和@使用。

泛型類型的規則是使用

型別參數定義的。 PHPDocs中,我們使用@template標籤對它們進行註釋。類型參數名稱可以是任何名稱,只要不使用現有的類別名稱即可。您也可以使用 of 關鍵字限制可以使用哪些類型來取代帶有上限的類型參數。這稱為有界型參數。

<?php

namespace Illuminate\Database\Eloquent;

/**
 * @template TModel of \Illuminate\Database\Eloquent\Model
 *
 */
class Builder implements BuilderContract
{
}
登入後複製
登入後複製
PHP 泛型的類型

通用函數

泛型函數與普通函數完全相同,但是它具有型別參數。這允許以更通用的方式使用通用方法。

以 IlluminateSupportValidatedInput::enum 方法為例:

  • 它定義了一個型別參數 TEnum。

  • $enumClass 參數是偽型別 class-string,並且綁定到相同型別參數 TEnum。

  • 回傳型別也可以是 TEnum 或 null。


 ------ -----------------------------------------------------------------------------------
  Line   app\Models\User.php
 ------ -----------------------------------------------------------------------------------
  13     Class App\Models\User uses generic trait
         Illuminate\Database\Eloquent\Factories\HasFactory but does not specify its types:
         TFactory
 ------ -----------------------------------------------------------------------------------
登入後複製
登入後複製
登入後複製

如果您隨後呼叫 $request→validated()→enum('status', OrderStatus::class),PHPStan 將知道您正在取得 OrderStatus 物件或 null!

通用類別

泛型類別允許建立可以對任何資料類型進行操作的類,同時確保類型安全。它們允許使用特定類型的佔位符來定義類,稍後可以在類實例化時替換該佔位符。

Laravel 原始碼中的一個很好的例子是 IlluminateDatabaseEloquentBuilder 類別:

/**
 * @template TFactory of \Illuminate\Database\Eloquent\Factories\Factory
 */
trait HasFactory
{
    ...
}
登入後複製
登入後複製
登入後複製

類型參數 TModel 被定義並綁定到 IlluminateDatabaseEloquentModel 的任何子類別。相同的型別參數用作 make 方法的傳回類型。

另一個例子是,如果我們有一個訂單模型,它有一個本地範圍來根據訂單狀態過濾訂單。範圍方法應指定 TModel 類型

parameters:
    ignoreErrors:
        -
            identifier: missingType.generics
登入後複製
登入後複製

ℹ️ info:命名空間 IlluminateDatabaseEloquentRelations 中的所有 Eloquent 關係類別(例如 BelongsTo 和 HasOne)現在都是通用的。

通用介面

通用介面並沒有那麼不同。 IlluminateContractsSupportArrayable 是通用介面的範例

/**
 * Execute the query and get the first result.
 *
 * @param  array|string  $columns
 * @return \Illuminate\Database\Eloquent\Model|object|static|null
 */
public function first($columns = ['*'])
{
    return $this->take(1)->get($columns)->first();
}
登入後複製
登入後複製

此介面定義了兩個型別參數:array-key類型的TKey(可以是int或string)和TValue。這兩個參數用於定義 toArray 函數的傳回類型。這是一個例子:

<?php

namespace Illuminate\Database\Eloquent;

/**
 * @template TModel of \Illuminate\Database\Eloquent\Model
 *
 */
class Builder implements BuilderContract
{
}
登入後複製
登入後複製

使用者類別實作 Arrayable 接口,並指定 Tkey 類型為 int,TValue 型為 string。

通用特徵

我們在本文開頭的錯誤中遇到了 IlluminateDatabaseEloquentFactoriesHasFactory 特徵。讓我們仔細看看:

/**
 * @template TEnum
 *
 * @param string $key
 * @param class-string<TEnum> $enumClass
 * @return TEnum|null
 */
public function enum($key, $enumClass)
{
    if ($this->isNotFilled($key) ||
        ! enum_exists($enumClass) ||
        ! method_exists($enumClass, 'tryFrom')) {
        return null;
    }
    return $enumClass::tryFrom($this->input($key));
}
登入後複製

HasFactory 定義了一個型別參數 TFactory,它綁定到 IlluminateDatabaseEloquentFactoriesFactory 的子類別。那麼要如何修復這個錯誤呢?

使用 Trait 時必須指定 TFactory 類型。因此,HasFactory 特徵的 use 語句需要使用 PHPDocs @use:
進行註釋

<?php

namespace Illuminate\Database\Eloquent;
/**
 * @template TModel of \Illuminate\Database\Eloquent\Model
 */
class Builder implements BuilderContract
{
    /**
     * @param  array  $attributes
     * @return TModel
     */
    public function make(array $attributes = [])
    {
        return $this->newModelInstance($attributes);
    }
}
登入後複製

保持通用性

擴充類別、實作介面或使用特徵時,可以保持子類別中的通用性。

透過在子類別上方定義相同的類型參數並將其傳遞給 @extends、@implements 和 @use 標籤來實現保留通用性。

我們將使用 IlluminateDatabaseConcernsBuildsQueries 通用特徵作為範例,

它定義了一個型別參數TValue:

 ------ -----------------------------------------------------------------------------------
  Line   app\Models\User.php
 ------ -----------------------------------------------------------------------------------
  13     Class App\Models\User uses generic trait
         Illuminate\Database\Eloquent\Factories\HasFactory but does not specify its types:
         TFactory
 ------ -----------------------------------------------------------------------------------
登入後複製
登入後複製
登入後複製

IlluminateDatabaseEloquentBuilder 類別使用此特徵,但透過向其傳遞 TModel 參數類型來保持其通用性。現在由客戶端程式碼來指定 TModel 的類型,從而在 BuildsQueries 特徵中指定 TValue。

/**
 * @template TFactory of \Illuminate\Database\Eloquent\Factories\Factory
 */
trait HasFactory
{
    ...
}
登入後複製
登入後複製
登入後複製

最後的想法

總之,雖然 PHP 並不像其他程式語言那樣原生支援泛型,但引入高階類型提示和工具(例如 PHPStan)允許開發人員在程式碼中實現類似泛型的功能。透過利用 PHPDocs、參數化類別和接口,您可以創建更靈活和類型安全的應用程序,從而提高程式碼的可重用性和可維護性。隨著 PHP 的不斷發展,社群對類型安全和靜態分析的日益關注可能會帶來更強大的泛型實現解決方案。接受這些實踐不僅可以提高您的編碼技能,還有助於開發經得起時間考驗的高品質軟體。

以上是Laravel 11 中的 PHP 泛型的詳細內容。更多資訊請關注PHP中文網其他相關文章!

來源:dev.to
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!