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