diff --git a/extension.neon b/extension.neon index f8516ed0..08f62b51 100644 --- a/extension.neon +++ b/extension.neon @@ -11,29 +11,11 @@ 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,56 +24,9 @@ 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 - - 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/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 - - 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/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 +70,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 +270,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..a9cec6a3 --- /dev/null +++ b/src/Stubs/Symfony/StubFilesExtensionLoader.php @@ -0,0 +1,194 @@ +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/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'; + } + + 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/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'; + } + + 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/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'; + } + + 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/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'; + } + + 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', - ]; - } - -}