A fluent method returning self promises the class it is declared in, not the class it is called on — so a subclass inherits a method whose signature lies about what it returns.
abstract class Query
{
public function where(string $c): self // NOT the subclass
{
$this->conditions[] = $c;
return $this;
}
}
// the 7.4 workaround: annotate what the engine cannot express
/**
* @return static
*/
public function where(string $c)
{
// ...
}
static as a return type arrives in 8.0. Until then the honest arrangement is no native type and a @return static docblock, which PHPStan and PhpStorm both understand even though the engine does not. Declaring self and hoping is the version that breaks: a subclass’s fluent chain type-checks to the parent, and the analyser then rejects a perfectly correct call to a method the subclass added.