k0p*_*kus 3 php laravel illuminate-container phpstan
鉴于我的班级
<?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>
}
}
Run Code Online (Sandbox Code Playgroud)
我非常困惑为什么 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)
Run Code Online (Sandbox Code Playgroud)
为什么 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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
将传递。
Foo为了这个问题,我的只是一个虚拟类。唯一相关的是它获取一个stdClass实例并将其转换为一个?Foo实例。
18: [ERROR] Method PhpstanIssue::whyDoesThisFail() should return
Illuminate\Support\Collection&iterable<Foo> but returns
Illuminate\Support\Collection&iterable<Illuminate\Support\Collection&iterable<stdClass>>. (phpstan)
Run Code Online (Sandbox Code Playgroud)
Illuminate\\Support\\Collection类本身不是通用的。所以写法Collection<Foo>是错误的。这会导致错误消息,例如Illuminate\\Support\\Collection&iterable<Illuminate\\Support\\Collection&iterable<stdClass>>
您有两个选择:
\n安装拉拉斯坦。它是 Laravel 的 PHPStan 扩展。它具有使类通用的存根文件。Illuminate\\Support\\Collection
或者,如果您只是使用illuminate/collections独立包而没有完整的 Laravel 应用程序,您可以编写自己的存根文件。\n来自PHPStan 文档:
\n\n...您可以使用正确的 PHPDoc 编写存根文件。它\xe2\x80\x99s就像源代码,但PHPStan只从中读取PHPDocs。因此,命名空间和类/接口/特征/方法/函数名称必须与您\xe2\x80\x99描述的原始源匹配。但方法体可以留空,PHPStan 只对 PHPDocs 感兴趣。
\n
对于您的示例,以下存根文件应该足够了:
\n<?php\n\nnamespace Illuminate\\Support;\n\n/**\n * @template TKey\n * @template TValue\n * @implements \\ArrayAccess<TKey, TValue>\n * @implements Enumerable<TKey, TValue>\n */\nclass Collection implements \\ArrayAccess, Enumerable\n{\n /**\n * @template TReturn\n * @param callable(TValue, TKey): TReturn $callable\n * @return static<TKey, TReturn>\n */\n public function map($callable) {}\n}\nRun Code Online (Sandbox Code Playgroud)\n