通過範例來深入瞭解PHP中的泛型

2022-04-12 13:00:52
本篇文章帶大家深入瞭解PHP中的泛型,介紹兩個泛型範例,希望對大家有所幫助!

深入泛型

我在 中展示了一個非常無聊的泛型範例,我們將在這個中做得更好。

$users = new Collection<User>();

$slugs = new Collection<string>();

集合 它們可能是解釋泛型的最簡單方法,但它們也是每個人在討論泛型時都會談論的範例。人們通常認為「泛型」和「具有型別的集合」是一回事。絕對不是這樣。

所以讓我們再看兩個例子。

1.png

這是一個名為「app」的函數——如果你使用像 Laravel 這樣的框架,它可能看起來很熟悉:這個函數接受一個類名,並使用依賴容器解析該類的一個範例:

function app(string $className): mixed
{
    return Container::get($className);
}

現在,你不需要知道容器是如何工作的,重要的是這個函數會給你一個你請求的類的範例。

所以,基本上,它是一個通用函數;一個返回型別取決於你給它的類名。如果我們的 IDE 和其他靜態分析器也明白,如果我給這個函數提供類名「UserRepository」,我希望返回一個 UserRepository 的範例,而不是別的,那就太酷了:

function app(string $className): mixed
{ /* … */ }

app(UserRepository::class); // ?

好吧,泛型允許我們這樣做。

我想現在是提一下我一直保守祕密的好時機,就像: 我在 上一篇 中提到 PHP 中不存在泛型;好吧,這並不完全正確。那裡的所有靜態分析器——無需執行即可讀取程式碼的工具,像你的 IDE 之類的工具——他們允許將 doc 塊註釋用於泛型:

/**
 * @template Type
 * @param class-string<Type> $className
 * @return Type
 */
function app(string $className): mixed
{ /* … */ }

誠然:這不是最完美的語法,所有靜態分析器都依賴於一個簡單的協定,即這是沒有官方規範語法; 但是:它有效。PHP 世界中最大的三個靜態分析器:PhpStorm、Psalm 和 PhpStan,都在一定程度上理解這種語法。

像 PhpStorm 這樣的 IDE 使用它,以便在程式設計師編寫程式碼時向他們提供反饋,而像 Psalm 和 PhpStan 這樣的工具使用它,來批次分析你的程式碼庫並檢測潛在的 bug,主要基於型別定義。

所以實際上,我們可以構建這個 app 函數,使我們的工具不再在黑暗中執行。 當然,PHP 本身並不能保證返回型別是正確的,因為 PHP 不會在執行時對該函數進行型別檢查; 但是,如果我們可以相信我們的靜態分析器是正確的,那麼在執行它時,這段程式碼就很少——甚至沒有機會被中斷。

這就是靜態分析令人難以置信的力量:實際上,我們可以確定,無需執行我們的程式碼; 其中大部分將按預期工作。 所有這一切都歸功於型別——包括泛型。

讓我們來看一個更復雜的例子:

Attributes::in(MyController::class)
    ->filter(RouteAttribute::class)
    ->newInstance()
    ->

在這裡,我們有一個可以「查詢」屬性並即時範例化它們的類。 如果你在知道它們的反射 API 相當冗長之前使用過屬性,那麼我發現這種輔助類非常有用。

當我們使用 filter 方法時,我們給它一個屬性的類名; 然後呼叫 newInstance 方法,我們知道結果將是我們過濾類的一個範例。 再說一遍:如果我們的 IDE 能理解我們在說什麼,那就太好了。

你猜對了:泛型允許我們這樣做:

/** @template AttributeType */
class Attributes
{
    /**
     * @template InputType
     * @param class-string<InputType> $className
     * @return self<InputType>
     */
    public function filter(string $className): self
    { /* … */ }

    /**
     * @return AttributeType 
     */   
    public function newInstance(): mixed
    { /* … */ }

    // …
}

我希望你開始看到簡單型別資訊的強大功能。幾年前,我需要一個 IDE 外掛才能讓這些洞察力發揮作用,現在我只需要新增一些型別資訊。

不過,這個最新的範例不僅依賴於泛型,還有另一個同樣重要的部分在起作用。型別推斷:靜態分析器「猜測」—— 或可靠地確定 —— 無需使用者指定型別的能力。 這就是那裡的類字串註釋正在發生的事情。我們的 IDE 能夠將我們提供給此函數的輸入識別為類名,並將該型別推斷為泛型型別。

所以,一切都解決了,對吧:PHP中有泛型,所有主要的靜態分析器都知道如何使用它們。嗯…有幾個警告。

首先,沒有關於泛型應該是什麼樣子的官方規範,現在每個靜態分析器都可以使用自己的語法;目前,他們碰巧已經就其中一個達成了一致;但未來幾乎沒有保障。

其次:在我看來,檔案塊是次優的。他們覺得自己在我們的程式碼庫中不那麼重要。當然,泛型註釋只提供靜態洞察,沒有執行時功能,但我們已經看到了靜態分析的強大功能,即使沒有執行時型別檢查。我認為將型別資訊視為「檔案註釋」是不公平的,它沒有在我們的程式碼中傳達這些型別的重要性。這就是為什麼我們在PHP8中得到了屬性:屬性提供的所有功能,在docblock註釋中都是可能的,但感覺還不夠好。泛型也是如此。

最後一點:如果沒有合適的規範,所有三種主要的靜態分析儀在其泛型實現之間都存在差異。PhpStorm是目前最缺乏的一種。理想情況下,會有一個來自PHP內部的官方規範。但是官方現在沒有。

這些是我認為值得在更持久、更可持續的解決方案上投入時間的主要原因。那麼為什麼PHP還沒有合適的泛型呢?為什麼我們依賴沒有明確規範的檔案塊?

原文地址:https://stitcher.io/blog/generics-in-php-2

譯文地址:https://learnku.com/php/t/66484

推薦:《》

以上就是通過範例來深入瞭解PHP中的泛型的詳細內容,更多請關注TW511.COM其它相關文章!