`Form` is not subtype of native type `FormInterface`
daniel-calderini opened this issue · 6 comments
Starting with version 1.3.0 of phpstan/phpstan-symfony, the following code is not valid anymore.
// src/Controller/DefaultController.php
use Symfony\Component\Form\Form;
use Symfony\Component\Form\FormInterface;
...
/** @var Form&FormInterface $form */
$form = $this->createForm(...);
Until version 1.2.25, no problems were reported here, but now I get the following error:
PHPDoc tag @var with type Symfony\Component\Form\Form is not subtype of native type Symfony\Component\Form\FormInterface.
This is weird. Can you please create a small reproducing repository?
I had the same issue. I ended up removing the phpdoc and surrounding some code with an if statement with $form instanceof Form.
We have the same issue currently (1.4.5 + PHPStan 1.11.6). Consider simplified code:
services:
# Interface
Foo\ClientFactory: '@Foo\ApplicationClientFactory'
# Implementation
Foo\ApplicationClientFactory: ~then, when we do something like:
/** @var \Foo\ClientFactory $factory */
$factory = $container->get(\Foo\ClientFactory::class);we get an error:
PHPDoc tag @var with type Foo\ClientFactory is not subtype of type Foo\ApplicationClientFactory.
which does not make sense. We fetch ClientFactory from DI container, it's aliased to ApplicationClientFactory implementation that satisfies the interface, so of course interface is not a subtype, it's super type.
PHPStan doesn't want you to use a less specific type in the PHPDoc, because it already knows a more specific type.
More often than not, you can simply delete this @var.
They're considered harmful anyway: https://phpstan.org/blog/phpstan-1-10-comes-with-lie-detector#validate-inline-phpdoc-%40var-tag-type
But I don't want more specific type here, as the whole idea of interface is interchangeability. In the code, when I fetch ClientFactory (interface) I don't care what implementation comes from DI container, I only want this particular contract, because I don't want to encounter problems when DI alias is changed to other implementation. In our case ApplicationClientFactory has more methods that implemented ClientFactory interface, I don't want developers to be able to use these methods because PHPStan allows it (as it knows the aliased service), rather the other way around - I would expect that static analysis reports usage of methods not defined in the interface.
I believe it's a bug and PHPStan should not narrow the type here. Similar here, I believe I should be allowed to restrict the contract to interface instead of relying on extended implementation.
More often than not, you can simply delete this
@var. They're considered harmful anyway: phpstan.org/blog/phpstan-1-10-comes-with-lie-detector
I agree and I don't use @var when I don't need, in this case it's only for IDE which does not have autocompletion for ClientFactory fetched from the container (since get()'s return type is ?object). We use dependency injection mostly, but unfortunately in legacy part of the code there is a lot of direct interaction with DI container.
@Wirone This is a completely different issue than the thing this was originally opened for so please create a separate one.