From 4ff55f93784e1b07f22491c144e512c08059f2fd Mon Sep 17 00:00:00 2001 From: Christophe Coevoet Date: Tue, 21 Apr 2026 17:11:21 +0200 Subject: [PATCH 1/2] Add a dynamic stub loader to prefer upstream types Once the precise types are added in Symfony, the upstream types should be used to benefit from the maintenance work of the Symfony team. --- extension.neon | 51 +----- .../Symfony/StubFilesExtensionLoader.php | 155 ++++++++++++++++++ src/Symfony/InputBagStubFilesExtension.php | 35 ---- 3 files changed, 161 insertions(+), 80 deletions(-) create mode 100644 src/Stubs/Symfony/StubFilesExtensionLoader.php delete mode 100644 src/Symfony/InputBagStubFilesExtension.php diff --git a/extension.neon b/extension.neon index f8516ed0..55e0b0a4 100644 --- a/extension.neon +++ b/extension.neon @@ -11,29 +11,13 @@ parameters: messenger: handleTraitWrappers: [] stubFiles: - - stubs/Psr/Cache/CacheException.stub - - stubs/Psr/Cache/CacheItemInterface.stub - - stubs/Psr/Cache/InvalidArgumentException.stub - stubs/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.stub - - stubs/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AuthenticatorFactoryInterface.stub - - stubs/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/FirewallListenerFactoryInterface.stub - - stubs/Symfony/Component/Console/Command.stub - - stubs/Symfony/Component/Console/Exception/ExceptionInterface.stub - - stubs/Symfony/Component/Console/Exception/InvalidArgumentException.stub - - stubs/Symfony/Component/Console/Exception/LogicException.stub - - stubs/Symfony/Component/Console/Helper/HelperInterface.stub - stubs/Symfony/Component/Console/Output/OutputInterface.stub - - stubs/Symfony/Component/DependencyInjection/ContainerBuilder.stub - - stubs/Symfony/Component/DependencyInjection/Extension/ExtensionInterface.stub - - stubs/Symfony/Component/EventDispatcher/EventDispatcherInterface.stub - - stubs/Symfony/Component/EventDispatcher/EventSubscriberInterface.stub - - stubs/Symfony/Component/EventDispatcher/GenericEvent.stub - stubs/Symfony/Component/Form/AbstractType.stub - stubs/Symfony/Component/Form/ChoiceList/Loader/ChoiceLoaderInterface.stub - stubs/Symfony/Component/Form/Exception/ExceptionInterface.stub - stubs/Symfony/Component/Form/Exception/RuntimeException.stub - stubs/Symfony/Component/Form/Exception/TransformationFailedException.stub - - stubs/Symfony/Component/Form/DataTransformerInterface.stub - stubs/Symfony/Component/Form/FormBuilderInterface.stub - stubs/Symfony/Component/Form/FormConfigBuilderInterface.stub - stubs/Symfony/Component/Form/FormConfigInterface.stub @@ -42,16 +26,8 @@ parameters: - stubs/Symfony/Component/Form/FormTypeExtensionInterface.stub - stubs/Symfony/Component/Form/FormTypeInterface.stub - stubs/Symfony/Component/Form/FormView.stub - - stubs/Symfony/Component/HttpFoundation/Cookie.stub - - stubs/Symfony/Component/HttpFoundation/HeaderBag.stub - - stubs/Symfony/Component/HttpFoundation/ParameterBag.stub - - stubs/Symfony/Component/HttpFoundation/Session.stub - - stubs/Symfony/Component/Messenger/StampInterface.stub - - stubs/Symfony/Component/Messenger/Envelope.stub - stubs/Symfony/Component/OptionsResolver/Exception/InvalidOptionsException.stub - stubs/Symfony/Component/OptionsResolver/Options.stub - - stubs/Symfony/Component/Process/Exception/LogicException.stub - - stubs/Symfony/Component/Process/Process.stub - stubs/Symfony/Component/PropertyAccess/Exception/AccessException.stub - stubs/Symfony/Component/PropertyAccess/Exception/ExceptionInterface.stub - stubs/Symfony/Component/PropertyAccess/Exception/InvalidArgumentException.stub @@ -60,12 +36,8 @@ parameters: - stubs/Symfony/Component/PropertyAccess/PropertyAccessor.stub - stubs/Symfony/Component/PropertyAccess/PropertyAccessorInterface.stub - stubs/Symfony/Component/PropertyAccess/PropertyPathInterface.stub - - stubs/Symfony/Component/Security/Acl/Model/AclInterface.stub - - stubs/Symfony/Component/Security/Acl/Model/EntryInterface.stub - stubs/Symfony/Component/Security/Core/Authentication/Token/TokenInterface.stub - - stubs/Symfony/Component/Security/Core/Authorization/Voter/Voter.stub - stubs/Symfony/Component/Security/Core/Authorization/Voter/VoterInterface.stub - - stubs/Symfony/Component/Serializer/Encoder/ContextAwareDecoderInterface.stub - stubs/Symfony/Component/Serializer/Encoder/DecoderInterface.stub - stubs/Symfony/Component/Serializer/Encoder/EncoderInterface.stub - stubs/Symfony/Component/Serializer/Exception/BadMethodCallException.stub @@ -76,22 +48,9 @@ parameters: - stubs/Symfony/Component/Serializer/Exception/LogicException.stub - stubs/Symfony/Component/Serializer/Exception/RuntimeException.stub - stubs/Symfony/Component/Serializer/Exception/UnexpectedValueException.stub - - stubs/Symfony/Component/Serializer/Normalizer/ContextAwareDenormalizerInterface.stub - - stubs/Symfony/Component/Serializer/Normalizer/ContextAwareNormalizerInterface.stub - stubs/Symfony/Component/Serializer/Normalizer/DenormalizableInterface.stub - - stubs/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.stub - stubs/Symfony/Component/Serializer/Normalizer/NormalizableInterface.stub - - stubs/Symfony/Component/Serializer/Normalizer/NormalizerInterface.stub - - stubs/Symfony/Component/Validator/Constraint.stub - - stubs/Symfony/Component/Validator/Constraints/Composite.stub - - stubs/Symfony/Component/Validator/Constraints/Compound.stub - - stubs/Symfony/Component/Validator/ConstraintViolationInterface.stub - - stubs/Symfony/Component/Validator/ConstraintViolationListInterface.stub - - stubs/Symfony/Contracts/Cache/CacheInterface.stub - - stubs/Symfony/Contracts/Cache/CallbackInterface.stub - - stubs/Symfony/Contracts/Cache/ItemInterface.stub - stubs/Symfony/Contracts/Service/ServiceSubscriberInterface.stub - - stubs/Twig/Node/Node.stub parametersSchema: symfony: structure([ @@ -135,6 +94,12 @@ services: - factory: @symfony.messageMapFactory::create() + # stub loader + - + class: PHPStan\Stubs\Symfony\StubFilesExtensionLoader + tags: + - phpstan.stubFilesExtension + # ControllerTrait::get()/has() return type - factory: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Symfony\Component\DependencyInjection\ContainerInterface, %symfony.constantHassers%) @@ -329,10 +294,6 @@ services: - factory: PHPStan\Type\Symfony\ParameterDynamicReturnTypeExtension(Symfony\Bundle\FrameworkBundle\Controller\Controller, 'getParameter', null, %symfony.constantHassers%) tags: [phpstan.broker.dynamicMethodReturnTypeExtension] - - - class: PHPStan\Symfony\InputBagStubFilesExtension - tags: - - phpstan.stubFilesExtension - class: PHPStan\Symfony\SymfonyDiagnoseExtension tags: diff --git a/src/Stubs/Symfony/StubFilesExtensionLoader.php b/src/Stubs/Symfony/StubFilesExtensionLoader.php new file mode 100644 index 00000000..590a9b1a --- /dev/null +++ b/src/Stubs/Symfony/StubFilesExtensionLoader.php @@ -0,0 +1,155 @@ +reflector = $reflector; + } + + public function getFiles(): array + { + $stubsDir = dirname(dirname(dirname(__DIR__))) . '/stubs'; + $files = []; + + if ($this->isInstalledVersionBelow('symfony/security-bundle', '6.3.0.0')) { + $files[] = $stubsDir . '/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AuthenticatorFactoryInterface.stub'; + $files[] = $stubsDir . '/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/FirewallListenerFactoryInterface.stub'; + } + + if ($this->isInstalledVersionBelow('symfony/console', '6.2.0.0')) { + $files[] = $stubsDir . '/Symfony/Component/Console/Command.stub'; + $files[] = $stubsDir . '/Symfony/Component/Console/Exception/ExceptionInterface.stub'; + $files[] = $stubsDir . '/Symfony/Component/Console/Exception/InvalidArgumentException.stub'; + $files[] = $stubsDir . '/Symfony/Component/Console/Exception/LogicException.stub'; + $files[] = $stubsDir . '/Symfony/Component/Console/Helper/HelperInterface.stub'; + } + + if ($this->isInstalledVersionBelow('symfony/dependency-injection', '6.2.0.0')) { + $files[] = $stubsDir . '/Symfony/Component/DependencyInjection/ContainerBuilder.stub'; + $files[] = $stubsDir . '/Symfony/Component/DependencyInjection/Extension/ExtensionInterface.stub'; + } + + if ($this->isInstalledVersionBelow('symfony/event-dispatcher-contracts', '3.1.0.0')) { + $files[] = $stubsDir . '/Symfony/Component/EventDispatcher/EventDispatcherInterface.stub'; + } + + if ($this->isInstalledVersionBelow('symfony/event-dispatcher', '5.4.0.0')) { + $files[] = $stubsDir . '/Symfony/Component/EventDispatcher/EventSubscriberInterface.stub'; + $files[] = $stubsDir . '/Symfony/Component/EventDispatcher/GenericEvent.stub'; + } + + if ($this->isInstalledVersionBelow('symfony/form', '6.2.0.0')) { + $files[] = $stubsDir . '/Symfony/Component/Form/DataTransformerInterface.stub'; + } + + if ($this->isInstalledVersionBelow('symfony/http-foundation', '5.4.0.0')) { + $files[] = $stubsDir . '/Symfony/Component/HttpFoundation/HeaderBag.stub'; + $files[] = $stubsDir . '/Symfony/Component/HttpFoundation/Session.stub'; + } + + if ($this->isInstalledVersionBelow('symfony/http-foundation', '6.2.0.0')) { + $files[] = $stubsDir . '/Symfony/Component/HttpFoundation/Cookie.stub'; + } + + if ($this->isInstalledVersionBelow('symfony/http-foundation', '7.4.0.0')) { + $files[] = $stubsDir . '/Symfony/Component/HttpFoundation/ParameterBag.stub'; + + try { + $this->reflector->reflectClass('Symfony\Component\HttpFoundation\InputBag'); + $files[] = $stubsDir . '/Symfony/Component/HttpFoundation/InputBag.stub'; + $files[] = $stubsDir . '/Symfony/Component/HttpFoundation/Request.stub'; + } catch (IdentifierNotFound $e) { + // Don't load the InputBag and the associated Request stubs for older Symfony versions that did not have InputBag yet to avoid breaking the Request. + } + } + + if ($this->isInstalledVersionBelow('symfony/messenger', '6.1.0.0')) { + $files[] = $stubsDir . '/Symfony/Component/Messenger/Envelope.stub'; + $files[] = $stubsDir . '/Symfony/Component/Messenger/StampInterface.stub'; + } + + if ($this->isInstalledVersionBelow('symfony/process', '5.4.0.0')) { + $files[] = $stubsDir . '/Symfony/Component/Process/Exception/LogicException.stub'; + $files[] = $stubsDir . '/Symfony/Component/Process/Process.stub'; + } + + if ($this->isInstalledVersionBelow('symfony/security-acl', '3.2.0.0')) { + $files[] = $stubsDir . '/Symfony/Component/Security/Acl/Model/AclInterface.stub'; + $files[] = $stubsDir . '/Symfony/Component/Security/Acl/Model/EntryInterface.stub'; + } + + if ($this->isInstalledVersionBelow('symfony/security-core', '6.3.0.0')) { + $files[] = $stubsDir . '/Symfony/Component/Security/Core/Authorization/Voter/Voter.stub'; + } + + if ($this->isInstalledVersionBelow('symfony/serializer', '7.0.0.0')) { + $files[] = $stubsDir . '/Symfony/Component/Serializer/Encoder/ContextAwareDecoderInterface.stub'; + $files[] = $stubsDir . '/Symfony/Component/Serializer/Normalizer/ContextAwareDenormalizerInterface.stub'; + $files[] = $stubsDir . '/Symfony/Component/Serializer/Normalizer/ContextAwareNormalizerInterface.stub'; + } + + if ($this->isInstalledVersionBelow('symfony/serializer', '7.4.0.0')) { + $files[] = $stubsDir . '/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.stub'; + $files[] = $stubsDir . '/Symfony/Component/Serializer/Normalizer/NormalizerInterface.stub'; + } + + if ($this->isInstalledVersionBelow('symfony/validator', '5.4.0.0')) { + $files[] = $stubsDir . '/Symfony/Component/Validator/ConstraintViolationInterface.stub'; + $files[] = $stubsDir . '/Symfony/Component/Validator/ConstraintViolationListInterface.stub'; + } + + if ($this->isInstalledVersionBelow('symfony/validator', '7.1.0.0')) { + $files[] = $stubsDir . '/Symfony/Component/Validator/Constraint.stub'; + $files[] = $stubsDir . '/Symfony/Component/Validator/Constraints/Composite.stub'; + $files[] = $stubsDir . '/Symfony/Component/Validator/Constraints/Compound.stub'; + } + + if ($this->isInstalledVersionBelow('symfony/cache-contracts', '3.2.1.0')) { + $files[] = $stubsDir . '/Symfony/Contracts/Cache/CacheInterface.stub'; + $files[] = $stubsDir . '/Symfony/Contracts/Cache/CallbackInterface.stub'; + $files[] = $stubsDir . '/Symfony/Contracts/Cache/ItemInterface.stub'; + $files[] = $stubsDir . '/Psr/Cache/CacheException.stub'; + $files[] = $stubsDir . '/Psr/Cache/CacheItemInterface.stub'; + $files[] = $stubsDir . '/Psr/Cache/InvalidArgumentException.stub'; + } + + if ($this->isInstalledVersionBelow('twig/twig', '3.15.0.0')) { + $files[] = $stubsDir . '/Twig/Node/Node.stub'; + } + + return $files; + } + + private function isInstalledVersionBelow(string $package, string $maxVersion): bool + { + if (!class_exists(InstalledVersions::class)) { + return false; + } + + try { + $installedVersion = InstalledVersions::getVersion($package); + } catch (OutOfBoundsException $e) { + return false; + } + + return $installedVersion !== null && version_compare($installedVersion, $maxVersion, '<'); + } + +} diff --git a/src/Symfony/InputBagStubFilesExtension.php b/src/Symfony/InputBagStubFilesExtension.php deleted file mode 100644 index 6ce36f4b..00000000 --- a/src/Symfony/InputBagStubFilesExtension.php +++ /dev/null @@ -1,35 +0,0 @@ -reflector = $reflector; - } - - public function getFiles(): array - { - try { - $this->reflector->reflectClass('Symfony\Component\HttpFoundation\InputBag'); - } catch (IdentifierNotFound $e) { - return []; - } - - return [ - __DIR__ . '/../../stubs/Symfony/Component/HttpFoundation/InputBag.stub', - __DIR__ . '/../../stubs/Symfony/Component/HttpFoundation/Request.stub', - ]; - } - -} From 02a7a8105f0d794bc1358c028f18421550ce54b5 Mon Sep 17 00:00:00 2001 From: Christophe Coevoet Date: Wed, 22 Apr 2026 10:55:32 +0200 Subject: [PATCH 2/2] Remove the usage of stubs for types shipping in Symfony 8.1 --- extension.neon | 24 ------------ .../Symfony/StubFilesExtensionLoader.php | 39 +++++++++++++++++++ 2 files changed, 39 insertions(+), 24 deletions(-) diff --git a/extension.neon b/extension.neon index 55e0b0a4..08f62b51 100644 --- a/extension.neon +++ b/extension.neon @@ -12,9 +12,7 @@ parameters: handleTraitWrappers: [] stubFiles: - stubs/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.stub - - stubs/Symfony/Component/Console/Output/OutputInterface.stub - stubs/Symfony/Component/Form/AbstractType.stub - - stubs/Symfony/Component/Form/ChoiceList/Loader/ChoiceLoaderInterface.stub - stubs/Symfony/Component/Form/Exception/ExceptionInterface.stub - stubs/Symfony/Component/Form/Exception/RuntimeException.stub - stubs/Symfony/Component/Form/Exception/TransformationFailedException.stub @@ -28,28 +26,6 @@ parameters: - stubs/Symfony/Component/Form/FormView.stub - stubs/Symfony/Component/OptionsResolver/Exception/InvalidOptionsException.stub - stubs/Symfony/Component/OptionsResolver/Options.stub - - stubs/Symfony/Component/PropertyAccess/Exception/AccessException.stub - - stubs/Symfony/Component/PropertyAccess/Exception/ExceptionInterface.stub - - stubs/Symfony/Component/PropertyAccess/Exception/InvalidArgumentException.stub - - stubs/Symfony/Component/PropertyAccess/Exception/RuntimeException.stub - - stubs/Symfony/Component/PropertyAccess/Exception/UnexpectedTypeException.stub - - stubs/Symfony/Component/PropertyAccess/PropertyAccessor.stub - - stubs/Symfony/Component/PropertyAccess/PropertyAccessorInterface.stub - - stubs/Symfony/Component/PropertyAccess/PropertyPathInterface.stub - - stubs/Symfony/Component/Security/Core/Authentication/Token/TokenInterface.stub - - stubs/Symfony/Component/Security/Core/Authorization/Voter/VoterInterface.stub - - stubs/Symfony/Component/Serializer/Encoder/DecoderInterface.stub - - stubs/Symfony/Component/Serializer/Encoder/EncoderInterface.stub - - stubs/Symfony/Component/Serializer/Exception/BadMethodCallException.stub - - stubs/Symfony/Component/Serializer/Exception/CircularReferenceException.stub - - stubs/Symfony/Component/Serializer/Exception/ExceptionInterface.stub - - stubs/Symfony/Component/Serializer/Exception/ExtraAttributesException.stub - - stubs/Symfony/Component/Serializer/Exception/InvalidArgumentException.stub - - stubs/Symfony/Component/Serializer/Exception/LogicException.stub - - stubs/Symfony/Component/Serializer/Exception/RuntimeException.stub - - stubs/Symfony/Component/Serializer/Exception/UnexpectedValueException.stub - - stubs/Symfony/Component/Serializer/Normalizer/DenormalizableInterface.stub - - stubs/Symfony/Component/Serializer/Normalizer/NormalizableInterface.stub - stubs/Symfony/Contracts/Service/ServiceSubscriberInterface.stub parametersSchema: diff --git a/src/Stubs/Symfony/StubFilesExtensionLoader.php b/src/Stubs/Symfony/StubFilesExtensionLoader.php index 590a9b1a..a9cec6a3 100644 --- a/src/Stubs/Symfony/StubFilesExtensionLoader.php +++ b/src/Stubs/Symfony/StubFilesExtensionLoader.php @@ -41,6 +41,10 @@ public function getFiles(): array $files[] = $stubsDir . '/Symfony/Component/Console/Helper/HelperInterface.stub'; } + if ($this->isInstalledVersionBelow('symfony/console', '8.1.0.0')) { + $files[] = $stubsDir . '/Symfony/Component/Console/Output/OutputInterface.stub'; + } + if ($this->isInstalledVersionBelow('symfony/dependency-injection', '6.2.0.0')) { $files[] = $stubsDir . '/Symfony/Component/DependencyInjection/ContainerBuilder.stub'; $files[] = $stubsDir . '/Symfony/Component/DependencyInjection/Extension/ExtensionInterface.stub'; @@ -59,6 +63,10 @@ public function getFiles(): array $files[] = $stubsDir . '/Symfony/Component/Form/DataTransformerInterface.stub'; } + if ($this->isInstalledVersionBelow('symfony/form', '8.1.0.0')) { + $files[] = $stubsDir . '/Symfony/Component/Form/ChoiceList/Loader/ChoiceLoaderInterface.stub'; + } + if ($this->isInstalledVersionBelow('symfony/http-foundation', '5.4.0.0')) { $files[] = $stubsDir . '/Symfony/Component/HttpFoundation/HeaderBag.stub'; $files[] = $stubsDir . '/Symfony/Component/HttpFoundation/Session.stub'; @@ -90,11 +98,27 @@ public function getFiles(): array $files[] = $stubsDir . '/Symfony/Component/Process/Process.stub'; } + if ($this->isInstalledVersionBelow('symfony/property-access', '8.1.0.0')) { + $files[] = $stubsDir . '/Symfony/Component/PropertyAccess/Exception/AccessException.stub'; + $files[] = $stubsDir . '/Symfony/Component/PropertyAccess/Exception/ExceptionInterface.stub'; + $files[] = $stubsDir . '/Symfony/Component/PropertyAccess/Exception/InvalidArgumentException.stub'; + $files[] = $stubsDir . '/Symfony/Component/PropertyAccess/Exception/RuntimeException.stub'; + $files[] = $stubsDir . '/Symfony/Component/PropertyAccess/Exception/UnexpectedTypeException.stub'; + $files[] = $stubsDir . '/Symfony/Component/PropertyAccess/PropertyAccessor.stub'; + $files[] = $stubsDir . '/Symfony/Component/PropertyAccess/PropertyAccessorInterface.stub'; + $files[] = $stubsDir . '/Symfony/Component/PropertyAccess/PropertyPathInterface.stub'; + } + if ($this->isInstalledVersionBelow('symfony/security-acl', '3.2.0.0')) { $files[] = $stubsDir . '/Symfony/Component/Security/Acl/Model/AclInterface.stub'; $files[] = $stubsDir . '/Symfony/Component/Security/Acl/Model/EntryInterface.stub'; } + if ($this->isInstalledVersionBelow('symfony/security-core', '8.1.0.0')) { + $files[] = $stubsDir . '/Symfony/Component/Security/Core/Authentication/Token/TokenInterface.stub'; + $files[] = $stubsDir . '/Symfony/Component/Security/Core/Authorization/Voter/VoterInterface.stub'; + } + if ($this->isInstalledVersionBelow('symfony/security-core', '6.3.0.0')) { $files[] = $stubsDir . '/Symfony/Component/Security/Core/Authorization/Voter/Voter.stub'; } @@ -110,6 +134,21 @@ public function getFiles(): array $files[] = $stubsDir . '/Symfony/Component/Serializer/Normalizer/NormalizerInterface.stub'; } + if ($this->isInstalledVersionBelow('symfony/serializer', '8.1.0.0')) { + $files[] = $stubsDir . '/Symfony/Component/Serializer/Encoder/DecoderInterface.stub'; + $files[] = $stubsDir . '/Symfony/Component/Serializer/Encoder/EncoderInterface.stub'; + $files[] = $stubsDir . '/Symfony/Component/Serializer/Exception/BadMethodCallException.stub'; + $files[] = $stubsDir . '/Symfony/Component/Serializer/Exception/CircularReferenceException.stub'; + $files[] = $stubsDir . '/Symfony/Component/Serializer/Exception/ExceptionInterface.stub'; + $files[] = $stubsDir . '/Symfony/Component/Serializer/Exception/ExtraAttributesException.stub'; + $files[] = $stubsDir . '/Symfony/Component/Serializer/Exception/InvalidArgumentException.stub'; + $files[] = $stubsDir . '/Symfony/Component/Serializer/Exception/LogicException.stub'; + $files[] = $stubsDir . '/Symfony/Component/Serializer/Exception/RuntimeException.stub'; + $files[] = $stubsDir . '/Symfony/Component/Serializer/Exception/UnexpectedValueException.stub'; + $files[] = $stubsDir . '/Symfony/Component/Serializer/Normalizer/DenormalizableInterface.stub'; + $files[] = $stubsDir . '/Symfony/Component/Serializer/Normalizer/NormalizableInterface.stub'; + } + if ($this->isInstalledVersionBelow('symfony/validator', '5.4.0.0')) { $files[] = $stubsDir . '/Symfony/Component/Validator/ConstraintViolationInterface.stub'; $files[] = $stubsDir . '/Symfony/Component/Validator/ConstraintViolationListInterface.stub';