Search code examples
phpsymfonyphpstan

Type hinting a property with var annotation


In Symfony, if you want to autowire some dependencies in a service, many times you use an interface instead of a concrete class. For example, you might have:

    public function __construct(
        private readonly TranslatorInterface $translator
    ) {
    }

However, the class that you receive will be a concrete class, in this case for example you might receive a DataCollectorTranslator

If we try to use a function which is present in the concrete class, but not in the interface, for example:

$this->translator->setLocale($locale);

Then PHPStan will (rightly) complain that setLocale() is not defined in TranslatorInterface which is the type that we used for the property when we defined it. We cannot define the property with the type of the actual class because that class has not been defined as a service and Symfony will not be able to autowire it.

What is the best way to let PHPStan know the actual type of the property that we receive in this case? I have been doing:

/** @var DataCollectorTranslator $translator */
$translator = $this->translator;
$translator->setLocale($locale->getCode());

But that looks cumbersome, with a variable added just to be able to define the actual type of a property. Are there any better solutions? Thanks!


Solution

  • This is not safe in the first place:

    $this->translator->setLocale($locale);
    

    You should ensure that the method setLocale exists before calling it, e.g. by using assert and instanceof:

    assert($this->translator instanceof DataCollectorTranslator);
    $this->translator->setLocale($locale);
    

    The assert calls can be skipped through php.ini, so it will not increase runtime overhead. This method is also recommended in the phpstan's docs.