I have two interfaces (ClienInterface
, ClientFactoryInterface
) and two classes implementing them (ConcreteClient
, ConcreteApiClientFactory
). ConcreteClient
has method not definded in ClienInterface
.
When I try to use this method in code, I get PHPStan errors:
Call to an undefined method ClienInterface::mySpecificFunction().
I've tried to implement this but with no luck: https://phpstan.org/blog/generics-by-examples#couple-relevant-classes-together
My example on PHPStan playground:
<?php declare(strict_types = 1);
interface ClienInterface
{
}
/** @template TClienInterface of ClienInterface */
interface ClientFactoryInterface
{
public function getClientByType(string $type): ClienInterface;
}
class ConcreteClient implements ClienInterface {
public function mySpecificFunction(): void {}
}
/** @implements ClientFactoryInterface<ConcreteClient> */
class ConcreteApiClientFactory implements ClientFactoryInterface {
public function getClientByType(string $type): ClienInterface {
return new ConcreteClient();
}
}
class Test {
public function __construct(
private readonly ClientFactoryInterface $factory
) {}
public function getClient(string $type): void {
$client = $this->factory->getClientByType($type);
$client->mySpecificFunction();
}
}
Errors
Method Test::__construct() has parameter $factory with generic interface ClientFactoryInterface but does not specify its types: TClienInterface
Call to an undefined method ClienInterface::mySpecificFunction().
Other answers might be fine, but I see you've started with generics, which could be a great solution. There are a couple of changes required:
/** @template TClienInterface of ClienInterface */
interface ClientFactoryInterface
{
/**
* @return TClienInterface
*/
public function getClientByType(string $type): ClienInterface;
}
Defining the returntype of getClientByType
to the generic you've defined on the class makes sure the specific type is returned.
class Test {
/**
* @param ClientFactoryInterface<ConcreteClient> $factory
*/
public function __construct(
private readonly ClientFactoryInterface $factory
) {}
}
Defining the generic on the constructor makes sure the class only accepts factories that return that specific type. Thiss passes PHPstan's tests.