php - 如何让 phpstan 推断我的 Laravel Collection 管道的类型?

标签 php laravel illuminate-container phpstan

考虑到我的类(class)

<?php
declare(strict_types=1);

use Illuminate\Support\Collection;
use stdClass;

class PhpstanIssue
{
    /**
     * @param Collection<Collection<stdClass>> $collection
     *
     * @return Collection<Foo>
     */
    public function whyDoesThisFail(Collection $collection): Collection
    {
        return $collection
            ->flatten() // Collection<stdClass>
            ->map(static function (\stdClass $std): ?Foo {
                return Foo::get($std);
            }) // should now be Collection<?Foo>
            ->filter(); // should now be Collection<Foo>
    }
}

我非常困惑为什么 phpstan (0.12.64) 会失败:

18: [ERROR] Method PhpstanIssue::whyDoesThisFail() should return
Illuminate\Support\Collection&iterable<Foo> but returns 
Illuminate\Support\Collection&iterable<Illuminate\Support\Collection&iterable<stdClass>>. (phpstan)

为什么 phpstan 无法推断出该管道的正确结果类型?如何让 phpstan 理解管道?


我可以验证我的代码是否在 phpunit 测试用例中工作:

class MyCodeWorks extends TestCase
{
    public function testPipeline()
    {
        $result = (new PhpstanIssue())->whyDoesThisFail(
            new Collection(
                [
                    new Collection([new \stdClass(), new \stdClass()]),
                    new Collection([new \stdClass()]),
                ]
            )
        );

        self::assertCount(3, $result);
        foreach ($result as $item) {
            self::assertInstanceOf(Foo::class, $item);
        }
    }
}

会通过的。


为了这个问题,我的 Foo 只是一个虚拟类。唯一相关的是它需要一个 stdClass 实例并将其转换为 ?Foo 实例。

class Foo
{
    public static function get(\stdClass $std): ?Foo
    {
        // @phpstan-ignore-next-line
        return (bool) $std ? new static() : null;
    }
}

最佳答案

Illuminate\Support\Collection类本身不是通用的。所以写Collection<Foo>是错的。这会导致类似 Illuminate\Support\Collection&iterable<Illuminate\Support\Collection&iterable<stdClass>> 的错误消息

您有两个选择:

  1. 正在安装 Larastan 。它是 Laravel 的 PHPStan 扩展。它有stub files这使得Illuminate\Support\Collection类泛型。

  2. 或者如果您只是使用 illuminate/collections没有完整 Laravel 应用程序的独立包,您可以编写自己的 stub 文件。 来自 PHPStan docs :

... you can write a stub file with the right PHPDoc. It’s like source code, but PHPStan only reads PHPDocs from it. So the namespace and class/interface/trait/method/function names must match with the original source you’re describing. But method bodies can stay empty, PHPStan is only interested in the PHPDocs.

对于您的示例,以下 stub 文件应该足够了:

<?php

namespace Illuminate\Support;

/**
 * @template TKey
 * @template TValue
 * @implements \ArrayAccess<TKey, TValue>
 * @implements Enumerable<TKey, TValue>
 */
class Collection implements \ArrayAccess, Enumerable
{
    /**
     * @template TReturn
     * @param callable(TValue, TKey): TReturn $callable
     * @return static<TKey, TReturn>
     */
    public function map($callable) {}
}

关于php - 如何让 phpstan 推断我的 Laravel Collection 管道的类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66282988/

相关文章:

javascript - 链接 jquery 文件未按预期工作

php - 如何使用 laravel 5.8 在 Controller 中创建一个条件?

php - Laravel 错误未捕获异常 'ReflectionException',消息为 'Class App\Http\Kernel does not exist'

php - 如何使用 Illuminate/Html 在 Laravel 中设置禁用选择选项

php - 我如何在 PHP 中查看**所有**当前定义的变量?

php - 这个 MySQL 表结构对我的性能和可扩展性有帮助吗?

laravel - 我应该如何在 Laravel 包中输入提示 UserModel?

php - 加载关系时,Laravel/Illuminate 的 useWritePdo 不受尊重

php - 索引中的 Seo 语言重定向

php - 将mysql相关表导出到csv