【发布时间】:2021-02-19 18:20:34
【问题描述】:
鉴于我的课
<?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默认不是泛型类。你也在使用 Larastan 吗? -
@CanVural 不,我目前没有使用 Larastan。使用它可以实现吗?
-
是的,Larastan 有将
Collection类标记为通用的存根文件。它应该适用于您的示例。虽然它也需要一个键类型。所以像Collection<int, Collection<int, stdClass>>这样的东西,但如果你不想使用它,你也可以创建你的存根文件,将通用 PHPDocs 添加到Collection类。 -
@CanVural 我会接受它作为支持/批准 btw 的答案 :)
-
我添加了一个答案。文档中解释了如何使用 PHPStan 注册该存根文件(我在答案中链接了该文件),所以我没有在这里重复。
标签: php laravel illuminate-container phpstan