What's the right way to fix the error generated by phpstan
for this sample code? The error message is:
Method Foo::foo() should return Child but returns Base.
<?php declare(strict_types = 1);
Interface MyI {
abstract function a(): self;
}
Class Base implements MyI {
public function a(): self {return $this;}
}
Class Child extends Base {
public function c(): self {return $this;}
}
Class Foo {
public function factory(): Child {
return new Child();
}
/**
* @return Child
*/
public function foo() /* note no return type */ {
return $this->factory()
->c()
->a();
}
}
One way to eliminate the error is to change the @return
to read:
/**
* @return Child | Base
*/
But I greatly dislike that because foo()
never returns an instance of Base
. The code only ever works with the derived class Child
. In fact, the interface and base classes are both in a library (dependency), so I mostly cannot even change that code.
I could re-implement a()
in my Child
class like this:
public function a(): Child {
parent::a();
return $this;
}
But that is also ugly, and worse, this is a toy example. In the real class, I'd have to re-implement many methods. Very bad.
You need @return static
for that: https://phpstan.org/r/42ee409c-ada2-44c9-a26d-6a85c2327bca
To learn about all different PHPDoc posibilities head to PHPStan documentation:
Had something similar too, although my case was a bit different.
My code was:
trait X
{
protected function createFoo(string $childClassName): BaseClass
{
// do something...
return $childClassName::createFrom($this);
}
}
And I was getting the same error as OP:
public function bar(): ChildClass
{
return $this->createFoo(ChildClass::class);
// phpstan error: Method bar() should return Child but returns Base
}
After reading briefly about phpstan generics, ended up with the following phpdoc. Seems to solve the issue the right way:
trait X
{
/**
* @template T of BaseClass
* @param class-string<T> $childClassName
* @return T
*/
protected function createFoo(string $childClassName): BaseClass
{
// do something...
return $childClassName::createFrom($this);
}
}
Hope this helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With