From c3ac708fc1860146e35760b16b8eb2a08dd25d62 Mon Sep 17 00:00:00 2001 From: Filip Ayazi Date: Thu, 10 Jun 2021 13:35:06 +0100 Subject: [PATCH 001/208] Allow boolean in Command->addOption default value --- src/Rules/Symfony/InvalidOptionDefaultValueRule.php | 8 ++------ tests/Rules/Symfony/ExampleCommand.php | 1 + tests/Rules/Symfony/UndefinedArgumentRuleTest.php | 2 +- tests/Rules/Symfony/UndefinedOptionRuleTest.php | 2 +- 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/Rules/Symfony/InvalidOptionDefaultValueRule.php b/src/Rules/Symfony/InvalidOptionDefaultValueRule.php index 42d40ee0..678feb35 100644 --- a/src/Rules/Symfony/InvalidOptionDefaultValueRule.php +++ b/src/Rules/Symfony/InvalidOptionDefaultValueRule.php @@ -8,14 +8,13 @@ use PHPStan\Rules\Rule; use PHPStan\ShouldNotHappenException; use PHPStan\Type\ArrayType; -use PHPStan\Type\Constant\ConstantBooleanType; +use PHPStan\Type\BooleanType; use PHPStan\Type\Constant\ConstantIntegerType; use PHPStan\Type\IntegerType; use PHPStan\Type\MixedType; use PHPStan\Type\NullType; use PHPStan\Type\ObjectType; use PHPStan\Type\StringType; -use PHPStan\Type\TypeCombinator; use PHPStan\Type\TypeUtils; use PHPStan\Type\UnionType; use PHPStan\Type\VerbosityLevel; @@ -70,10 +69,7 @@ public function processNode(Node $node, Scope $scope): array // not an array if (($mode & 8) !== 8) { - $checkType = new UnionType([new StringType(), new IntegerType(), new NullType()]); - if (($mode & 4) === 4) { // https://symfony.com/doc/current/console/input.html#options-with-optional-arguments - $checkType = TypeCombinator::union($checkType, new ConstantBooleanType(false)); - } + $checkType = new UnionType([new StringType(), new IntegerType(), new NullType(), new BooleanType()]); if (!$checkType->isSuperTypeOf($defaultType)->yes()) { return [sprintf('Parameter #5 $default of method Symfony\Component\Console\Command\Command::addOption() expects %s, %s given.', $checkType->describe(VerbosityLevel::typeOnly()), $defaultType->describe(VerbosityLevel::typeOnly()))]; } diff --git a/tests/Rules/Symfony/ExampleCommand.php b/tests/Rules/Symfony/ExampleCommand.php index c376d875..6dec4cbd 100644 --- a/tests/Rules/Symfony/ExampleCommand.php +++ b/tests/Rules/Symfony/ExampleCommand.php @@ -29,6 +29,7 @@ protected function configure(): void $this->addOption('b', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL, '', [1]); $this->addOption('c', null, InputOption::VALUE_OPTIONAL, '', 1); $this->addOption('d', null, InputOption::VALUE_OPTIONAL, '', false); + $this->addOption('f', null, InputOption::VALUE_REQUIRED, '', true); /** @var string[] $defaults */ $defaults = []; diff --git a/tests/Rules/Symfony/UndefinedArgumentRuleTest.php b/tests/Rules/Symfony/UndefinedArgumentRuleTest.php index 22fee153..ab4e1b05 100644 --- a/tests/Rules/Symfony/UndefinedArgumentRuleTest.php +++ b/tests/Rules/Symfony/UndefinedArgumentRuleTest.php @@ -38,7 +38,7 @@ public function testGetArgument(): void [ [ 'Command "example-rule" does not define argument "undefined".', - 41, + 42, ], ] ); diff --git a/tests/Rules/Symfony/UndefinedOptionRuleTest.php b/tests/Rules/Symfony/UndefinedOptionRuleTest.php index 1ca949de..5e81e401 100644 --- a/tests/Rules/Symfony/UndefinedOptionRuleTest.php +++ b/tests/Rules/Symfony/UndefinedOptionRuleTest.php @@ -38,7 +38,7 @@ public function testGetArgument(): void [ [ 'Command "example-rule" does not define option "bbb".', - 48, + 49, ], ] ); From 45e2ae29f6f53f5039b3d5c401dfc26724d8f5bb Mon Sep 17 00:00:00 2001 From: Michael Telgmann Date: Wed, 16 Jun 2021 11:59:23 +0200 Subject: [PATCH 002/208] Introduce stub for EventDispatcherInterface --- extension.neon | 1 + .../EventDispatcher/EventDispatcherInterface.stub | 14 ++++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 stubs/Symfony/Component/EventDispatcher/EventDispatcherInterface.stub diff --git a/extension.neon b/extension.neon index 544a4d89..c02cbbe7 100644 --- a/extension.neon +++ b/extension.neon @@ -12,6 +12,7 @@ parameters: - stubs/Symfony/Component/Form/ChoiceList/Loader/ChoiceLoaderInterface.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/FormBuilderInterface.stub diff --git a/stubs/Symfony/Component/EventDispatcher/EventDispatcherInterface.stub b/stubs/Symfony/Component/EventDispatcher/EventDispatcherInterface.stub new file mode 100644 index 00000000..e4ad8fc4 --- /dev/null +++ b/stubs/Symfony/Component/EventDispatcher/EventDispatcherInterface.stub @@ -0,0 +1,14 @@ + Date: Wed, 30 Jun 2021 15:12:49 +0200 Subject: [PATCH 003/208] Fix hasSession specifier --- .../RequestTypeSpecifyingExtension.php | 7 +++- .../ImpossibleCheckTypeMethodCallRuleTest.php | 32 +++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 tests/Type/Symfony/ImpossibleCheckTypeMethodCallRuleTest.php diff --git a/src/Type/Symfony/RequestTypeSpecifyingExtension.php b/src/Type/Symfony/RequestTypeSpecifyingExtension.php index 295d37e4..66372a7b 100644 --- a/src/Type/Symfony/RequestTypeSpecifyingExtension.php +++ b/src/Type/Symfony/RequestTypeSpecifyingExtension.php @@ -46,10 +46,15 @@ public function specifyTypes(MethodReflection $methodReflection, MethodCall $nod { $classReflection = $this->broker->getClass(self::REQUEST_CLASS); $methodVariants = $classReflection->getNativeMethod(self::GET_METHOD_NAME)->getVariants(); + $returnType = ParametersAcceptorSelector::selectSingle($methodVariants)->getReturnType(); + + if (!TypeCombinator::containsNull($returnType)) { + return new SpecifiedTypes(); + } return $this->typeSpecifier->create( new MethodCall($node->var, self::GET_METHOD_NAME), - TypeCombinator::removeNull(ParametersAcceptorSelector::selectSingle($methodVariants)->getReturnType()), + TypeCombinator::removeNull($returnType), $context ); } diff --git a/tests/Type/Symfony/ImpossibleCheckTypeMethodCallRuleTest.php b/tests/Type/Symfony/ImpossibleCheckTypeMethodCallRuleTest.php new file mode 100644 index 00000000..dba9cbfa --- /dev/null +++ b/tests/Type/Symfony/ImpossibleCheckTypeMethodCallRuleTest.php @@ -0,0 +1,32 @@ + + */ +class ImpossibleCheckTypeMethodCallRuleTest extends \PHPStan\Testing\RuleTestCase +{ + + protected function getRule(): Rule + { + return self::getContainer()->getByType(ImpossibleCheckTypeMethodCallRule::class); + } + + public function testExtension(): void + { + $this->analyse([__DIR__ . '/data/request_get_session.php'], []); + } + + public static function getAdditionalConfigFiles(): array + { + return [ + __DIR__ . '/../../../extension.neon', + __DIR__ . '/../../../vendor/phpstan/phpstan-strict-rules/rules.neon', + ]; + } + +} From 58b5959a11e26ce495b424ee634e721aaa9674d1 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Thu, 1 Jul 2021 09:33:28 +0200 Subject: [PATCH 004/208] Fixed createMarkerNode for long constant strings --- src/Type/Symfony/Helper.php | 2 +- .../ImpossibleCheckTypeMethodCallRuleTest.php | 5 +++++ tests/Type/Symfony/data/bug-178.php | 17 +++++++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 tests/Type/Symfony/data/bug-178.php diff --git a/src/Type/Symfony/Helper.php b/src/Type/Symfony/Helper.php index a39af310..099b64c2 100644 --- a/src/Type/Symfony/Helper.php +++ b/src/Type/Symfony/Helper.php @@ -17,7 +17,7 @@ public static function createMarkerNode(Expr $expr, Type $type, PrettyPrinterAbs return new Expr\Variable(md5(sprintf( '%s::%s', $printer->prettyPrintExpr($expr), - $type->describe(VerbosityLevel::value()) + $type->describe(VerbosityLevel::precise()) ))); } diff --git a/tests/Type/Symfony/ImpossibleCheckTypeMethodCallRuleTest.php b/tests/Type/Symfony/ImpossibleCheckTypeMethodCallRuleTest.php index dba9cbfa..ab247b9f 100644 --- a/tests/Type/Symfony/ImpossibleCheckTypeMethodCallRuleTest.php +++ b/tests/Type/Symfony/ImpossibleCheckTypeMethodCallRuleTest.php @@ -21,6 +21,11 @@ public function testExtension(): void $this->analyse([__DIR__ . '/data/request_get_session.php'], []); } + public function testBug178(): void + { + $this->analyse([__DIR__ . '/data/bug-178.php'], []); + } + public static function getAdditionalConfigFiles(): array { return [ diff --git a/tests/Type/Symfony/data/bug-178.php b/tests/Type/Symfony/data/bug-178.php new file mode 100644 index 00000000..9dbd5137 --- /dev/null +++ b/tests/Type/Symfony/data/bug-178.php @@ -0,0 +1,17 @@ +has('sonata.media.manager.category') && $this->has('sonata.media.manager.context')) { + // do stuff that requires both managers. + } + } + +} From 41fff98845bb8c4c0dc6c56f82b75358e57ccef9 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Thu, 8 Jul 2021 14:43:33 +0200 Subject: [PATCH 005/208] Fix AclProviderInterface and relevant classes because of the overriding method error --- extension.neon | 2 ++ .../Component/Security/Acl/Dbal/AclProvider.stub | 14 ++++++++++++++ .../Security/Acl/Dbal/MutableAclProvider.stub | 14 ++++++++++++++ .../Security/Acl/Model/AclProviderInterface.stub | 7 +++++-- .../Acl/Model/MutableAclProviderInterface.stub | 8 ++++++-- 5 files changed, 41 insertions(+), 4 deletions(-) create mode 100644 stubs/Symfony/Component/Security/Acl/Dbal/AclProvider.stub create mode 100644 stubs/Symfony/Component/Security/Acl/Dbal/MutableAclProvider.stub diff --git a/extension.neon b/extension.neon index c02cbbe7..72cefce6 100644 --- a/extension.neon +++ b/extension.neon @@ -26,6 +26,8 @@ parameters: - stubs/Symfony/Component/HttpFoundation/Session.stub - stubs/Symfony/Component/Process/Process.stub - stubs/Symfony/Component/PropertyAccess/PropertyPathInterface.stub + - stubs/Symfony/Component/Security/Acl/Dbal/AclProvider.stub + - stubs/Symfony/Component/Security/Acl/Dbal/MutableAclProvider.stub - stubs/Symfony/Component/Security/Acl/Model/AclInterface.stub - stubs/Symfony/Component/Security/Acl/Model/AclProviderInterface.stub - stubs/Symfony/Component/Security/Acl/Model/EntryInterface.stub diff --git a/stubs/Symfony/Component/Security/Acl/Dbal/AclProvider.stub b/stubs/Symfony/Component/Security/Acl/Dbal/AclProvider.stub new file mode 100644 index 00000000..cc0a37c3 --- /dev/null +++ b/stubs/Symfony/Component/Security/Acl/Dbal/AclProvider.stub @@ -0,0 +1,14 @@ + + */ +class AclProvider implements AclProviderInterface +{ + +} diff --git a/stubs/Symfony/Component/Security/Acl/Dbal/MutableAclProvider.stub b/stubs/Symfony/Component/Security/Acl/Dbal/MutableAclProvider.stub new file mode 100644 index 00000000..e8568734 --- /dev/null +++ b/stubs/Symfony/Component/Security/Acl/Dbal/MutableAclProvider.stub @@ -0,0 +1,14 @@ + + */ +class MutableAclProvider implements MutableAclProviderInterface +{ + +} diff --git a/stubs/Symfony/Component/Security/Acl/Model/AclProviderInterface.stub b/stubs/Symfony/Component/Security/Acl/Model/AclProviderInterface.stub index 97cb6880..5c0ad94c 100644 --- a/stubs/Symfony/Component/Security/Acl/Model/AclProviderInterface.stub +++ b/stubs/Symfony/Component/Security/Acl/Model/AclProviderInterface.stub @@ -2,18 +2,21 @@ namespace Symfony\Component\Security\Acl\Model; +/** + * @template TAcl of AclInterface + */ interface AclProviderInterface { /** * @phpstan-param array $sids - * @phpstan-return AclInterface + * @phpstan-return TAcl */ public function findAcl(ObjectIdentityInterface $oid, array $sids = []); /** * @phpstan-param array $oids * @phpstan-param array $sids - * @phpstan-return \SplObjectStorage + * @phpstan-return \SplObjectStorage */ public function findAcls(array $oids, array $sids = []); } diff --git a/stubs/Symfony/Component/Security/Acl/Model/MutableAclProviderInterface.stub b/stubs/Symfony/Component/Security/Acl/Model/MutableAclProviderInterface.stub index 4f25b6c5..0a8bd147 100644 --- a/stubs/Symfony/Component/Security/Acl/Model/MutableAclProviderInterface.stub +++ b/stubs/Symfony/Component/Security/Acl/Model/MutableAclProviderInterface.stub @@ -2,18 +2,22 @@ namespace Symfony\Component\Security\Acl\Model; +/** + * @template TAcl of MutableAclInterface + * @extends AclProviderInterface + */ interface MutableAclProviderInterface extends AclProviderInterface { /** * @phpstan-param array $sids - * @phpstan-return MutableAclInterface + * @phpstan-return TAcl */ public function findAcl(ObjectIdentityInterface $oid, array $sids = []); /** * @phpstan-param array $oids * @phpstan-param array $sids - * @phpstan-return \SplObjectStorage + * @phpstan-return \SplObjectStorage */ public function findAcls(array $oids, array $sids = []); } From 21e6ec58dcfdb684d599776cbf706774b9106c38 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Thu, 15 Jul 2021 13:28:37 +0200 Subject: [PATCH 006/208] Removed Security stubs, they're no longer needed --- extension.neon | 9 ----- .../Security/Acl/Dbal/AclProvider.stub | 14 ------- .../Security/Acl/Dbal/MutableAclProvider.stub | 14 ------- .../Security/Acl/Model/AclInterface.stub | 40 ------------------- .../Acl/Model/AclProviderInterface.stub | 22 ---------- .../Security/Acl/Model/EntryInterface.stub | 7 ---- .../Acl/Model/MutableAclInterface.stub | 8 ---- .../Model/MutableAclProviderInterface.stub | 23 ----------- .../Acl/Model/ObjectIdentityInterface.stub | 8 ---- .../Acl/Model/SecurityIdentityInterface.stub | 8 ---- 10 files changed, 153 deletions(-) delete mode 100644 stubs/Symfony/Component/Security/Acl/Dbal/AclProvider.stub delete mode 100644 stubs/Symfony/Component/Security/Acl/Dbal/MutableAclProvider.stub delete mode 100644 stubs/Symfony/Component/Security/Acl/Model/AclInterface.stub delete mode 100644 stubs/Symfony/Component/Security/Acl/Model/AclProviderInterface.stub delete mode 100644 stubs/Symfony/Component/Security/Acl/Model/EntryInterface.stub delete mode 100644 stubs/Symfony/Component/Security/Acl/Model/MutableAclInterface.stub delete mode 100644 stubs/Symfony/Component/Security/Acl/Model/MutableAclProviderInterface.stub delete mode 100644 stubs/Symfony/Component/Security/Acl/Model/ObjectIdentityInterface.stub delete mode 100644 stubs/Symfony/Component/Security/Acl/Model/SecurityIdentityInterface.stub diff --git a/extension.neon b/extension.neon index 72cefce6..f890042a 100644 --- a/extension.neon +++ b/extension.neon @@ -26,15 +26,6 @@ parameters: - stubs/Symfony/Component/HttpFoundation/Session.stub - stubs/Symfony/Component/Process/Process.stub - stubs/Symfony/Component/PropertyAccess/PropertyPathInterface.stub - - stubs/Symfony/Component/Security/Acl/Dbal/AclProvider.stub - - stubs/Symfony/Component/Security/Acl/Dbal/MutableAclProvider.stub - - stubs/Symfony/Component/Security/Acl/Model/AclInterface.stub - - stubs/Symfony/Component/Security/Acl/Model/AclProviderInterface.stub - - stubs/Symfony/Component/Security/Acl/Model/EntryInterface.stub - - stubs/Symfony/Component/Security/Acl/Model/MutableAclInterface.stub - - stubs/Symfony/Component/Security/Acl/Model/MutableAclProviderInterface.stub - - stubs/Symfony/Component/Security/Acl/Model/ObjectIdentityInterface.stub - - stubs/Symfony/Component/Security/Acl/Model/SecurityIdentityInterface.stub - stubs/Symfony/Component/Serializer/Encoder/ContextAwareDecoderInterface.stub - stubs/Symfony/Component/Serializer/Encoder/DecoderInterface.stub - stubs/Symfony/Component/Serializer/Encoder/EncoderInterface.stub diff --git a/stubs/Symfony/Component/Security/Acl/Dbal/AclProvider.stub b/stubs/Symfony/Component/Security/Acl/Dbal/AclProvider.stub deleted file mode 100644 index cc0a37c3..00000000 --- a/stubs/Symfony/Component/Security/Acl/Dbal/AclProvider.stub +++ /dev/null @@ -1,14 +0,0 @@ - - */ -class AclProvider implements AclProviderInterface -{ - -} diff --git a/stubs/Symfony/Component/Security/Acl/Dbal/MutableAclProvider.stub b/stubs/Symfony/Component/Security/Acl/Dbal/MutableAclProvider.stub deleted file mode 100644 index e8568734..00000000 --- a/stubs/Symfony/Component/Security/Acl/Dbal/MutableAclProvider.stub +++ /dev/null @@ -1,14 +0,0 @@ - - */ -class MutableAclProvider implements MutableAclProviderInterface -{ - -} diff --git a/stubs/Symfony/Component/Security/Acl/Model/AclInterface.stub b/stubs/Symfony/Component/Security/Acl/Model/AclInterface.stub deleted file mode 100644 index 2f509501..00000000 --- a/stubs/Symfony/Component/Security/Acl/Model/AclInterface.stub +++ /dev/null @@ -1,40 +0,0 @@ - - */ - public function getClassAces(); - - /** - * Returns all class-field-based ACEs associated with this ACL. - * - * @param string $field - * - * @return array - */ - public function getClassFieldAces($field); - - /** - * Returns all object-based ACEs associated with this ACL. - * - * @return array - */ - public function getObjectAces(); - - /** - * Returns all object-field-based ACEs associated with this ACL. - * - * @param string $field - * - * @return array - */ - public function getObjectFieldAces($field); - -} diff --git a/stubs/Symfony/Component/Security/Acl/Model/AclProviderInterface.stub b/stubs/Symfony/Component/Security/Acl/Model/AclProviderInterface.stub deleted file mode 100644 index 5c0ad94c..00000000 --- a/stubs/Symfony/Component/Security/Acl/Model/AclProviderInterface.stub +++ /dev/null @@ -1,22 +0,0 @@ - $sids - * @phpstan-return TAcl - */ - public function findAcl(ObjectIdentityInterface $oid, array $sids = []); - - /** - * @phpstan-param array $oids - * @phpstan-param array $sids - * @phpstan-return \SplObjectStorage - */ - public function findAcls(array $oids, array $sids = []); -} diff --git a/stubs/Symfony/Component/Security/Acl/Model/EntryInterface.stub b/stubs/Symfony/Component/Security/Acl/Model/EntryInterface.stub deleted file mode 100644 index 335e581d..00000000 --- a/stubs/Symfony/Component/Security/Acl/Model/EntryInterface.stub +++ /dev/null @@ -1,7 +0,0 @@ - - */ -interface MutableAclProviderInterface extends AclProviderInterface -{ - /** - * @phpstan-param array $sids - * @phpstan-return TAcl - */ - public function findAcl(ObjectIdentityInterface $oid, array $sids = []); - - /** - * @phpstan-param array $oids - * @phpstan-param array $sids - * @phpstan-return \SplObjectStorage - */ - public function findAcls(array $oids, array $sids = []); -} diff --git a/stubs/Symfony/Component/Security/Acl/Model/ObjectIdentityInterface.stub b/stubs/Symfony/Component/Security/Acl/Model/ObjectIdentityInterface.stub deleted file mode 100644 index 8c03dabc..00000000 --- a/stubs/Symfony/Component/Security/Acl/Model/ObjectIdentityInterface.stub +++ /dev/null @@ -1,8 +0,0 @@ - Date: Fri, 16 Jul 2021 14:24:20 +0200 Subject: [PATCH 007/208] Add back AclInterface stub --- extension.neon | 2 + .../Security/Acl/Model/AclInterface.stub | 40 +++++++++++++++++++ .../Security/Acl/Model/EntryInterface.stub | 7 ++++ 3 files changed, 49 insertions(+) create mode 100644 stubs/Symfony/Component/Security/Acl/Model/AclInterface.stub create mode 100644 stubs/Symfony/Component/Security/Acl/Model/EntryInterface.stub diff --git a/extension.neon b/extension.neon index f890042a..a3e1b1dd 100644 --- a/extension.neon +++ b/extension.neon @@ -26,6 +26,8 @@ parameters: - stubs/Symfony/Component/HttpFoundation/Session.stub - stubs/Symfony/Component/Process/Process.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/Serializer/Encoder/ContextAwareDecoderInterface.stub - stubs/Symfony/Component/Serializer/Encoder/DecoderInterface.stub - stubs/Symfony/Component/Serializer/Encoder/EncoderInterface.stub diff --git a/stubs/Symfony/Component/Security/Acl/Model/AclInterface.stub b/stubs/Symfony/Component/Security/Acl/Model/AclInterface.stub new file mode 100644 index 00000000..2f509501 --- /dev/null +++ b/stubs/Symfony/Component/Security/Acl/Model/AclInterface.stub @@ -0,0 +1,40 @@ + + */ + public function getClassAces(); + + /** + * Returns all class-field-based ACEs associated with this ACL. + * + * @param string $field + * + * @return array + */ + public function getClassFieldAces($field); + + /** + * Returns all object-based ACEs associated with this ACL. + * + * @return array + */ + public function getObjectAces(); + + /** + * Returns all object-field-based ACEs associated with this ACL. + * + * @param string $field + * + * @return array + */ + public function getObjectFieldAces($field); + +} diff --git a/stubs/Symfony/Component/Security/Acl/Model/EntryInterface.stub b/stubs/Symfony/Component/Security/Acl/Model/EntryInterface.stub new file mode 100644 index 00000000..335e581d --- /dev/null +++ b/stubs/Symfony/Component/Security/Acl/Model/EntryInterface.stub @@ -0,0 +1,7 @@ + Date: Sun, 18 Jul 2021 09:21:50 +0200 Subject: [PATCH 008/208] Add Lazy Command Support --- src/Symfony/ConsoleApplicationResolver.php | 10 +++- tests/Type/Symfony/ExtensionTest.php | 1 + .../Symfony/console_application_loader.php | 11 +++++ .../Symfony/data/ExampleOptionLazyCommand.php | 47 +++++++++++++++++++ 4 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 tests/Type/Symfony/data/ExampleOptionLazyCommand.php diff --git a/src/Symfony/ConsoleApplicationResolver.php b/src/Symfony/ConsoleApplicationResolver.php index 88ee908f..cc4ece4e 100644 --- a/src/Symfony/ConsoleApplicationResolver.php +++ b/src/Symfony/ConsoleApplicationResolver.php @@ -54,9 +54,17 @@ public function findCommands(ClassReflection $classReflection): array $commands = []; foreach ($this->consoleApplication->all() as $name => $command) { - if (!$classType->isSuperTypeOf(new ObjectType(get_class($command)))->yes()) { + $commandClass = new ObjectType(get_class($command)); + $isLazyCommand = (new ObjectType('Symfony\Component\Console\Command\LazyCommand'))->isSuperTypeOf($commandClass)->yes(); + + if ($isLazyCommand && method_exists($command, 'getCommand') && !$classType->isSuperTypeOf(new ObjectType(get_class($command->getCommand())))->yes()) { continue; } + + if (!$isLazyCommand && !$classType->isSuperTypeOf($commandClass)->yes()) { + continue; + } + $commands[$name] = $command; } diff --git a/tests/Type/Symfony/ExtensionTest.php b/tests/Type/Symfony/ExtensionTest.php index a2de90ec..8ee9aced 100644 --- a/tests/Type/Symfony/ExtensionTest.php +++ b/tests/Type/Symfony/ExtensionTest.php @@ -22,6 +22,7 @@ public function dataFileAsserts(): iterable yield from $this->gatherAssertTypes(__DIR__ . '/data/tree_builder.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/ExampleBaseCommand.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/ExampleOptionCommand.php'); + yield from $this->gatherAssertTypes(__DIR__ . '/data/ExampleOptionLazyCommand.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/kernel_interface.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/request_get_content.php'); diff --git a/tests/Type/Symfony/console_application_loader.php b/tests/Type/Symfony/console_application_loader.php index 524bc159..0ef2e0ae 100644 --- a/tests/Type/Symfony/console_application_loader.php +++ b/tests/Type/Symfony/console_application_loader.php @@ -3,7 +3,9 @@ use PHPStan\Type\Symfony\ExampleACommand; use PHPStan\Type\Symfony\ExampleBCommand; use PHPStan\Type\Symfony\ExampleOptionCommand; +use PHPStan\Type\Symfony\ExampleOptionLazyCommand; use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\LazyCommand; require_once __DIR__ . '/../../../vendor/autoload.php'; @@ -11,4 +13,13 @@ $application->add(new ExampleACommand()); $application->add(new ExampleBCommand()); $application->add(new ExampleOptionCommand()); + +if (class_exists(LazyCommand::class)) { + $application->add(new LazyCommand('lazy-example-option', [], '', false, function () { + return new ExampleOptionLazyCommand(); + })); +} else { + $application->add(new ExampleOptionLazyCommand()); +} + return $application; diff --git a/tests/Type/Symfony/data/ExampleOptionLazyCommand.php b/tests/Type/Symfony/data/ExampleOptionLazyCommand.php new file mode 100644 index 00000000..7933cd41 --- /dev/null +++ b/tests/Type/Symfony/data/ExampleOptionLazyCommand.php @@ -0,0 +1,47 @@ +addOption('a', null, InputOption::VALUE_NONE); + $this->addOption('b', null, InputOption::VALUE_OPTIONAL); + $this->addOption('c', null, InputOption::VALUE_REQUIRED); + $this->addOption('d', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL); + $this->addOption('e', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED); + + $this->addOption('bb', null, InputOption::VALUE_OPTIONAL, '', 1); + $this->addOption('cc', null, InputOption::VALUE_REQUIRED, '', 1); + $this->addOption('dd', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL, '', [1]); + $this->addOption('ee', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, '', [1]); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + assertType('bool', $input->getOption('a')); + assertType('string|null', $input->getOption('b')); + assertType('string|null', $input->getOption('c')); + assertType('array', $input->getOption('d')); + assertType('array', $input->getOption('e')); + + assertType('1|string|null', $input->getOption('bb')); + assertType('1|string', $input->getOption('cc')); + assertType('array', $input->getOption('dd')); + assertType('array', $input->getOption('ee')); + } + +} From fe5f5d8db30e1d25a7e874497902bd900d6229ab Mon Sep 17 00:00:00 2001 From: Thomas Landauer Date: Thu, 12 Aug 2021 15:25:38 +0200 Subject: [PATCH 009/208] Adding `scanDirectories` to README for Symfony 5.3+ --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index b853b3fa..ef670e6b 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,9 @@ parameters: container_xml_path: var/cache/dev/srcApp_KernelDevDebugContainer.xml # or with Symfony 5+ container_xml_path: var/cache/dev/App_KernelDevDebugContainer.xml + # If you're using PHP config files for Symfony 5.3+, you also need this for auto-loading of `Symfony\Config`: + scanDirectories: + - var/cache/dev/Symfony/Config ``` ## Constant hassers From 2c240808116be56c7129d9d48b8dd40d1344e0eb Mon Sep 17 00:00:00 2001 From: Alexey Sakhnov Date: Tue, 17 Aug 2021 20:54:08 +0300 Subject: [PATCH 010/208] Ignore ContainerBagInterface in ContainerInterfaceUnknownServiceRule --- .../ContainerInterfaceUnknownServiceRule.php | 5 +++++ .../ContainerInterfaceUnknownServiceRuleTest.php | 14 ++++++++++++++ tests/Rules/Symfony/ExampleServiceSubscriber.php | 7 +++++++ 3 files changed, 26 insertions(+) diff --git a/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php b/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php index 2719bb4e..eacc2071 100644 --- a/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php +++ b/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php @@ -55,6 +55,11 @@ public function processNode(Node $node, Scope $scope): array } $argType = $scope->getType($node->var); + $isContainerBagType = (new ObjectType('Symfony\Component\DependencyInjection\ParameterBag\ContainerBagInterface'))->isSuperTypeOf($argType); + if ($isContainerBagType->yes()) { + return []; + } + $isControllerType = (new ObjectType('Symfony\Bundle\FrameworkBundle\Controller\Controller'))->isSuperTypeOf($argType); $isAbstractControllerType = (new ObjectType('Symfony\Bundle\FrameworkBundle\Controller\AbstractController'))->isSuperTypeOf($argType); $isContainerType = (new ObjectType('Symfony\Component\DependencyInjection\ContainerInterface'))->isSuperTypeOf($argType); diff --git a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php index 306e219a..4f4fc092 100644 --- a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php +++ b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php @@ -68,4 +68,18 @@ public function testGetPrivateServiceInAbstractController(): void ); } + public function testGetPrivateServiceInLegacyServiceSubscriber(): void + { + if (!interface_exists('Symfony\Contracts\Service\ServiceSubscriberInterface')) { + self::markTestSkipped('The test needs Symfony\Contracts\Service\ServiceSubscriberInterface class.'); + } + + $this->analyse( + [ + __DIR__ . '/ExampleServiceSubscriber.php', + ], + [] + ); + } + } diff --git a/tests/Rules/Symfony/ExampleServiceSubscriber.php b/tests/Rules/Symfony/ExampleServiceSubscriber.php index c9a009d2..9f5968df 100644 --- a/tests/Rules/Symfony/ExampleServiceSubscriber.php +++ b/tests/Rules/Symfony/ExampleServiceSubscriber.php @@ -12,6 +12,13 @@ public function privateService(): void $this->get('private'); } + public function containerParameter(): void + { + /** @var \Symfony\Component\DependencyInjection\ParameterBag\ContainerBag $containerBag */ + $containerBag = doFoo(); + $containerBag->get('parameter_name'); + } + /** * @return string[] */ From 95d9ae78605e5023e0edcf905c9b42da2a2a8359 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Sun, 22 Aug 2021 12:58:31 +0200 Subject: [PATCH 011/208] Make InputBag generic --- composer.json | 2 +- extension.neon | 5 ++++ .../Component/HttpFoundation/InputBag.stub | 20 +++++++++++++ .../Component/HttpFoundation/Request.stub | 29 +++++++++++++++++++ tests/Type/Symfony/ExtensionTest.php | 1 + .../Symfony/data/input_bag_from_request.php | 22 ++++++++++++++ 6 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 stubs/Symfony/Component/HttpFoundation/InputBag.stub create mode 100644 stubs/Symfony/Component/HttpFoundation/Request.stub create mode 100644 tests/Type/Symfony/data/input_bag_from_request.php diff --git a/composer.json b/composer.json index e9d076d1..f895d38e 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ "require": { "php": "^7.1 || ^8.0", "ext-simplexml": "*", - "phpstan/phpstan": "^0.12.86" + "phpstan/phpstan": "^0.12.97" }, "conflict": { "symfony/framework-bundle": "<3.0" diff --git a/extension.neon b/extension.neon index a3e1b1dd..2dee6a69 100644 --- a/extension.neon +++ b/extension.neon @@ -4,6 +4,9 @@ parameters: exceptions: uncheckedExceptionClasses: - 'Symfony\Component\Console\Exception\InvalidArgumentException' + featureToggles: + skipCheckGenericClasses: + - Symfony\Component\HttpFoundation\InputBag symfony: container_xml_path: null constant_hassers: true @@ -22,7 +25,9 @@ parameters: - stubs/Symfony/Component/Form/FormView.stub - stubs/Symfony/Component/HttpFoundation/Cookie.stub - stubs/Symfony/Component/HttpFoundation/HeaderBag.stub + - stubs/Symfony/Component/HttpFoundation/InputBag.stub - stubs/Symfony/Component/HttpFoundation/ParameterBag.stub + - stubs/Symfony/Component/HttpFoundation/Request.stub - stubs/Symfony/Component/HttpFoundation/Session.stub - stubs/Symfony/Component/Process/Process.stub - stubs/Symfony/Component/PropertyAccess/PropertyPathInterface.stub diff --git a/stubs/Symfony/Component/HttpFoundation/InputBag.stub b/stubs/Symfony/Component/HttpFoundation/InputBag.stub new file mode 100644 index 00000000..98223ce6 --- /dev/null +++ b/stubs/Symfony/Component/HttpFoundation/InputBag.stub @@ -0,0 +1,20 @@ + + */ + public $request; + + /** + * Query string parameters ($_GET). + * + * @var InputBag + */ + public $query; + + /** + * Cookies ($_COOKIE). + * + * @var InputBag + */ + public $cookies; + +} diff --git a/tests/Type/Symfony/ExtensionTest.php b/tests/Type/Symfony/ExtensionTest.php index 8ee9aced..dac1c232 100644 --- a/tests/Type/Symfony/ExtensionTest.php +++ b/tests/Type/Symfony/ExtensionTest.php @@ -44,6 +44,7 @@ public function dataFileAsserts(): iterable yield from $this->gatherAssertTypes(__DIR__ . '/data/serializer.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/denormalizer.php'); + yield from $this->gatherAssertTypes(__DIR__ . '/data/input_bag_from_request.php'); } /** diff --git a/tests/Type/Symfony/data/input_bag_from_request.php b/tests/Type/Symfony/data/input_bag_from_request.php new file mode 100644 index 00000000..7aa6057c --- /dev/null +++ b/tests/Type/Symfony/data/input_bag_from_request.php @@ -0,0 +1,22 @@ +request->get('foo')); + assertType('string|null', $request->query->get('foo')); + assertType('string|null', $request->cookies->get('foo')); + + assertType('bool|float|int|string', $request->request->get('foo', 'foo')); + assertType('string', $request->query->get('foo', 'foo')); + assertType('string', $request->cookies->get('foo', 'foo')); + } + +} From b339195f7c09a44d6346a22c04a521845ddfb3a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A1chym=20Tou=C5=A1ek?= Date: Mon, 23 Aug 2021 14:54:12 +0200 Subject: [PATCH 012/208] Add Constraint::$errorNames --- stubs/Symfony/Component/Validator/Constraint.stub | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/stubs/Symfony/Component/Validator/Constraint.stub b/stubs/Symfony/Component/Validator/Constraint.stub index 7d875f2a..e7a4b501 100644 --- a/stubs/Symfony/Component/Validator/Constraint.stub +++ b/stubs/Symfony/Component/Validator/Constraint.stub @@ -4,6 +4,11 @@ namespace Symfony\Component\Validator; class Constraint { + /** + * @var array + */ + protected static $errorNames = []; + /** * @return array */ From 390cfa13ffabd7968f733843828c4bb3f7a7c3c0 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Thu, 2 Sep 2021 13:49:47 +0200 Subject: [PATCH 013/208] Fix tests --- tests/Type/Symfony/ExtensionTest.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/Type/Symfony/ExtensionTest.php b/tests/Type/Symfony/ExtensionTest.php index dac1c232..5a49366b 100644 --- a/tests/Type/Symfony/ExtensionTest.php +++ b/tests/Type/Symfony/ExtensionTest.php @@ -43,8 +43,12 @@ public function dataFileAsserts(): iterable } yield from $this->gatherAssertTypes(__DIR__ . '/data/serializer.php'); + + if (class_exists('Symfony\Component\HttpFoundation\InputBag')) { + yield from $this->gatherAssertTypes(__DIR__ . '/data/input_bag_from_request.php'); + } + yield from $this->gatherAssertTypes(__DIR__ . '/data/denormalizer.php'); - yield from $this->gatherAssertTypes(__DIR__ . '/data/input_bag_from_request.php'); } /** From c1627fce5b505b3f53d9d4fbd4d7963603305f98 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Thu, 2 Sep 2021 13:53:08 +0200 Subject: [PATCH 014/208] Fix nonexistent InputBag on Symfony 4.x --- composer.json | 2 +- extension.neon | 6 ++-- src/Symfony/InputBagStubFilesExtension.php | 22 ++++++++++++++ tests/Rules/NonexistentInputBagClassTest.php | 32 ++++++++++++++++++++ tests/Rules/data/input_bag.php | 23 ++++++++++++++ 5 files changed, 82 insertions(+), 3 deletions(-) create mode 100644 src/Symfony/InputBagStubFilesExtension.php create mode 100644 tests/Rules/NonexistentInputBagClassTest.php create mode 100644 tests/Rules/data/input_bag.php diff --git a/composer.json b/composer.json index f895d38e..2320f059 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ "require": { "php": "^7.1 || ^8.0", "ext-simplexml": "*", - "phpstan/phpstan": "^0.12.97" + "phpstan/phpstan": "^0.12.98" }, "conflict": { "symfony/framework-bundle": "<3.0" diff --git a/extension.neon b/extension.neon index 2dee6a69..8907cc54 100644 --- a/extension.neon +++ b/extension.neon @@ -25,9 +25,7 @@ parameters: - stubs/Symfony/Component/Form/FormView.stub - stubs/Symfony/Component/HttpFoundation/Cookie.stub - stubs/Symfony/Component/HttpFoundation/HeaderBag.stub - - stubs/Symfony/Component/HttpFoundation/InputBag.stub - stubs/Symfony/Component/HttpFoundation/ParameterBag.stub - - stubs/Symfony/Component/HttpFoundation/Request.stub - stubs/Symfony/Component/HttpFoundation/Session.stub - stubs/Symfony/Component/Process/Process.stub - stubs/Symfony/Component/PropertyAccess/PropertyPathInterface.stub @@ -245,3 +243,7 @@ services: - factory: PHPStan\Type\Symfony\ParameterDynamicReturnTypeExtension(Symfony\Bundle\FrameworkBundle\Controller\Controller, 'getParameter', null, %symfony.constant_hassers%) tags: [phpstan.broker.dynamicMethodReturnTypeExtension] + - + class: PHPStan\Symfony\InputBagStubFilesExtension + tags: + - phpstan.stubFilesExtension diff --git a/src/Symfony/InputBagStubFilesExtension.php b/src/Symfony/InputBagStubFilesExtension.php new file mode 100644 index 00000000..94fd2104 --- /dev/null +++ b/src/Symfony/InputBagStubFilesExtension.php @@ -0,0 +1,22 @@ + + */ +class NonexistentInputBagClassTest extends RuleTestCase +{ + + protected function getRule(): \PHPStan\Rules\Rule + { + return self::getContainer()->getByType(CallMethodsRule::class); + } + + public function testInputBag(): void + { + $this->analyse([__DIR__ . '/data/input_bag.php'], []); + } + + public static function getAdditionalConfigFiles(): array + { + return [ + __DIR__ . '/../../extension.neon', + __DIR__ . '/../../rules.neon', + ]; + } + +} diff --git a/tests/Rules/data/input_bag.php b/tests/Rules/data/input_bag.php new file mode 100644 index 00000000..03699f6b --- /dev/null +++ b/tests/Rules/data/input_bag.php @@ -0,0 +1,23 @@ +query->get('foo'); + + return $this->render('test/index.html.twig', [ + 'controller_name' => 'TestController', + ]); + } +} From 44251cd5f45cbcc76c8bb5c27f61fceb79ba799f Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Sun, 12 Sep 2021 22:36:24 +0200 Subject: [PATCH 015/208] Open 1.0-dev --- composer.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/composer.json b/composer.json index 2320f059..59676289 100644 --- a/composer.json +++ b/composer.json @@ -15,15 +15,15 @@ "require": { "php": "^7.1 || ^8.0", "ext-simplexml": "*", - "phpstan/phpstan": "^0.12.98" + "phpstan/phpstan": "^1.0" }, "conflict": { "symfony/framework-bundle": "<3.0" }, "require-dev": { "php-parallel-lint/php-parallel-lint": "^1.2", - "phpstan/phpstan-phpunit": "^0.12.16", - "phpstan/phpstan-strict-rules": "^0.12.5", + "phpstan/phpstan-phpunit": "^1.0", + "phpstan/phpstan-strict-rules": "^1.0", "phpunit/phpunit": "^9.5", "symfony/config": "^4.2 || ^5.0", "symfony/console": "^4.0 || ^5.0", @@ -40,7 +40,7 @@ }, "extra": { "branch-alias": { - "dev-master": "0.12-dev" + "dev-master": "1.0-dev" }, "phpstan": { "includes": [ From 3707cff25f90b16bf792beb70c70e5dcad6f5a7d Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Sun, 12 Sep 2021 23:52:07 +0200 Subject: [PATCH 016/208] Enforce generic InputBag --- extension.neon | 3 --- 1 file changed, 3 deletions(-) diff --git a/extension.neon b/extension.neon index 8907cc54..27e504bd 100644 --- a/extension.neon +++ b/extension.neon @@ -4,9 +4,6 @@ parameters: exceptions: uncheckedExceptionClasses: - 'Symfony\Component\Console\Exception\InvalidArgumentException' - featureToggles: - skipCheckGenericClasses: - - Symfony\Component\HttpFoundation\InputBag symfony: container_xml_path: null constant_hassers: true From fb817e2f0ae9b9a90990de4f0a537dbfaddcad7a Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Mon, 13 Sep 2021 10:45:19 +0200 Subject: [PATCH 017/208] Improve compatibility with PHPStan 1.0 --- src/Symfony/ConsoleApplicationResolver.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Symfony/ConsoleApplicationResolver.php b/src/Symfony/ConsoleApplicationResolver.php index cc4ece4e..a493cbf0 100644 --- a/src/Symfony/ConsoleApplicationResolver.php +++ b/src/Symfony/ConsoleApplicationResolver.php @@ -5,6 +5,7 @@ use PHPStan\Reflection\ClassReflection; use PHPStan\ShouldNotHappenException; use PHPStan\Type\ObjectType; +use Symfony\Component\Console\Command\Command; use function file_exists; use function get_class; use function is_readable; @@ -57,8 +58,12 @@ public function findCommands(ClassReflection $classReflection): array $commandClass = new ObjectType(get_class($command)); $isLazyCommand = (new ObjectType('Symfony\Component\Console\Command\LazyCommand'))->isSuperTypeOf($commandClass)->yes(); - if ($isLazyCommand && method_exists($command, 'getCommand') && !$classType->isSuperTypeOf(new ObjectType(get_class($command->getCommand())))->yes()) { - continue; + if ($isLazyCommand && method_exists($command, 'getCommand')) { + /** @var Command $wrappedCommand */ + $wrappedCommand = $command->getCommand(); + if (!$classType->isSuperTypeOf(new ObjectType(get_class($wrappedCommand)))->yes()) { + continue; + } } if (!$isLazyCommand && !$classType->isSuperTypeOf($commandClass)->yes()) { From 1108c45d4326f3aaa85547ee0893bcc6ee375156 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Wed, 15 Sep 2021 16:16:02 +0200 Subject: [PATCH 018/208] Improve compatibility with PHPStan 1.0 --- phpstan.neon | 2 +- .../Symfony/UndefinedArgumentRuleTest.php | 18 +++++++----------- .../Rules/Symfony/UndefinedOptionRuleTest.php | 18 +++++++----------- tests/Rules/Symfony/argument.neon | 5 +++++ tests/Rules/Symfony/option.neon | 5 +++++ 5 files changed, 25 insertions(+), 23 deletions(-) create mode 100644 tests/Rules/Symfony/argument.neon create mode 100644 tests/Rules/Symfony/option.neon diff --git a/phpstan.neon b/phpstan.neon index 13242793..39998f59 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -7,7 +7,7 @@ includes: - phar://phpstan.phar/conf/bleedingEdge.neon parameters: - excludes_analyse: + excludePaths: - tests/tmp/* - tests/*/Example*.php - tests/*/console_application_loader.php diff --git a/tests/Rules/Symfony/UndefinedArgumentRuleTest.php b/tests/Rules/Symfony/UndefinedArgumentRuleTest.php index ab4e1b05..b3f9e56e 100644 --- a/tests/Rules/Symfony/UndefinedArgumentRuleTest.php +++ b/tests/Rules/Symfony/UndefinedArgumentRuleTest.php @@ -6,7 +6,6 @@ use PHPStan\Rules\Rule; use PHPStan\Symfony\ConsoleApplicationResolver; use PHPStan\Testing\RuleTestCase; -use PHPStan\Type\Symfony\ArgumentTypeSpecifyingExtension; /** * @extends RuleTestCase @@ -19,16 +18,6 @@ protected function getRule(): Rule return new UndefinedArgumentRule(new ConsoleApplicationResolver(__DIR__ . '/console_application_loader.php'), new Standard()); } - /** - * @return \PHPStan\Type\MethodTypeSpecifyingExtension[] - */ - protected function getMethodTypeSpecifyingExtensions(): array - { - return [ - new ArgumentTypeSpecifyingExtension(new Standard()), - ]; - } - public function testGetArgument(): void { $this->analyse( @@ -44,4 +33,11 @@ public function testGetArgument(): void ); } + public static function getAdditionalConfigFiles(): array + { + return [ + __DIR__ . '/argument.neon', + ]; + } + } diff --git a/tests/Rules/Symfony/UndefinedOptionRuleTest.php b/tests/Rules/Symfony/UndefinedOptionRuleTest.php index 5e81e401..32ebe773 100644 --- a/tests/Rules/Symfony/UndefinedOptionRuleTest.php +++ b/tests/Rules/Symfony/UndefinedOptionRuleTest.php @@ -6,7 +6,6 @@ use PHPStan\Rules\Rule; use PHPStan\Symfony\ConsoleApplicationResolver; use PHPStan\Testing\RuleTestCase; -use PHPStan\Type\Symfony\OptionTypeSpecifyingExtension; /** * @extends RuleTestCase @@ -19,16 +18,6 @@ protected function getRule(): Rule return new UndefinedOptionRule(new ConsoleApplicationResolver(__DIR__ . '/console_application_loader.php'), new Standard()); } - /** - * @return \PHPStan\Type\MethodTypeSpecifyingExtension[] - */ - protected function getMethodTypeSpecifyingExtensions(): array - { - return [ - new OptionTypeSpecifyingExtension(new Standard()), - ]; - } - public function testGetArgument(): void { $this->analyse( @@ -44,4 +33,11 @@ public function testGetArgument(): void ); } + public static function getAdditionalConfigFiles(): array + { + return [ + __DIR__ . '/option.neon', + ]; + } + } diff --git a/tests/Rules/Symfony/argument.neon b/tests/Rules/Symfony/argument.neon new file mode 100644 index 00000000..86fa3f16 --- /dev/null +++ b/tests/Rules/Symfony/argument.neon @@ -0,0 +1,5 @@ +services: + - + class: PHPStan\Type\Symfony\ArgumentTypeSpecifyingExtension + tags: + - phpstan.typeSpecifier.methodTypeSpecifyingExtension diff --git a/tests/Rules/Symfony/option.neon b/tests/Rules/Symfony/option.neon new file mode 100644 index 00000000..30984a7a --- /dev/null +++ b/tests/Rules/Symfony/option.neon @@ -0,0 +1,5 @@ +services: + - + class: PHPStan\Type\Symfony\OptionTypeSpecifyingExtension + tags: + - phpstan.typeSpecifier.methodTypeSpecifyingExtension From 0e272751e6141e6ece0c4b4a6cdb5d10ac39c73e Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Wed, 15 Sep 2021 16:50:11 +0200 Subject: [PATCH 019/208] Improved compatibility with PHPStan 1.0 --- ...ntainerInterfaceUnknownServiceRuleTest.php | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php index 4f4fc092..297158a6 100644 --- a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php +++ b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php @@ -6,8 +6,6 @@ use PHPStan\Rules\Rule; use PHPStan\Symfony\XmlServiceMapFactory; use PHPStan\Testing\RuleTestCase; -use PHPStan\Type\Symfony\ServiceTypeSpecifyingExtension; -use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; /** * @extends RuleTestCase @@ -20,17 +18,6 @@ protected function getRule(): Rule return new ContainerInterfaceUnknownServiceRule((new XmlServiceMapFactory(__DIR__ . '/container.xml'))->create(), new Standard()); } - /** - * @return \PHPStan\Type\MethodTypeSpecifyingExtension[] - */ - protected function getMethodTypeSpecifyingExtensions(): array - { - return [ - new ServiceTypeSpecifyingExtension('Symfony\Bundle\FrameworkBundle\Controller\Controller', new Standard()), - new ServiceTypeSpecifyingExtension(AbstractController::class, new Standard()), - ]; - } - public function testGetPrivateService(): void { if (!class_exists('Symfony\Bundle\FrameworkBundle\Controller\Controller')) { @@ -82,4 +69,11 @@ public function testGetPrivateServiceInLegacyServiceSubscriber(): void ); } + public static function getAdditionalConfigFiles(): array + { + return [ + __DIR__ . '/../../../extension.neon', + ]; + } + } From a0ce7fd33dcc5308c2c7d697b55bd80e20b78e2c Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Mon, 20 Sep 2021 18:21:26 +0200 Subject: [PATCH 020/208] Constrain lowest PHPParser version to 4.13.0 --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index 59676289..06adbfbf 100644 --- a/composer.json +++ b/composer.json @@ -21,6 +21,7 @@ "symfony/framework-bundle": "<3.0" }, "require-dev": { + "nikic/php-parser": "^4.13.0", "php-parallel-lint/php-parallel-lint": "^1.2", "phpstan/phpstan-phpunit": "^1.0", "phpstan/phpstan-strict-rules": "^1.0", From 84541f032b2b27def3b3139d52d605c43a1afb84 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Mon, 20 Sep 2021 18:21:59 +0200 Subject: [PATCH 021/208] Improve compatibility with nikic/php-parser 4.13.0 --- .../Symfony/ContainerInterfacePrivateServiceRule.php | 4 ++-- .../Symfony/ContainerInterfaceUnknownServiceRule.php | 6 +++--- src/Rules/Symfony/InvalidArgumentDefaultValueRule.php | 6 +++--- src/Rules/Symfony/InvalidOptionDefaultValueRule.php | 6 +++--- src/Rules/Symfony/UndefinedArgumentRule.php | 4 ++-- src/Rules/Symfony/UndefinedOptionRule.php | 4 ++-- src/Type/Symfony/ArgumentTypeSpecifyingExtension.php | 4 ++-- ...yNodeDefinitionPrototypeDynamicReturnTypeExtension.php | 4 ++-- .../Config/TreeBuilderDynamicReturnTypeExtension.php | 4 ++-- src/Type/Symfony/EnvelopeReturnTypeExtension.php | 4 ++-- src/Type/Symfony/HeaderBagDynamicReturnTypeExtension.php | 4 ++-- src/Type/Symfony/InputBagDynamicReturnTypeExtension.php | 6 +++--- ...nputInterfaceGetArgumentDynamicReturnTypeExtension.php | 6 +++--- .../InputInterfaceGetOptionDynamicReturnTypeExtension.php | 6 +++--- ...nputInterfaceHasArgumentDynamicReturnTypeExtension.php | 4 ++-- .../InputInterfaceHasOptionDynamicReturnTypeExtension.php | 4 ++-- .../Symfony/KernelInterfaceDynamicReturnTypeExtension.php | 2 +- src/Type/Symfony/OptionTypeSpecifyingExtension.php | 4 ++-- src/Type/Symfony/ParameterDynamicReturnTypeExtension.php | 8 ++++---- src/Type/Symfony/RequestDynamicReturnTypeExtension.php | 4 ++-- src/Type/Symfony/SerializerDynamicReturnTypeExtension.php | 4 ++-- src/Type/Symfony/ServiceDynamicReturnTypeExtension.php | 8 ++++---- src/Type/Symfony/ServiceTypeSpecifyingExtension.php | 4 ++-- 23 files changed, 55 insertions(+), 55 deletions(-) diff --git a/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php b/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php index c471e6ba..ee4d5b05 100644 --- a/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php +++ b/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php @@ -45,7 +45,7 @@ public function processNode(Node $node, Scope $scope): array return []; } - if ($node->name->name !== 'get' || !isset($node->args[0])) { + if ($node->name->name !== 'get' || !isset($node->getArgs()[0])) { return []; } @@ -72,7 +72,7 @@ public function processNode(Node $node, Scope $scope): array return []; } - $serviceId = $this->serviceMap::getServiceIdFromNode($node->args[0]->value, $scope); + $serviceId = $this->serviceMap::getServiceIdFromNode($node->getArgs()[0]->value, $scope); if ($serviceId !== null) { $service = $this->serviceMap->getService($serviceId); if ($service !== null && !$service->isPublic()) { diff --git a/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php b/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php index eacc2071..cb0ae4e6 100644 --- a/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php +++ b/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php @@ -50,7 +50,7 @@ public function processNode(Node $node, Scope $scope): array return []; } - if ($node->name->name !== 'get' || !isset($node->args[0])) { + if ($node->name->name !== 'get' || !isset($node->getArgs()[0])) { return []; } @@ -73,10 +73,10 @@ public function processNode(Node $node, Scope $scope): array return []; } - $serviceId = $this->serviceMap::getServiceIdFromNode($node->args[0]->value, $scope); + $serviceId = $this->serviceMap::getServiceIdFromNode($node->getArgs()[0]->value, $scope); if ($serviceId !== null) { $service = $this->serviceMap->getService($serviceId); - $serviceIdType = $scope->getType($node->args[0]->value); + $serviceIdType = $scope->getType($node->getArgs()[0]->value); if ($service === null && !$scope->getType(Helper::createMarkerNode($node->var, $serviceIdType, $this->printer))->equals($serviceIdType)) { return [sprintf('Service "%s" is not registered in the container.', $serviceId)]; } diff --git a/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php b/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php index b735dbfe..c29f11ed 100644 --- a/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php +++ b/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php @@ -46,11 +46,11 @@ public function processNode(Node $node, Scope $scope): array if (!$node->name instanceof Node\Identifier || $node->name->name !== 'addArgument') { return []; } - if (!isset($node->args[3])) { + if (!isset($node->getArgs()[3])) { return []; } - $modeType = isset($node->args[1]) ? $scope->getType($node->args[1]->value) : new NullType(); + $modeType = isset($node->getArgs()[1]) ? $scope->getType($node->getArgs()[1]->value) : new NullType(); if ($modeType instanceof NullType) { $modeType = new ConstantIntegerType(2); // InputArgument::OPTIONAL } @@ -63,7 +63,7 @@ public function processNode(Node $node, Scope $scope): array } $mode = $modeTypes[0]->getValue(); - $defaultType = $scope->getType($node->args[3]->value); + $defaultType = $scope->getType($node->getArgs()[3]->value); // not an array if (($mode & 4) !== 4 && !(new UnionType([new StringType(), new NullType()]))->isSuperTypeOf($defaultType)->yes()) { diff --git a/src/Rules/Symfony/InvalidOptionDefaultValueRule.php b/src/Rules/Symfony/InvalidOptionDefaultValueRule.php index 678feb35..3eb2df1b 100644 --- a/src/Rules/Symfony/InvalidOptionDefaultValueRule.php +++ b/src/Rules/Symfony/InvalidOptionDefaultValueRule.php @@ -48,11 +48,11 @@ public function processNode(Node $node, Scope $scope): array if (!$node->name instanceof Node\Identifier || $node->name->name !== 'addOption') { return []; } - if (!isset($node->args[4])) { + if (!isset($node->getArgs()[4])) { return []; } - $modeType = isset($node->args[2]) ? $scope->getType($node->args[2]->value) : new NullType(); + $modeType = isset($node->getArgs()[2]) ? $scope->getType($node->getArgs()[2]->value) : new NullType(); if ($modeType instanceof NullType) { $modeType = new ConstantIntegerType(1); // InputOption::VALUE_NONE } @@ -65,7 +65,7 @@ public function processNode(Node $node, Scope $scope): array } $mode = $modeTypes[0]->getValue(); - $defaultType = $scope->getType($node->args[4]->value); + $defaultType = $scope->getType($node->getArgs()[4]->value); // not an array if (($mode & 8) !== 8) { diff --git a/src/Rules/Symfony/UndefinedArgumentRule.php b/src/Rules/Symfony/UndefinedArgumentRule.php index e6de90b7..59f71ed6 100644 --- a/src/Rules/Symfony/UndefinedArgumentRule.php +++ b/src/Rules/Symfony/UndefinedArgumentRule.php @@ -64,11 +64,11 @@ public function processNode(Node $node, Scope $scope): array if (!$node->name instanceof Node\Identifier || $node->name->name !== 'getArgument') { return []; } - if (!isset($node->args[0])) { + if (!isset($node->getArgs()[0])) { return []; } - $argType = $scope->getType($node->args[0]->value); + $argType = $scope->getType($node->getArgs()[0]->value); $argStrings = TypeUtils::getConstantStrings($argType); if (count($argStrings) !== 1) { return []; diff --git a/src/Rules/Symfony/UndefinedOptionRule.php b/src/Rules/Symfony/UndefinedOptionRule.php index ee041929..29e08819 100644 --- a/src/Rules/Symfony/UndefinedOptionRule.php +++ b/src/Rules/Symfony/UndefinedOptionRule.php @@ -64,11 +64,11 @@ public function processNode(Node $node, Scope $scope): array if (!$node->name instanceof Node\Identifier || $node->name->name !== 'getOption') { return []; } - if (!isset($node->args[0])) { + if (!isset($node->getArgs()[0])) { return []; } - $optType = $scope->getType($node->args[0]->value); + $optType = $scope->getType($node->getArgs()[0]->value); $optStrings = TypeUtils::getConstantStrings($optType); if (count($optStrings) !== 1) { return []; diff --git a/src/Type/Symfony/ArgumentTypeSpecifyingExtension.php b/src/Type/Symfony/ArgumentTypeSpecifyingExtension.php index edf5574c..74b6981f 100644 --- a/src/Type/Symfony/ArgumentTypeSpecifyingExtension.php +++ b/src/Type/Symfony/ArgumentTypeSpecifyingExtension.php @@ -38,10 +38,10 @@ public function isMethodSupported(MethodReflection $methodReflection, MethodCall public function specifyTypes(MethodReflection $methodReflection, MethodCall $node, Scope $scope, TypeSpecifierContext $context): SpecifiedTypes { - if (!isset($node->args[0])) { + if (!isset($node->getArgs()[0])) { return new SpecifiedTypes(); } - $argType = $scope->getType($node->args[0]->value); + $argType = $scope->getType($node->getArgs()[0]->value); return $this->typeSpecifier->create( Helper::createMarkerNode($node->var, $argType, $this->printer), $argType, diff --git a/src/Type/Symfony/Config/ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php index 71e4fb17..cd1d4e51 100644 --- a/src/Type/Symfony/Config/ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/Config/ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php @@ -56,11 +56,11 @@ public function getTypeFromMethodCall( $defaultType = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType(); if ($methodReflection->getName() === 'prototype') { - if (!isset($methodCall->args[0])) { + if (!isset($methodCall->getArgs()[0])) { return $defaultType; } - $argStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->args[0]->value)); + $argStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->getArgs()[0]->value)); if (count($argStrings) === 1 && isset(self::MAPPING[$argStrings[0]->getValue()])) { $type = $argStrings[0]->getValue(); diff --git a/src/Type/Symfony/Config/TreeBuilderDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/TreeBuilderDynamicReturnTypeExtension.php index 25cd12dc..a5f4f724 100644 --- a/src/Type/Symfony/Config/TreeBuilderDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/Config/TreeBuilderDynamicReturnTypeExtension.php @@ -44,8 +44,8 @@ public function getTypeFromStaticMethodCall(MethodReflection $methodReflection, $type = 'array'; - if (isset($methodCall->args[1])) { - $argStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->args[1]->value)); + if (isset($methodCall->getArgs()[1])) { + $argStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->getArgs()[1]->value)); if (count($argStrings) === 1 && isset(self::MAPPING[$argStrings[0]->getValue()])) { $type = $argStrings[0]->getValue(); } diff --git a/src/Type/Symfony/EnvelopeReturnTypeExtension.php b/src/Type/Symfony/EnvelopeReturnTypeExtension.php index bf0712fc..e4e6a34c 100644 --- a/src/Type/Symfony/EnvelopeReturnTypeExtension.php +++ b/src/Type/Symfony/EnvelopeReturnTypeExtension.php @@ -31,11 +31,11 @@ public function getTypeFromMethodCall( Scope $scope ): Type { - if (count($methodCall->args) === 0) { + if (count($methodCall->getArgs()) === 0) { return new ArrayType(new MixedType(), new ArrayType(new MixedType(), new ObjectType('Symfony\Component\Messenger\Stamp\StampInterface'))); } - $argType = $scope->getType($methodCall->args[0]->value); + $argType = $scope->getType($methodCall->getArgs()[0]->value); if (!$argType instanceof ConstantStringType) { return new ArrayType(new MixedType(), new ObjectType('Symfony\Component\Messenger\Stamp\StampInterface')); } diff --git a/src/Type/Symfony/HeaderBagDynamicReturnTypeExtension.php b/src/Type/Symfony/HeaderBagDynamicReturnTypeExtension.php index 42c6878a..5b05a711 100644 --- a/src/Type/Symfony/HeaderBagDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/HeaderBagDynamicReturnTypeExtension.php @@ -34,13 +34,13 @@ public function getTypeFromMethodCall( Scope $scope ): Type { - $firstArgType = isset($methodCall->args[2]) ? $scope->getType($methodCall->args[2]->value) : new ConstantBooleanType(true); + $firstArgType = isset($methodCall->getArgs()[2]) ? $scope->getType($methodCall->getArgs()[2]->value) : new ConstantBooleanType(true); $isTrueType = (new ConstantBooleanType(true))->isSuperTypeOf($firstArgType); $isFalseType = (new ConstantBooleanType(false))->isSuperTypeOf($firstArgType); $compareTypes = $isTrueType->compareTo($isFalseType); if ($compareTypes === $isTrueType) { - $defaultArgType = isset($methodCall->args[1]) ? $scope->getType($methodCall->args[1]->value) : new NullType(); + $defaultArgType = isset($methodCall->getArgs()[1]) ? $scope->getType($methodCall->getArgs()[1]->value) : new NullType(); return TypeCombinator::union($defaultArgType, new StringType()); } diff --git a/src/Type/Symfony/InputBagDynamicReturnTypeExtension.php b/src/Type/Symfony/InputBagDynamicReturnTypeExtension.php index a403af7f..f328ea86 100644 --- a/src/Type/Symfony/InputBagDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/InputBagDynamicReturnTypeExtension.php @@ -55,8 +55,8 @@ private function getGetTypeFromMethodCall( Scope $scope ): Type { - if (isset($methodCall->args[1])) { - $argType = $scope->getType($methodCall->args[1]->value); + if (isset($methodCall->getArgs()[1])) { + $argType = $scope->getType($methodCall->getArgs()[1]->value); $isNull = (new NullType())->isSuperTypeOf($argType); if ($isNull->no()) { return TypeCombinator::removeNull(ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType()); @@ -77,7 +77,7 @@ private function getAllTypeFromMethodCall( new StringType(), ]; $oneParameterType = new UnionType($types); - if (isset($methodCall->args[0])) { + if (isset($methodCall->getArgs()[0])) { return new ArrayType(new MixedType(), $oneParameterType); } diff --git a/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php index 24cccc40..15e75d0e 100644 --- a/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php @@ -40,9 +40,9 @@ public function isMethodSupported(MethodReflection $methodReflection): bool public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type { - $defaultReturnType = ParametersAcceptorSelector::selectFromArgs($scope, $methodCall->args, $methodReflection->getVariants())->getReturnType(); + $defaultReturnType = ParametersAcceptorSelector::selectFromArgs($scope, $methodCall->getArgs(), $methodReflection->getVariants())->getReturnType(); - if (!isset($methodCall->args[0])) { + if (!isset($methodCall->getArgs()[0])) { return $defaultReturnType; } @@ -51,7 +51,7 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method return $defaultReturnType; } - $argStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->args[0]->value)); + $argStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->getArgs()[0]->value)); if (count($argStrings) !== 1) { return $defaultReturnType; } diff --git a/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php index 40a3993f..fb397311 100644 --- a/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php @@ -42,9 +42,9 @@ public function isMethodSupported(MethodReflection $methodReflection): bool public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type { - $defaultReturnType = ParametersAcceptorSelector::selectFromArgs($scope, $methodCall->args, $methodReflection->getVariants())->getReturnType(); + $defaultReturnType = ParametersAcceptorSelector::selectFromArgs($scope, $methodCall->getArgs(), $methodReflection->getVariants())->getReturnType(); - if (!isset($methodCall->args[0])) { + if (!isset($methodCall->getArgs()[0])) { return $defaultReturnType; } @@ -53,7 +53,7 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method return $defaultReturnType; } - $optStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->args[0]->value)); + $optStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->getArgs()[0]->value)); if (count($optStrings) !== 1) { return $defaultReturnType; } diff --git a/src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php index f69908fe..112a2bcf 100644 --- a/src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php @@ -40,7 +40,7 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method { $defaultReturnType = new BooleanType(); - if (!isset($methodCall->args[0])) { + if (!isset($methodCall->getArgs()[0])) { return $defaultReturnType; } @@ -49,7 +49,7 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method return $defaultReturnType; } - $argStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->args[0]->value)); + $argStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->getArgs()[0]->value)); if (count($argStrings) !== 1) { return $defaultReturnType; } diff --git a/src/Type/Symfony/InputInterfaceHasOptionDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceHasOptionDynamicReturnTypeExtension.php index c60e2644..eec7201c 100644 --- a/src/Type/Symfony/InputInterfaceHasOptionDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/InputInterfaceHasOptionDynamicReturnTypeExtension.php @@ -40,7 +40,7 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method { $defaultReturnType = new BooleanType(); - if (!isset($methodCall->args[0])) { + if (!isset($methodCall->getArgs()[0])) { return $defaultReturnType; } @@ -49,7 +49,7 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method return $defaultReturnType; } - $optStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->args[0]->value)); + $optStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->getArgs()[0]->value)); if (count($optStrings) !== 1) { return $defaultReturnType; } diff --git a/src/Type/Symfony/KernelInterfaceDynamicReturnTypeExtension.php b/src/Type/Symfony/KernelInterfaceDynamicReturnTypeExtension.php index f74f114e..5a2adae5 100644 --- a/src/Type/Symfony/KernelInterfaceDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/KernelInterfaceDynamicReturnTypeExtension.php @@ -32,7 +32,7 @@ public function getTypeFromMethodCall( Scope $scope ): Type { - $firstArgType = isset($methodCall->args[2]) ? $scope->getType($methodCall->args[2]->value) : new ConstantBooleanType(true); + $firstArgType = isset($methodCall->getArgs()[2]) ? $scope->getType($methodCall->getArgs()[2]->value) : new ConstantBooleanType(true); $isTrueType = (new ConstantBooleanType(true))->isSuperTypeOf($firstArgType); $isFalseType = (new ConstantBooleanType(false))->isSuperTypeOf($firstArgType); $compareTypes = $isTrueType->compareTo($isFalseType); diff --git a/src/Type/Symfony/OptionTypeSpecifyingExtension.php b/src/Type/Symfony/OptionTypeSpecifyingExtension.php index aaff84c0..347cf591 100644 --- a/src/Type/Symfony/OptionTypeSpecifyingExtension.php +++ b/src/Type/Symfony/OptionTypeSpecifyingExtension.php @@ -38,10 +38,10 @@ public function isMethodSupported(MethodReflection $methodReflection, MethodCall public function specifyTypes(MethodReflection $methodReflection, MethodCall $node, Scope $scope, TypeSpecifierContext $context): SpecifiedTypes { - if (!isset($node->args[0])) { + if (!isset($node->getArgs()[0])) { return new SpecifiedTypes(); } - $argType = $scope->getType($node->args[0]->value); + $argType = $scope->getType($node->getArgs()[0]->value); return $this->typeSpecifier->create( Helper::createMarkerNode($node->var, $argType, $this->printer), $argType, diff --git a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php index da7a489a..44477124 100644 --- a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php @@ -92,11 +92,11 @@ private function getGetTypeFromMethodCall( new StringType(), new NullType(), ]); - if (!isset($methodCall->args[0])) { + if (!isset($methodCall->getArgs()[0])) { return $returnType; } - $parameterKey = $this->parameterMap::getParameterKeyFromNode($methodCall->args[0]->value, $scope); + $parameterKey = $this->parameterMap::getParameterKeyFromNode($methodCall->getArgs()[0]->value, $scope); if ($parameterKey !== null) { $parameter = $this->parameterMap->getParameter($parameterKey); if ($parameter !== null) { @@ -130,11 +130,11 @@ private function getHasTypeFromMethodCall( ): Type { $returnType = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType(); - if (!isset($methodCall->args[0]) || !$this->constantHassers) { + if (!isset($methodCall->getArgs()[0]) || !$this->constantHassers) { return $returnType; } - $parameterKey = $this->parameterMap::getParameterKeyFromNode($methodCall->args[0]->value, $scope); + $parameterKey = $this->parameterMap::getParameterKeyFromNode($methodCall->getArgs()[0]->value, $scope); if ($parameterKey !== null) { $parameter = $this->parameterMap->getParameter($parameterKey); return new ConstantBooleanType($parameter !== null); diff --git a/src/Type/Symfony/RequestDynamicReturnTypeExtension.php b/src/Type/Symfony/RequestDynamicReturnTypeExtension.php index 72991824..284ddf01 100644 --- a/src/Type/Symfony/RequestDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/RequestDynamicReturnTypeExtension.php @@ -31,11 +31,11 @@ public function getTypeFromMethodCall( Scope $scope ): Type { - if (!isset($methodCall->args[0])) { + if (!isset($methodCall->getArgs()[0])) { return new StringType(); } - $argType = $scope->getType($methodCall->args[0]->value); + $argType = $scope->getType($methodCall->getArgs()[0]->value); $isTrueType = (new ConstantBooleanType(true))->isSuperTypeOf($argType); $isFalseType = (new ConstantBooleanType(false))->isSuperTypeOf($argType); $compareTypes = $isTrueType->compareTo($isFalseType); diff --git a/src/Type/Symfony/SerializerDynamicReturnTypeExtension.php b/src/Type/Symfony/SerializerDynamicReturnTypeExtension.php index c6c237ee..fda9b048 100755 --- a/src/Type/Symfony/SerializerDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/SerializerDynamicReturnTypeExtension.php @@ -39,11 +39,11 @@ public function isMethodSupported(MethodReflection $methodReflection): bool public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type { - if (!isset($methodCall->args[1])) { + if (!isset($methodCall->getArgs()[1])) { return new MixedType(); } - $argType = $scope->getType($methodCall->args[1]->value); + $argType = $scope->getType($methodCall->getArgs()[1]->value); if (!$argType instanceof ConstantStringType) { return new MixedType(); } diff --git a/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php b/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php index 9d028c35..650aaa42 100644 --- a/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php @@ -61,11 +61,11 @@ private function getGetTypeFromMethodCall( ): Type { $returnType = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType(); - if (!isset($methodCall->args[0])) { + if (!isset($methodCall->getArgs()[0])) { return $returnType; } - $serviceId = $this->serviceMap::getServiceIdFromNode($methodCall->args[0]->value, $scope); + $serviceId = $this->serviceMap::getServiceIdFromNode($methodCall->getArgs()[0]->value, $scope); if ($serviceId !== null) { $service = $this->serviceMap->getService($serviceId); if ($service !== null && (!$service->isSynthetic() || $service->getClass() !== null)) { @@ -83,11 +83,11 @@ private function getHasTypeFromMethodCall( ): Type { $returnType = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType(); - if (!isset($methodCall->args[0]) || !$this->constantHassers) { + if (!isset($methodCall->getArgs()[0]) || !$this->constantHassers) { return $returnType; } - $serviceId = $this->serviceMap::getServiceIdFromNode($methodCall->args[0]->value, $scope); + $serviceId = $this->serviceMap::getServiceIdFromNode($methodCall->getArgs()[0]->value, $scope); if ($serviceId !== null) { $service = $this->serviceMap->getService($serviceId); return new ConstantBooleanType($service !== null && $service->isPublic()); diff --git a/src/Type/Symfony/ServiceTypeSpecifyingExtension.php b/src/Type/Symfony/ServiceTypeSpecifyingExtension.php index e9946387..a5aad54e 100644 --- a/src/Type/Symfony/ServiceTypeSpecifyingExtension.php +++ b/src/Type/Symfony/ServiceTypeSpecifyingExtension.php @@ -42,10 +42,10 @@ public function isMethodSupported(MethodReflection $methodReflection, MethodCall public function specifyTypes(MethodReflection $methodReflection, MethodCall $node, Scope $scope, TypeSpecifierContext $context): SpecifiedTypes { - if (!isset($node->args[0])) { + if (!isset($node->getArgs()[0])) { return new SpecifiedTypes(); } - $argType = $scope->getType($node->args[0]->value); + $argType = $scope->getType($node->getArgs()[0]->value); return $this->typeSpecifier->create( Helper::createMarkerNode($node->var, $argType, $this->printer), $argType, From 7d8ac24aa6f71ea790dc750bef2ffd62f88ec7d8 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Thu, 23 Sep 2021 13:04:39 +0200 Subject: [PATCH 022/208] Improved compatibility with PHPStan 1.0 --- src/Type/Symfony/ParameterDynamicReturnTypeExtension.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php index 44477124..8e79cf59 100644 --- a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php @@ -15,6 +15,7 @@ use PHPStan\Type\ConstantType; use PHPStan\Type\DynamicMethodReturnTypeExtension; use PHPStan\Type\FloatType; +use PHPStan\Type\GeneralizePrecision; use PHPStan\Type\IntegerType; use PHPStan\Type\MixedType; use PHPStan\Type\NullType; @@ -117,7 +118,7 @@ private function generalizeType(Type $type): Type return new ArrayType($this->generalizeType($type->getKeyType()), $this->generalizeType($type->getItemType())); } if ($type instanceof ConstantType) { - return $type->generalize(); + return $type->generalize(GeneralizePrecision::lessSpecific()); } return $traverse($type); }); From 1e62ed37347f9d8dd20264d6136bbe7b4c179916 Mon Sep 17 00:00:00 2001 From: greeflas Date: Mon, 27 Sep 2021 20:00:47 +0300 Subject: [PATCH 023/208] Add stubs for messenger component --- extension.neon | 2 ++ stubs/Symfony/Component/Messenger/Envelope.stub | 17 +++++++++++++++++ .../Component/Messenger/StampInterface.stub | 7 +++++++ 3 files changed, 26 insertions(+) create mode 100644 stubs/Symfony/Component/Messenger/Envelope.stub create mode 100644 stubs/Symfony/Component/Messenger/StampInterface.stub diff --git a/extension.neon b/extension.neon index 27e504bd..e389a125 100644 --- a/extension.neon +++ b/extension.neon @@ -24,6 +24,8 @@ parameters: - 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/Process/Process.stub - stubs/Symfony/Component/PropertyAccess/PropertyPathInterface.stub - stubs/Symfony/Component/Security/Acl/Model/AclInterface.stub diff --git a/stubs/Symfony/Component/Messenger/Envelope.stub b/stubs/Symfony/Component/Messenger/Envelope.stub new file mode 100644 index 00000000..c40a5ee6 --- /dev/null +++ b/stubs/Symfony/Component/Messenger/Envelope.stub @@ -0,0 +1,17 @@ + $stampFqcn + * @phpstan-return T|null + */ + public function last(string $stampFqcn): ?StampInterface + { + } +} diff --git a/stubs/Symfony/Component/Messenger/StampInterface.stub b/stubs/Symfony/Component/Messenger/StampInterface.stub new file mode 100644 index 00000000..2951ab45 --- /dev/null +++ b/stubs/Symfony/Component/Messenger/StampInterface.stub @@ -0,0 +1,7 @@ + Date: Mon, 25 Oct 2021 09:40:16 +0200 Subject: [PATCH 024/208] Add DataTransformer stub --- extension.neon | 1 + .../Form/DataTransformerInterface.stub | 26 +++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 stubs/Symfony/Component/Form/DataTransformerInterface.stub diff --git a/extension.neon b/extension.neon index e389a125..32ad05f4 100644 --- a/extension.neon +++ b/extension.neon @@ -15,6 +15,7 @@ parameters: - stubs/Symfony/Component/EventDispatcher/EventDispatcherInterface.stub - stubs/Symfony/Component/EventDispatcher/EventSubscriberInterface.stub - stubs/Symfony/Component/EventDispatcher/GenericEvent.stub + - stubs/Symfony/Component/Form/DataTransformerInterface.stub - stubs/Symfony/Component/Form/FormBuilderInterface.stub - stubs/Symfony/Component/Form/FormInterface.stub - stubs/Symfony/Component/Form/FormTypeExtensionInterface.stub diff --git a/stubs/Symfony/Component/Form/DataTransformerInterface.stub b/stubs/Symfony/Component/Form/DataTransformerInterface.stub new file mode 100644 index 00000000..edd4411e --- /dev/null +++ b/stubs/Symfony/Component/Form/DataTransformerInterface.stub @@ -0,0 +1,26 @@ + Date: Wed, 3 Nov 2021 15:14:43 +0100 Subject: [PATCH 025/208] Fix: Incorrect return type for InputBag::all() --- .../Symfony/InputBagDynamicReturnTypeExtension.php | 11 ++--------- tests/Type/Symfony/data/input_bag.php | 4 ++-- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/Type/Symfony/InputBagDynamicReturnTypeExtension.php b/src/Type/Symfony/InputBagDynamicReturnTypeExtension.php index f328ea86..b4875829 100644 --- a/src/Type/Symfony/InputBagDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/InputBagDynamicReturnTypeExtension.php @@ -70,18 +70,11 @@ private function getAllTypeFromMethodCall( MethodCall $methodCall ): Type { - $types = [ - new BooleanType(), - new FloatType(), - new IntegerType(), - new StringType(), - ]; - $oneParameterType = new UnionType($types); if (isset($methodCall->getArgs()[0])) { - return new ArrayType(new MixedType(), $oneParameterType); + return new ArrayType(new MixedType(), new MixedType(true)); } - return new ArrayType(new StringType(), new UnionType([new ArrayType(new MixedType(), $oneParameterType), new BooleanType(), new FloatType(), new IntegerType(), new StringType()])); + return new ArrayType(new StringType(), new UnionType([new ArrayType(new MixedType(), new MixedType(true)), new BooleanType(), new FloatType(), new IntegerType(), new StringType()])); } } diff --git a/tests/Type/Symfony/data/input_bag.php b/tests/Type/Symfony/data/input_bag.php index c99fe6b6..4c836c5f 100644 --- a/tests/Type/Symfony/data/input_bag.php +++ b/tests/Type/Symfony/data/input_bag.php @@ -8,5 +8,5 @@ assertType('bool|float|int|string|null', $bag->get('foo', null)); assertType('bool|float|int|string', $bag->get('foo', '')); assertType('bool|float|int|string', $bag->get('foo', 'baz')); -assertType('array|bool|float|int|string>', $bag->all()); -assertType('array', $bag->all('bar')); +assertType('array', $bag->all()); +assertType('array', $bag->all('bar')); From 197c4cf78e1568abf0f95593b561220072f69c65 Mon Sep 17 00:00:00 2001 From: Jeroen Noten Date: Mon, 29 Nov 2021 09:55:28 +0100 Subject: [PATCH 026/208] Fix false positive "Service ... is private." when using KernelTestCase::$container --- extension.neon | 2 ++ .../Bundle/FrameworkBundle/Test/KernelTestCase.stub | 11 +++++++++++ .../Bundle/FrameworkBundle/Test/TestContainer.stub | 7 +++++++ 3 files changed, 20 insertions(+) create mode 100644 stubs/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.stub create mode 100644 stubs/Symfony/Bundle/FrameworkBundle/Test/TestContainer.stub diff --git a/extension.neon b/extension.neon index 32ad05f4..4b36a06c 100644 --- a/extension.neon +++ b/extension.neon @@ -9,6 +9,8 @@ parameters: constant_hassers: true console_application_loader: null stubFiles: + - stubs/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.stub + - stubs/Symfony/Bundle/FrameworkBundle/Test/TestContainer.stub - stubs/Symfony/Component/Form/ChoiceList/Loader/ChoiceLoaderInterface.stub - stubs/Symfony/Component/DependencyInjection/ContainerBuilder.stub - stubs/Symfony/Component/DependencyInjection/Extension/ExtensionInterface.stub diff --git a/stubs/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.stub b/stubs/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.stub new file mode 100644 index 00000000..4b3d6b35 --- /dev/null +++ b/stubs/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.stub @@ -0,0 +1,11 @@ + Date: Mon, 29 Nov 2021 12:00:23 +0100 Subject: [PATCH 027/208] Services are private by default --- src/Symfony/XmlServiceMapFactory.php | 2 +- tests/Symfony/DefaultServiceMapTest.php | 22 +++++++++++----------- tests/Symfony/container.xml | 4 ++-- tests/Type/Symfony/container.xml | 4 ++-- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/Symfony/XmlServiceMapFactory.php b/src/Symfony/XmlServiceMapFactory.php index 47aa1bc9..409ef847 100644 --- a/src/Symfony/XmlServiceMapFactory.php +++ b/src/Symfony/XmlServiceMapFactory.php @@ -48,7 +48,7 @@ public function create(): ServiceMap $service = new Service( strpos((string) $attrs->id, '.') === 0 ? substr((string) $attrs->id, 1) : (string) $attrs->id, isset($attrs->class) ? (string) $attrs->class : null, - !isset($attrs->public) || (string) $attrs->public !== 'false', + isset($attrs->public) && (string) $attrs->public === 'true', isset($attrs->synthetic) && (string) $attrs->synthetic === 'true', isset($attrs->alias) ? (string) $attrs->alias : null ); diff --git a/tests/Symfony/DefaultServiceMapTest.php b/tests/Symfony/DefaultServiceMapTest.php index 35d6b35d..8616ffaf 100644 --- a/tests/Symfony/DefaultServiceMapTest.php +++ b/tests/Symfony/DefaultServiceMapTest.php @@ -42,7 +42,7 @@ function (?Service $service): void { self::assertNotNull($service); self::assertSame('withoutClass', $service->getId()); self::assertNull($service->getClass()); - self::assertTrue($service->isPublic()); + self::assertFalse($service->isPublic()); self::assertFalse($service->isSynthetic()); self::assertNull($service->getAlias()); }, @@ -53,7 +53,7 @@ function (?Service $service): void { self::assertNotNull($service); self::assertSame('withClass', $service->getId()); self::assertSame('Foo', $service->getClass()); - self::assertTrue($service->isPublic()); + self::assertFalse($service->isPublic()); self::assertFalse($service->isSynthetic()); self::assertNull($service->getAlias()); }, @@ -64,29 +64,29 @@ function (?Service $service): void { self::assertNotNull($service); self::assertSame('withoutPublic', $service->getId()); self::assertSame('Foo', $service->getClass()); - self::assertTrue($service->isPublic()); + self::assertFalse($service->isPublic()); self::assertFalse($service->isSynthetic()); self::assertNull($service->getAlias()); }, ]; yield [ - 'publicNotFalse', + 'publicNotTrue', function (?Service $service): void { self::assertNotNull($service); - self::assertSame('publicNotFalse', $service->getId()); + self::assertSame('publicNotTrue', $service->getId()); self::assertSame('Foo', $service->getClass()); - self::assertTrue($service->isPublic()); + self::assertFalse($service->isPublic()); self::assertFalse($service->isSynthetic()); self::assertNull($service->getAlias()); }, ]; yield [ - 'private', + 'public', function (?Service $service): void { self::assertNotNull($service); - self::assertSame('private', $service->getId()); + self::assertSame('public', $service->getId()); self::assertSame('Foo', $service->getClass()); - self::assertFalse($service->isPublic()); + self::assertTrue($service->isPublic()); self::assertFalse($service->isSynthetic()); self::assertNull($service->getAlias()); }, @@ -97,7 +97,7 @@ function (?Service $service): void { self::assertNotNull($service); self::assertSame('synthetic', $service->getId()); self::assertSame('Foo', $service->getClass()); - self::assertTrue($service->isPublic()); + self::assertFalse($service->isPublic()); self::assertTrue($service->isSynthetic()); self::assertNull($service->getAlias()); }, @@ -108,7 +108,7 @@ function (?Service $service): void { self::assertNotNull($service); self::assertSame('alias', $service->getId()); self::assertSame('Foo', $service->getClass()); - self::assertTrue($service->isPublic()); + self::assertFalse($service->isPublic()); self::assertFalse($service->isSynthetic()); self::assertSame('withClass', $service->getAlias()); }, diff --git a/tests/Symfony/container.xml b/tests/Symfony/container.xml index 5bed7715..4be3bd98 100644 --- a/tests/Symfony/container.xml +++ b/tests/Symfony/container.xml @@ -37,8 +37,8 @@ - - + + diff --git a/tests/Type/Symfony/container.xml b/tests/Type/Symfony/container.xml index 5bedede2..f4240c3b 100644 --- a/tests/Type/Symfony/container.xml +++ b/tests/Type/Symfony/container.xml @@ -37,7 +37,7 @@ - - + + From a5aed927c36088284267c6170b41fcb556e32a44 Mon Sep 17 00:00:00 2001 From: Alex Rock Ancelet Date: Mon, 29 Nov 2021 16:07:36 +0100 Subject: [PATCH 028/208] Followup of TestContainer issue --- .../Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.stub | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/stubs/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.stub b/stubs/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.stub index 4b3d6b35..88654f6c 100644 --- a/stubs/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.stub +++ b/stubs/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.stub @@ -8,4 +8,9 @@ abstract class KernelTestCase * @var TestContainer */ protected static $container; + + /** + * @return TestContainer + */ + abstract public function getContainer(); } From 84df52516771a2d8cf26b7b5b42a6dd4b1d26c2c Mon Sep 17 00:00:00 2001 From: Jeroen Noten Date: Wed, 15 Dec 2021 17:16:31 +0100 Subject: [PATCH 029/208] Fix false positive about private service in service subscriber --- .../ContainerInterfacePrivateServiceRule.php | 16 +++++++++++++++- tests/Rules/Symfony/ExampleServiceSubscriber.php | 10 ++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php b/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php index ee4d5b05..4c9a6af1 100644 --- a/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php +++ b/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php @@ -8,7 +8,9 @@ use PHPStan\Rules\Rule; use PHPStan\ShouldNotHappenException; use PHPStan\Symfony\ServiceMap; +use PHPStan\TrinaryLogic; use PHPStan\Type\ObjectType; +use PHPStan\Type\Type; use function sprintf; /** @@ -53,7 +55,7 @@ public function processNode(Node $node, Scope $scope): array $isTestContainerType = (new ObjectType('Symfony\Bundle\FrameworkBundle\Test\TestContainer'))->isSuperTypeOf($argType); $isOldServiceSubscriber = (new ObjectType('Symfony\Component\DependencyInjection\ServiceSubscriberInterface'))->isSuperTypeOf($argType); - $isServiceSubscriber = (new ObjectType('Symfony\Contracts\Service\ServiceSubscriberInterface'))->isSuperTypeOf($argType); + $isServiceSubscriber = $this->isServiceSubscriber($argType, $scope); $isServiceLocator = (new ObjectType('Symfony\Component\DependencyInjection\ServiceLocator'))->isSuperTypeOf($argType); if ($isTestContainerType->yes() || $isOldServiceSubscriber->yes() || $isServiceSubscriber->yes() || $isServiceLocator->yes()) { return []; @@ -83,4 +85,16 @@ public function processNode(Node $node, Scope $scope): array return []; } + private function isServiceSubscriber(Type $containerType, Scope $scope): TrinaryLogic + { + $serviceSubscriberInterfaceType = new ObjectType('Symfony\Contracts\Service\ServiceSubscriberInterface'); + $isContainerServiceSubscriber = $serviceSubscriberInterfaceType->isSuperTypeOf($containerType); + $classReflection = $scope->getClassReflection(); + if ($classReflection === null) { + return $isContainerServiceSubscriber; + } + $containedClassType = new ObjectType($classReflection->getName()); + return $isContainerServiceSubscriber->or($serviceSubscriberInterfaceType->isSuperTypeOf($containedClassType)); + } + } diff --git a/tests/Rules/Symfony/ExampleServiceSubscriber.php b/tests/Rules/Symfony/ExampleServiceSubscriber.php index 9f5968df..3d7f294d 100644 --- a/tests/Rules/Symfony/ExampleServiceSubscriber.php +++ b/tests/Rules/Symfony/ExampleServiceSubscriber.php @@ -2,14 +2,24 @@ namespace PHPStan\Rules\Symfony; +use Psr\Container\ContainerInterface; use Symfony\Contracts\Service\ServiceSubscriberInterface; final class ExampleServiceSubscriber implements ServiceSubscriberInterface { + /** @var ContainerInterface */ + private $locator; + + public function __construct(ContainerInterface $locator) + { + $this->locator = $locator; + } + public function privateService(): void { $this->get('private'); + $this->locator->get('private'); } public function containerParameter(): void From 8ab1d52efbf416304b0285176a38b8a01a085d09 Mon Sep 17 00:00:00 2001 From: Ruud Kamphuis Date: Thu, 16 Dec 2021 15:43:01 +0100 Subject: [PATCH 030/208] Test on PHP 8.1 --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 05fb9cb6..9c582d40 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -21,6 +21,7 @@ jobs: - "7.3" - "7.4" - "8.0" + - "8.1" steps: - name: "Checkout" From 749b0037ab9a478c3322208b30fc29c3f0eefbb1 Mon Sep 17 00:00:00 2001 From: Ruud Kamphuis Date: Thu, 16 Dec 2021 19:36:26 +0100 Subject: [PATCH 031/208] Composer > Remove `--no-suggest` You are using the deprecated option "--no-suggest". It has no effect and will break in Composer 3. --- .github/workflows/build.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9c582d40..76066c35 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -37,7 +37,7 @@ jobs: run: "composer validate" - name: "Install dependencies" - run: "composer install --no-interaction --no-progress --no-suggest" + run: "composer install --no-interaction --no-progress" - name: "Downgrade PHPUnit" if: matrix.php-version == '7.1' || matrix.php-version == '7.2' || matrix.php-version == '7.3' @@ -65,7 +65,7 @@ jobs: run: "composer validate" - name: "Install dependencies" - run: "composer install --no-interaction --no-progress --no-suggest" + run: "composer install --no-interaction --no-progress" - name: "Lint" run: "make lint" @@ -105,11 +105,11 @@ jobs: - name: "Install lowest dependencies" if: ${{ matrix.dependencies == 'lowest' }} - run: "composer update --prefer-lowest --no-interaction --no-progress --no-suggest" + run: "composer update --prefer-lowest --no-interaction --no-progress" - name: "Install highest dependencies" if: ${{ matrix.dependencies == 'highest' }} - run: "composer update --no-interaction --no-progress --no-suggest" + run: "composer update --no-interaction --no-progress" - name: "Downgrade PHPUnit" if: matrix.php-version == '7.1' || matrix.php-version == '7.2' || matrix.php-version == '7.3' @@ -149,11 +149,11 @@ jobs: - name: "Install lowest dependencies" if: ${{ matrix.dependencies == 'lowest' }} - run: "composer update --prefer-lowest --no-interaction --no-progress --no-suggest" + run: "composer update --prefer-lowest --no-interaction --no-progress" - name: "Install highest dependencies" if: ${{ matrix.dependencies == 'highest' }} - run: "composer update --no-interaction --no-progress --no-suggest" + run: "composer update --no-interaction --no-progress" - name: "Downgrade PHPUnit" if: matrix.php-version == '7.1' || matrix.php-version == '7.2' || matrix.php-version == '7.3' From 4140813ebfe7536222b67652fb82f870606ef204 Mon Sep 17 00:00:00 2001 From: Ruud Kamphuis Date: Thu, 16 Dec 2021 19:43:58 +0100 Subject: [PATCH 032/208] Test all jobs on PHP 8.1 --- .github/workflows/build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 76066c35..bccd397b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -86,6 +86,7 @@ jobs: - "7.3" - "7.4" - "8.0" + - "8.1" dependencies: - "lowest" - "highest" @@ -131,6 +132,7 @@ jobs: - "7.3" - "7.4" - "8.0" + - "8.1" dependencies: - "lowest" - "highest" From 19b0934a119c8ea60b2017313fae366e584dc739 Mon Sep 17 00:00:00 2001 From: Martin Herndl Date: Thu, 6 Jan 2022 13:34:42 +0100 Subject: [PATCH 033/208] Allow Composer plugins --- build-cs/composer.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/build-cs/composer.json b/build-cs/composer.json index ed7744e1..cc6a4983 100644 --- a/build-cs/composer.json +++ b/build-cs/composer.json @@ -3,5 +3,10 @@ "consistence-community/coding-standard": "^3.10", "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", "slevomat/coding-standard": "^6.4" + }, + "config": { + "allow-plugins": { + "dealerdirect/phpcodesniffer-composer-installer": true + } } } From 29ad8a949139ba8a5342cda60c62fea4d7afafce Mon Sep 17 00:00:00 2001 From: Michael Telgmann Date: Fri, 7 Jan 2022 22:35:38 +0100 Subject: [PATCH 034/208] Introduce DynamicReturnTypeExtension for 'Symfony\Component\Form\FormInterface::getErrors' fixes https://github.com/phpstan/phpstan-symfony/issues/166 --- composer.json | 1 + extension.neon | 5 ++ ...ormInterfaceDynamicReturnTypeExtension.php | 64 +++++++++++++++++++ tests/Type/Symfony/ExtensionTest.php | 2 + .../Symfony/data/FormInterface_getErrors.php | 20 ++++++ 5 files changed, 92 insertions(+) create mode 100644 src/Type/Symfony/Form/FormInterfaceDynamicReturnTypeExtension.php create mode 100644 tests/Type/Symfony/data/FormInterface_getErrors.php diff --git a/composer.json b/composer.json index 06adbfbf..3cddfd1b 100644 --- a/composer.json +++ b/composer.json @@ -28,6 +28,7 @@ "phpunit/phpunit": "^9.5", "symfony/config": "^4.2 || ^5.0", "symfony/console": "^4.0 || ^5.0", + "symfony/form": "^4.0 || ^5.0", "symfony/framework-bundle": "^4.4 || ^5.0", "symfony/http-foundation": "^4.0 || ^5.0", "symfony/messenger": "^4.2 || ^5.0", diff --git a/extension.neon b/extension.neon index 4b36a06c..47746a0b 100644 --- a/extension.neon +++ b/extension.neon @@ -249,3 +249,8 @@ services: class: PHPStan\Symfony\InputBagStubFilesExtension tags: - phpstan.stubFilesExtension + + # FormInterface::getErrors() return type + - + factory: PHPStan\Type\Symfony\Form\FormInterfaceDynamicReturnTypeExtension + tags: [phpstan.broker.dynamicMethodReturnTypeExtension] diff --git a/src/Type/Symfony/Form/FormInterfaceDynamicReturnTypeExtension.php b/src/Type/Symfony/Form/FormInterfaceDynamicReturnTypeExtension.php new file mode 100644 index 00000000..bdbc95ee --- /dev/null +++ b/src/Type/Symfony/Form/FormInterfaceDynamicReturnTypeExtension.php @@ -0,0 +1,64 @@ +getName() === 'getErrors'; + } + + public function getTypeFromMethodCall( + MethodReflection $methodReflection, + MethodCall $methodCall, + Scope $scope + ): Type + { + if (!isset($methodCall->getArgs()[1])) { + return new GenericObjectType(FormErrorIterator::class, [new ObjectType(FormError::class)]); + } + + $firstArgType = $scope->getType($methodCall->getArgs()[0]->value); + $secondArgType = $scope->getType($methodCall->getArgs()[1]->value); + + $firstIsTrueType = (new ConstantBooleanType(true))->isSuperTypeOf($firstArgType); + $firstIsFalseType = (new ConstantBooleanType(false))->isSuperTypeOf($firstArgType); + $secondIsTrueType = (new ConstantBooleanType(true))->isSuperTypeOf($secondArgType); + $secondIsFalseType = (new ConstantBooleanType(false))->isSuperTypeOf($secondArgType); + + $firstCompareType = $firstIsTrueType->compareTo($firstIsFalseType); + $secondCompareType = $secondIsTrueType->compareTo($secondIsFalseType); + + if ($firstCompareType === $firstIsTrueType && $secondCompareType === $secondIsFalseType) { + return new GenericObjectType(FormErrorIterator::class, [ + new UnionType([ + new ObjectType(FormError::class), + new ObjectType(FormErrorIterator::class), + ]), + ]); + } + + return new GenericObjectType(FormErrorIterator::class, [new ObjectType(FormError::class)]); + } + +} diff --git a/tests/Type/Symfony/ExtensionTest.php b/tests/Type/Symfony/ExtensionTest.php index 5a49366b..61d354da 100644 --- a/tests/Type/Symfony/ExtensionTest.php +++ b/tests/Type/Symfony/ExtensionTest.php @@ -49,6 +49,8 @@ public function dataFileAsserts(): iterable } yield from $this->gatherAssertTypes(__DIR__ . '/data/denormalizer.php'); + + yield from $this->gatherAssertTypes(__DIR__ . '/data/FormInterface_getErrors.php'); } /** diff --git a/tests/Type/Symfony/data/FormInterface_getErrors.php b/tests/Type/Symfony/data/FormInterface_getErrors.php new file mode 100644 index 00000000..a360a21d --- /dev/null +++ b/tests/Type/Symfony/data/FormInterface_getErrors.php @@ -0,0 +1,20 @@ +', $form->getErrors()); +assertType(FormErrorIterator::class . '<'. FormError::class .'>', $form->getErrors(false)); +assertType(FormErrorIterator::class . '<'. FormError::class .'>', $form->getErrors(false, true)); + +assertType(FormErrorIterator::class . '<'. FormError::class .'>', $form->getErrors(true)); +assertType(FormErrorIterator::class . '<'. FormError::class .'>', $form->getErrors(true, true)); + +assertType(FormErrorIterator::class . '<'. FormError::class .'>', $form->getErrors(false, false)); + +assertType(FormErrorIterator::class . '<'. FormError::class .'|'. FormErrorIterator::class . '>', $form->getErrors(true, false)); From 9b8b650619a1febd7261b59e5c8c9287755bcf02 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Thu, 30 Dec 2021 23:07:26 +0100 Subject: [PATCH 035/208] Fix FormView stub --- stubs/Symfony/Component/Form/FormView.stub | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stubs/Symfony/Component/Form/FormView.stub b/stubs/Symfony/Component/Form/FormView.stub index 8f920ed6..08c64752 100644 --- a/stubs/Symfony/Component/Form/FormView.stub +++ b/stubs/Symfony/Component/Form/FormView.stub @@ -6,8 +6,8 @@ use ArrayAccess; use IteratorAggregate; /** - * @implements IteratorAggregate - * @implements ArrayAccess + * @implements IteratorAggregate + * @implements ArrayAccess */ class FormView implements ArrayAccess, IteratorAggregate { @@ -15,7 +15,7 @@ class FormView implements ArrayAccess, IteratorAggregate /** * Returns an iterator to iterate over children (implements \IteratorAggregate). * - * @return \ArrayIterator The iterator + * @return \ArrayIterator The iterator */ public function getIterator(); From 9e985a975b1ccff52fd65cf54d1bd5c32b736de5 Mon Sep 17 00:00:00 2001 From: Anatoly Pashin Date: Wed, 8 Dec 2021 21:49:08 +1000 Subject: [PATCH 036/208] Add KernelBrowser stub Fixes #216 --- extension.neon | 1 + .../Bundle/FrameworkBundle/KernelBrowser.stub | 13 +++++++++++++ 2 files changed, 14 insertions(+) create mode 100644 stubs/Symfony/Bundle/FrameworkBundle/KernelBrowser.stub diff --git a/extension.neon b/extension.neon index 47746a0b..59d9f3f6 100644 --- a/extension.neon +++ b/extension.neon @@ -9,6 +9,7 @@ parameters: constant_hassers: true console_application_loader: null stubFiles: + - stubs/Symfony/Bundle/FrameworkBundle/KernelBrowser.stub - stubs/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.stub - stubs/Symfony/Bundle/FrameworkBundle/Test/TestContainer.stub - stubs/Symfony/Component/Form/ChoiceList/Loader/ChoiceLoaderInterface.stub diff --git a/stubs/Symfony/Bundle/FrameworkBundle/KernelBrowser.stub b/stubs/Symfony/Bundle/FrameworkBundle/KernelBrowser.stub new file mode 100644 index 00000000..ec54f1eb --- /dev/null +++ b/stubs/Symfony/Bundle/FrameworkBundle/KernelBrowser.stub @@ -0,0 +1,13 @@ + Date: Sat, 8 Jan 2022 19:51:27 +0100 Subject: [PATCH 037/208] Command::getHelper return type --- extension.neon | 7 ++ ...andGetHelperDynamicReturnTypeExtension.php | 71 +++++++++++++++++++ stubs/Symfony/Component/Console/Command.stub | 11 +++ .../Console/Helper/HelperInterface.stub | 7 ++ .../Type/Symfony/data/ExampleBaseCommand.php | 1 + 5 files changed, 97 insertions(+) create mode 100644 src/Type/Symfony/CommandGetHelperDynamicReturnTypeExtension.php create mode 100644 stubs/Symfony/Component/Console/Command.stub create mode 100644 stubs/Symfony/Component/Console/Helper/HelperInterface.stub diff --git a/extension.neon b/extension.neon index 59d9f3f6..aab376e8 100644 --- a/extension.neon +++ b/extension.neon @@ -12,6 +12,8 @@ parameters: - stubs/Symfony/Bundle/FrameworkBundle/KernelBrowser.stub - stubs/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.stub - stubs/Symfony/Bundle/FrameworkBundle/Test/TestContainer.stub + - stubs/Symfony/Component/Console/Command.stub + - stubs/Symfony/Component/Console/Helper/HelperInterface.stub - stubs/Symfony/Component/Form/ChoiceList/Loader/ChoiceLoaderInterface.stub - stubs/Symfony/Component/DependencyInjection/ContainerBuilder.stub - stubs/Symfony/Component/DependencyInjection/Extension/ExtensionInterface.stub @@ -255,3 +257,8 @@ services: - factory: PHPStan\Type\Symfony\Form\FormInterfaceDynamicReturnTypeExtension tags: [phpstan.broker.dynamicMethodReturnTypeExtension] + + # Command::getHelper() return type + - + factory: PHPStan\Type\Symfony\CommandGetHelperDynamicReturnTypeExtension + tags: [phpstan.broker.dynamicMethodReturnTypeExtension] diff --git a/src/Type/Symfony/CommandGetHelperDynamicReturnTypeExtension.php b/src/Type/Symfony/CommandGetHelperDynamicReturnTypeExtension.php new file mode 100644 index 00000000..e9b6ed7e --- /dev/null +++ b/src/Type/Symfony/CommandGetHelperDynamicReturnTypeExtension.php @@ -0,0 +1,71 @@ +consoleApplicationResolver = $consoleApplicationResolver; + } + + public function getClass(): string + { + return 'Symfony\Component\Console\Command\Command'; + } + + public function isMethodSupported(MethodReflection $methodReflection): bool + { + return $methodReflection->getName() === 'getHelper'; + } + + public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type + { + $defaultReturnType = new ObjectType('Symfony\Component\Console\Helper\HelperInterface'); + + if (!isset($methodCall->getArgs()[0])) { + return $defaultReturnType; + } + + $classReflection = $scope->getClassReflection(); + if ($classReflection === null) { + return $defaultReturnType; + } + + $argStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->getArgs()[0]->value)); + if (count($argStrings) !== 1) { + return $defaultReturnType; + } + $argName = $argStrings[0]->getValue(); + + $returnTypes = []; + foreach ($this->consoleApplicationResolver->findCommands($classReflection) as $command) { + try { + $command->mergeApplicationDefinition(); + $returnTypes[] = new ObjectType(get_class($command->getHelper($argName))); + } catch (Throwable $e) { + // no-op + } + } + + return count($returnTypes) > 0 ? TypeCombinator::union(...$returnTypes) : $defaultReturnType; + } + +} diff --git a/stubs/Symfony/Component/Console/Command.stub b/stubs/Symfony/Component/Console/Command.stub new file mode 100644 index 00000000..6bc2ea4b --- /dev/null +++ b/stubs/Symfony/Component/Console/Command.stub @@ -0,0 +1,11 @@ +|string', $input->getArgument('diff')); assertType('array', $input->getArgument('arr')); assertType('string|null', $input->getArgument('both')); + assertType('Symfony\Component\Console\Helper\QuestionHelper', $this->getHelper('question')); } } From 39ac41f418dd2df8da3b8d20aa1eb8bd4deedbcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Unger?= Date: Sun, 9 Jan 2022 09:14:41 +0100 Subject: [PATCH 038/208] Use camelCase config parameters (old ones still work) --- README.md | 10 +++--- extension.neon | 31 ++++++++++------- src/Symfony/Configuration.php | 34 +++++++++++++++++++ src/Symfony/ConsoleApplicationResolver.php | 3 +- src/Symfony/XmlParameterMapFactory.php | 4 +-- src/Symfony/XmlServiceMapFactory.php | 4 +-- .../ParameterDynamicReturnTypeExtension.php | 5 +-- .../ServiceDynamicReturnTypeExtension.php | 5 +-- ...nerInterfacePrivateServiceRuleFakeTest.php | 3 +- ...ntainerInterfacePrivateServiceRuleTest.php | 3 +- ...nerInterfaceUnknownServiceRuleFakeTest.php | 3 +- ...ntainerInterfaceUnknownServiceRuleTest.php | 3 +- .../Symfony/UndefinedArgumentRuleTest.php | 3 +- .../Rules/Symfony/UndefinedOptionRuleTest.php | 3 +- tests/Symfony/DefaultParameterMapTest.php | 4 +-- tests/Symfony/DefaultServiceMapTest.php | 4 +-- 16 files changed, 86 insertions(+), 36 deletions(-) create mode 100644 src/Symfony/Configuration.php diff --git a/README.md b/README.md index ef670e6b..7fe1a8bb 100644 --- a/README.md +++ b/README.md @@ -61,11 +61,11 @@ You have to provide a path to `srcDevDebugProjectContainer.xml` or similar XML f ```yaml parameters: symfony: - container_xml_path: var/cache/dev/srcDevDebugProjectContainer.xml + containerXmlPath: var/cache/dev/srcDevDebugProjectContainer.xml # or with Symfony 4.2+ - container_xml_path: var/cache/dev/srcApp_KernelDevDebugContainer.xml + containerXmlPath: var/cache/dev/srcApp_KernelDevDebugContainer.xml # or with Symfony 5+ - container_xml_path: var/cache/dev/App_KernelDevDebugContainer.xml + containerXmlPath: var/cache/dev/App_KernelDevDebugContainer.xml # If you're using PHP config files for Symfony 5.3+, you also need this for auto-loading of `Symfony\Config`: scanDirectories: - var/cache/dev/Symfony/Config @@ -86,7 +86,7 @@ In that case, you can disable the `::has()` method return type resolving like th ``` parameters: symfony: - constant_hassers: false + constantHassers: false ``` Be aware that it may hide genuine errors in your application. @@ -99,7 +99,7 @@ by providing the console application from your own application. This will allow ```neon parameters: symfony: - console_application_loader: tests/console-application.php + consoleApplicationLoader: tests/console-application.php ``` Symfony 4: diff --git a/extension.neon b/extension.neon index aab376e8..23008e5d 100644 --- a/extension.neon +++ b/extension.neon @@ -6,8 +6,11 @@ parameters: - 'Symfony\Component\Console\Exception\InvalidArgumentException' symfony: container_xml_path: null + containerXmlPath: null constant_hassers: true + constantHassers: true console_application_loader: null + consoleApplicationLoader: null stubFiles: - stubs/Symfony/Bundle/FrameworkBundle/KernelBrowser.stub - stubs/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.stub @@ -53,43 +56,47 @@ parameters: parametersSchema: symfony: structure([ container_xml_path: schema(string(), nullable()) + containerXmlPath: schema(string(), nullable()) constant_hassers: bool() + constantHassers: bool() console_application_loader: schema(string(), nullable()) + consoleApplicationLoader: schema(string(), nullable()) ]) services: + - + factory: PHPStan\Symfony\Configuration(%symfony%) + # console resolver - factory: PHPStan\Symfony\ConsoleApplicationResolver - arguments: - consoleApplicationLoader: %symfony.console_application_loader% # service map symfony.serviceMapFactory: class: PHPStan\Symfony\ServiceMapFactory - factory: PHPStan\Symfony\XmlServiceMapFactory(%symfony.container_xml_path%) + factory: PHPStan\Symfony\XmlServiceMapFactory - factory: @symfony.serviceMapFactory::create() # parameter map symfony.parameterMapFactory: class: PHPStan\Symfony\ParameterMapFactory - factory: PHPStan\Symfony\XmlParameterMapFactory(%symfony.container_xml_path%) + factory: PHPStan\Symfony\XmlParameterMapFactory - factory: @symfony.parameterMapFactory::create() # ControllerTrait::get()/has() return type - - factory: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Symfony\Component\DependencyInjection\ContainerInterface, %symfony.constant_hassers%) + factory: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Symfony\Component\DependencyInjection\ContainerInterface) tags: [phpstan.broker.dynamicMethodReturnTypeExtension] - - factory: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Psr\Container\ContainerInterface, %symfony.constant_hassers%) + factory: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Psr\Container\ContainerInterface) tags: [phpstan.broker.dynamicMethodReturnTypeExtension] - - factory: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Symfony\Bundle\FrameworkBundle\Controller\Controller, %symfony.constant_hassers%) + factory: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Symfony\Bundle\FrameworkBundle\Controller\Controller) tags: [phpstan.broker.dynamicMethodReturnTypeExtension] - - factory: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Symfony\Bundle\FrameworkBundle\Controller\AbstractController, %symfony.constant_hassers%) + factory: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Symfony\Bundle\FrameworkBundle\Controller\AbstractController) tags: [phpstan.broker.dynamicMethodReturnTypeExtension] # ControllerTrait::has() type specification @@ -233,20 +240,20 @@ services: # ParameterBagInterface::get()/has() return type - - factory: PHPStan\Type\Symfony\ParameterDynamicReturnTypeExtension(Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface, 'get', 'has', %symfony.constant_hassers%) + factory: PHPStan\Type\Symfony\ParameterDynamicReturnTypeExtension(Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface, 'get', 'has') tags: [phpstan.broker.dynamicMethodReturnTypeExtension] # ContainerInterface::getParameter()/hasParameter() return type - - factory: PHPStan\Type\Symfony\ParameterDynamicReturnTypeExtension(Symfony\Component\DependencyInjection\ContainerInterface, 'getParameter', 'hasParameter', %symfony.constant_hassers%) + factory: PHPStan\Type\Symfony\ParameterDynamicReturnTypeExtension(Symfony\Component\DependencyInjection\ContainerInterface, 'getParameter', 'hasParameter') tags: [phpstan.broker.dynamicMethodReturnTypeExtension] # (Abstract)Controller::getParameter() return type - - factory: PHPStan\Type\Symfony\ParameterDynamicReturnTypeExtension(Symfony\Bundle\FrameworkBundle\Controller\AbstractController, 'getParameter', null, %symfony.constant_hassers%) + factory: PHPStan\Type\Symfony\ParameterDynamicReturnTypeExtension(Symfony\Bundle\FrameworkBundle\Controller\AbstractController, 'getParameter', null) tags: [phpstan.broker.dynamicMethodReturnTypeExtension] - - factory: PHPStan\Type\Symfony\ParameterDynamicReturnTypeExtension(Symfony\Bundle\FrameworkBundle\Controller\Controller, 'getParameter', null, %symfony.constant_hassers%) + factory: PHPStan\Type\Symfony\ParameterDynamicReturnTypeExtension(Symfony\Bundle\FrameworkBundle\Controller\Controller, 'getParameter', null) tags: [phpstan.broker.dynamicMethodReturnTypeExtension] - class: PHPStan\Symfony\InputBagStubFilesExtension diff --git a/src/Symfony/Configuration.php b/src/Symfony/Configuration.php new file mode 100644 index 00000000..4c1f1a31 --- /dev/null +++ b/src/Symfony/Configuration.php @@ -0,0 +1,34 @@ + */ + private $parameters; + + /** + * @param array $parameters + */ + public function __construct(array $parameters) + { + $this->parameters = $parameters; + } + + public function getContainerXmlPath(): ?string + { + return $this->parameters['containerXmlPath'] ?? $this->parameters['container_xml_path'] ?? null; + } + + public function hasConstantHassers(): bool + { + return $this->parameters['constantHassers'] ?? $this->parameters['constant_hassers'] ?? true; + } + + public function getConsoleApplicationLoader(): ?string + { + return $this->parameters['consoleApplicationLoader'] ?? $this->parameters['console_application_loader'] ?? null; + } + +} diff --git a/src/Symfony/ConsoleApplicationResolver.php b/src/Symfony/ConsoleApplicationResolver.php index a493cbf0..3efc0d3f 100644 --- a/src/Symfony/ConsoleApplicationResolver.php +++ b/src/Symfony/ConsoleApplicationResolver.php @@ -16,8 +16,9 @@ final class ConsoleApplicationResolver /** @var \Symfony\Component\Console\Application|null */ private $consoleApplication; - public function __construct(?string $consoleApplicationLoader) + public function __construct(Configuration $configuration) { + $consoleApplicationLoader = $configuration->getConsoleApplicationLoader(); if ($consoleApplicationLoader === null) { return; } diff --git a/src/Symfony/XmlParameterMapFactory.php b/src/Symfony/XmlParameterMapFactory.php index 6a7575f4..9b34d14d 100644 --- a/src/Symfony/XmlParameterMapFactory.php +++ b/src/Symfony/XmlParameterMapFactory.php @@ -10,9 +10,9 @@ final class XmlParameterMapFactory implements ParameterMapFactory /** @var string|null */ private $containerXml; - public function __construct(?string $containerXml) + public function __construct(Configuration $configuration) { - $this->containerXml = $containerXml; + $this->containerXml = $configuration->getContainerXmlPath(); } public function create(): ParameterMap diff --git a/src/Symfony/XmlServiceMapFactory.php b/src/Symfony/XmlServiceMapFactory.php index 409ef847..60af1326 100644 --- a/src/Symfony/XmlServiceMapFactory.php +++ b/src/Symfony/XmlServiceMapFactory.php @@ -13,9 +13,9 @@ final class XmlServiceMapFactory implements ServiceMapFactory /** @var string|null */ private $containerXml; - public function __construct(?string $containerXml) + public function __construct(Configuration $configuration) { - $this->containerXml = $containerXml; + $this->containerXml = $configuration->getContainerXmlPath(); } public function create(): ServiceMap diff --git a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php index 8e79cf59..29b1a883 100644 --- a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php @@ -7,6 +7,7 @@ use PHPStan\Reflection\MethodReflection; use PHPStan\Reflection\ParametersAcceptorSelector; use PHPStan\ShouldNotHappenException; +use PHPStan\Symfony\Configuration; use PHPStan\Symfony\ParameterMap; use PHPStan\Type\ArrayType; use PHPStan\Type\BooleanType; @@ -43,12 +44,12 @@ final class ParameterDynamicReturnTypeExtension implements DynamicMethodReturnTy /** @var \PHPStan\Symfony\ParameterMap */ private $parameterMap; - public function __construct(string $className, ?string $methodGet, ?string $methodHas, bool $constantHassers, ParameterMap $symfonyParameterMap) + public function __construct(string $className, ?string $methodGet, ?string $methodHas, Configuration $configuration, ParameterMap $symfonyParameterMap) { $this->className = $className; $this->methodGet = $methodGet; $this->methodHas = $methodHas; - $this->constantHassers = $constantHassers; + $this->constantHassers = $configuration->hasConstantHassers(); $this->parameterMap = $symfonyParameterMap; } diff --git a/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php b/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php index 650aaa42..41c3a394 100644 --- a/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php @@ -7,6 +7,7 @@ use PHPStan\Reflection\MethodReflection; use PHPStan\Reflection\ParametersAcceptorSelector; use PHPStan\ShouldNotHappenException; +use PHPStan\Symfony\Configuration; use PHPStan\Symfony\ServiceMap; use PHPStan\Type\Constant\ConstantBooleanType; use PHPStan\Type\DynamicMethodReturnTypeExtension; @@ -26,10 +27,10 @@ final class ServiceDynamicReturnTypeExtension implements DynamicMethodReturnType /** @var \PHPStan\Symfony\ServiceMap */ private $serviceMap; - public function __construct(string $className, bool $constantHassers, ServiceMap $symfonyServiceMap) + public function __construct(string $className, Configuration $configuration, ServiceMap $symfonyServiceMap) { $this->className = $className; - $this->constantHassers = $constantHassers; + $this->constantHassers = $configuration->hasConstantHassers(); $this->serviceMap = $symfonyServiceMap; } diff --git a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleFakeTest.php b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleFakeTest.php index 81925291..23bdd4e6 100644 --- a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleFakeTest.php +++ b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleFakeTest.php @@ -3,6 +3,7 @@ namespace PHPStan\Rules\Symfony; use PHPStan\Rules\Rule; +use PHPStan\Symfony\Configuration; use PHPStan\Symfony\XmlServiceMapFactory; use PHPStan\Testing\RuleTestCase; @@ -14,7 +15,7 @@ final class ContainerInterfacePrivateServiceRuleFakeTest extends RuleTestCase protected function getRule(): Rule { - return new ContainerInterfacePrivateServiceRule((new XmlServiceMapFactory(null))->create()); + return new ContainerInterfacePrivateServiceRule((new XmlServiceMapFactory(new Configuration([])))->create()); } public function testGetPrivateService(): void diff --git a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php index b3a8fa31..c349b695 100644 --- a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php +++ b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php @@ -3,6 +3,7 @@ namespace PHPStan\Rules\Symfony; use PHPStan\Rules\Rule; +use PHPStan\Symfony\Configuration; use PHPStan\Symfony\XmlServiceMapFactory; use PHPStan\Testing\RuleTestCase; @@ -14,7 +15,7 @@ final class ContainerInterfacePrivateServiceRuleTest extends RuleTestCase protected function getRule(): Rule { - return new ContainerInterfacePrivateServiceRule((new XmlServiceMapFactory(__DIR__ . '/container.xml'))->create()); + return new ContainerInterfacePrivateServiceRule((new XmlServiceMapFactory(new Configuration(['containerXmlPath' => __DIR__ . '/container.xml'])))->create()); } public function testGetPrivateService(): void diff --git a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php index 33e3015d..46ea3b63 100644 --- a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php +++ b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php @@ -4,6 +4,7 @@ use PhpParser\PrettyPrinter\Standard; use PHPStan\Rules\Rule; +use PHPStan\Symfony\Configuration; use PHPStan\Symfony\XmlServiceMapFactory; use PHPStan\Testing\RuleTestCase; use PHPStan\Type\Symfony\ServiceTypeSpecifyingExtension; @@ -16,7 +17,7 @@ final class ContainerInterfaceUnknownServiceRuleFakeTest extends RuleTestCase protected function getRule(): Rule { - return new ContainerInterfaceUnknownServiceRule((new XmlServiceMapFactory(null))->create(), new Standard()); + return new ContainerInterfaceUnknownServiceRule((new XmlServiceMapFactory(new Configuration([])))->create(), new Standard()); } /** diff --git a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php index 297158a6..e433a233 100644 --- a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php +++ b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php @@ -4,6 +4,7 @@ use PhpParser\PrettyPrinter\Standard; use PHPStan\Rules\Rule; +use PHPStan\Symfony\Configuration; use PHPStan\Symfony\XmlServiceMapFactory; use PHPStan\Testing\RuleTestCase; @@ -15,7 +16,7 @@ final class ContainerInterfaceUnknownServiceRuleTest extends RuleTestCase protected function getRule(): Rule { - return new ContainerInterfaceUnknownServiceRule((new XmlServiceMapFactory(__DIR__ . '/container.xml'))->create(), new Standard()); + return new ContainerInterfaceUnknownServiceRule((new XmlServiceMapFactory(new Configuration(['containerXmlPath' => __DIR__ . '/container.xml'])))->create(), new Standard()); } public function testGetPrivateService(): void diff --git a/tests/Rules/Symfony/UndefinedArgumentRuleTest.php b/tests/Rules/Symfony/UndefinedArgumentRuleTest.php index b3f9e56e..95f4b733 100644 --- a/tests/Rules/Symfony/UndefinedArgumentRuleTest.php +++ b/tests/Rules/Symfony/UndefinedArgumentRuleTest.php @@ -4,6 +4,7 @@ use PhpParser\PrettyPrinter\Standard; use PHPStan\Rules\Rule; +use PHPStan\Symfony\Configuration; use PHPStan\Symfony\ConsoleApplicationResolver; use PHPStan\Testing\RuleTestCase; @@ -15,7 +16,7 @@ final class UndefinedArgumentRuleTest extends RuleTestCase protected function getRule(): Rule { - return new UndefinedArgumentRule(new ConsoleApplicationResolver(__DIR__ . '/console_application_loader.php'), new Standard()); + return new UndefinedArgumentRule(new ConsoleApplicationResolver(new Configuration(['consoleApplicationLoader' => __DIR__ . '/console_application_loader.php'])), new Standard()); } public function testGetArgument(): void diff --git a/tests/Rules/Symfony/UndefinedOptionRuleTest.php b/tests/Rules/Symfony/UndefinedOptionRuleTest.php index 32ebe773..b3cbe0ef 100644 --- a/tests/Rules/Symfony/UndefinedOptionRuleTest.php +++ b/tests/Rules/Symfony/UndefinedOptionRuleTest.php @@ -4,6 +4,7 @@ use PhpParser\PrettyPrinter\Standard; use PHPStan\Rules\Rule; +use PHPStan\Symfony\Configuration; use PHPStan\Symfony\ConsoleApplicationResolver; use PHPStan\Testing\RuleTestCase; @@ -15,7 +16,7 @@ final class UndefinedOptionRuleTest extends RuleTestCase protected function getRule(): Rule { - return new UndefinedOptionRule(new ConsoleApplicationResolver(__DIR__ . '/console_application_loader.php'), new Standard()); + return new UndefinedOptionRule(new ConsoleApplicationResolver(new Configuration(['consoleApplicationLoader' => __DIR__ . '/console_application_loader.php'])), new Standard()); } public function testGetArgument(): void diff --git a/tests/Symfony/DefaultParameterMapTest.php b/tests/Symfony/DefaultParameterMapTest.php index 0b97d078..caab6f97 100644 --- a/tests/Symfony/DefaultParameterMapTest.php +++ b/tests/Symfony/DefaultParameterMapTest.php @@ -13,13 +13,13 @@ final class DefaultParameterMapTest extends TestCase */ public function testGetParameter(string $key, callable $validator): void { - $factory = new XmlParameterMapFactory(__DIR__ . '/container.xml'); + $factory = new XmlParameterMapFactory(new Configuration(['containerXmlPath' => __DIR__ . '/container.xml'])); $validator($factory->create()->getParameter($key)); } public function testGetParameterEscapedPath(): void { - $factory = new XmlParameterMapFactory(__DIR__ . '/containers/bugfix%2Fcontainer.xml'); + $factory = new XmlParameterMapFactory(new Configuration(['containerXmlPath' => __DIR__ . '/containers/bugfix%2Fcontainer.xml'])); $serviceMap = $factory->create(); self::assertNotNull($serviceMap->getParameter('app.string')); diff --git a/tests/Symfony/DefaultServiceMapTest.php b/tests/Symfony/DefaultServiceMapTest.php index 8616ffaf..0ffbb457 100644 --- a/tests/Symfony/DefaultServiceMapTest.php +++ b/tests/Symfony/DefaultServiceMapTest.php @@ -13,13 +13,13 @@ final class DefaultServiceMapTest extends TestCase */ public function testGetService(string $id, callable $validator): void { - $factory = new XmlServiceMapFactory(__DIR__ . '/container.xml'); + $factory = new XmlServiceMapFactory(new Configuration(['containerXmlPath' => __DIR__ . '/container.xml'])); $validator($factory->create()->getService($id)); } public function testGetContainerEscapedPath(): void { - $factory = new XmlServiceMapFactory(__DIR__ . '/containers/bugfix%2Fcontainer.xml'); + $factory = new XmlServiceMapFactory(new Configuration(['containerXmlPath' => __DIR__ . '/containers/bugfix%2Fcontainer.xml'])); $serviceMap = $factory->create(); self::assertNotNull($serviceMap->getService('withClass')); From 5e9fc30ca533ba06886c0e08bf84171e063bf163 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Sun, 9 Jan 2022 13:18:25 +0100 Subject: [PATCH 039/208] Require PHPStan 1.4 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 3cddfd1b..001c0f78 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ "require": { "php": "^7.1 || ^8.0", "ext-simplexml": "*", - "phpstan/phpstan": "^1.0" + "phpstan/phpstan": "^1.4" }, "conflict": { "symfony/framework-bundle": "<3.0" From d779d1a5a1678827e56010b47dedf790a0a4baad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Unger?= Date: Mon, 10 Jan 2022 16:59:52 +0100 Subject: [PATCH 040/208] Better error for invalid consoleApplicationLoader setting --- src/Symfony/ConsoleApplicationResolver.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Symfony/ConsoleApplicationResolver.php b/src/Symfony/ConsoleApplicationResolver.php index 3efc0d3f..efee6292 100644 --- a/src/Symfony/ConsoleApplicationResolver.php +++ b/src/Symfony/ConsoleApplicationResolver.php @@ -9,6 +9,7 @@ use function file_exists; use function get_class; use function is_readable; +use function sprintf; final class ConsoleApplicationResolver { @@ -34,7 +35,7 @@ private function loadConsoleApplication(string $consoleApplicationLoader) if (!file_exists($consoleApplicationLoader) || !is_readable($consoleApplicationLoader) ) { - throw new ShouldNotHappenException(); + throw new ShouldNotHappenException(sprintf('Cannot load console application. Check the parameters.symfony.consoleApplicationLoader setting in PHPStan\'s config. The offending value is "%s".', $consoleApplicationLoader)); } return require $consoleApplicationLoader; From 99a1dc953f818e9939de37f4398f8b3be0baf9dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Mirtes?= Date: Sun, 16 Jan 2022 09:43:24 +0100 Subject: [PATCH 041/208] Tweet release action --- .github/workflows/release-tweet.yml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 .github/workflows/release-tweet.yml diff --git a/.github/workflows/release-tweet.yml b/.github/workflows/release-tweet.yml new file mode 100644 index 00000000..09b39ded --- /dev/null +++ b/.github/workflows/release-tweet.yml @@ -0,0 +1,24 @@ +name: Tweet release + +# More triggers +# https://docs.github.com/en/actions/learn-github-actions/events-that-trigger-workflows#release +on: + release: + types: [published] + +jobs: + tweet: + runs-on: ubuntu-latest + steps: + - uses: Eomm/why-don-t-you-tweet@v1 + if: ${{ !github.event.repository.private }} + with: + # GitHub event payload + # https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#release + tweet-message: "New release: ${{ github.event.repository.name }} ${{ github.event.release.tag_name }} ${{ github.event.release.html_url }} #phpstan" + env: + # Get your tokens from https://developer.twitter.com/apps + TWITTER_CONSUMER_API_KEY: ${{ secrets.TWITTER_CONSUMER_API_KEY }} + TWITTER_CONSUMER_API_SECRET: ${{ secrets.TWITTER_CONSUMER_API_SECRET }} + TWITTER_ACCESS_TOKEN: ${{ secrets.TWITTER_ACCESS_TOKEN }} + TWITTER_ACCESS_TOKEN_SECRET: ${{ secrets.TWITTER_ACCESS_TOKEN_SECRET }} From 012305dab7a08f4e1a7158e01d7b4bccfbc6fa31 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Sun, 16 Jan 2022 09:47:42 +0100 Subject: [PATCH 042/208] Request: Return Session instead of SessionInterface See https://github.com/phpstan/phpstan-symfony/pull/233 --- stubs/Symfony/Component/HttpFoundation/Request.stub | 5 +++++ tests/Type/Symfony/data/request_get_session.php | 6 +++--- tests/Type/Symfony/data/request_get_session_null.php | 6 +++--- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/stubs/Symfony/Component/HttpFoundation/Request.stub b/stubs/Symfony/Component/HttpFoundation/Request.stub index 00d03b09..f32365db 100644 --- a/stubs/Symfony/Component/HttpFoundation/Request.stub +++ b/stubs/Symfony/Component/HttpFoundation/Request.stub @@ -26,4 +26,9 @@ class Request */ public $cookies; + /** + * @return \Symfony\Component\HttpFoundation\Session\Session + */ + public function getSession(): SessionInterface; + } diff --git a/tests/Type/Symfony/data/request_get_session.php b/tests/Type/Symfony/data/request_get_session.php index 9a0335e5..17ef88a6 100644 --- a/tests/Type/Symfony/data/request_get_session.php +++ b/tests/Type/Symfony/data/request_get_session.php @@ -1,14 +1,14 @@ getSession(); -assertType(SessionInterface::class, $request->getSession()); +assertType(Session::class, $request->getSession()); if ($request->hasSession()) { - assertType(SessionInterface::class, $request->getSession()); + assertType(Session::class, $request->getSession()); } diff --git a/tests/Type/Symfony/data/request_get_session_null.php b/tests/Type/Symfony/data/request_get_session_null.php index 9c37979d..be0fd0d1 100644 --- a/tests/Type/Symfony/data/request_get_session_null.php +++ b/tests/Type/Symfony/data/request_get_session_null.php @@ -1,14 +1,14 @@ getSession(); -assertType(SessionInterface::class . '|null', $request->getSession()); +assertType(Session::class . '|null', $request->getSession()); if ($request->hasSession()) { - assertType(SessionInterface::class, $request->getSession()); + assertType(Session::class, $request->getSession()); } From 78565072185b9e507ab2af35115ae24bff3dcf20 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Sun, 16 Jan 2022 09:57:02 +0100 Subject: [PATCH 043/208] Fix --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 001c0f78..52ec4121 100644 --- a/composer.json +++ b/composer.json @@ -30,7 +30,7 @@ "symfony/console": "^4.0 || ^5.0", "symfony/form": "^4.0 || ^5.0", "symfony/framework-bundle": "^4.4 || ^5.0", - "symfony/http-foundation": "^4.0 || ^5.0", + "symfony/http-foundation": "^5.1", "symfony/messenger": "^4.2 || ^5.0", "symfony/serializer": "^4.0 || ^5.0" }, From 28c137977468bfc750d6796480fc1629f860088a Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Sun, 16 Jan 2022 10:00:07 +0100 Subject: [PATCH 044/208] PHP 8.0 polyfills for testing purposes --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index 52ec4121..08f5b685 100644 --- a/composer.json +++ b/composer.json @@ -32,6 +32,7 @@ "symfony/framework-bundle": "^4.4 || ^5.0", "symfony/http-foundation": "^5.1", "symfony/messenger": "^4.2 || ^5.0", + "symfony/polyfill-php80": "^1.24", "symfony/serializer": "^4.0 || ^5.0" }, "config": { From 2e25964784fb657dbd1723691ffe6d5bc0e195a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Mirtes?= Date: Sun, 16 Jan 2022 10:09:48 +0100 Subject: [PATCH 045/208] Update release.yml --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 225470a6..5ed11761 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,13 +20,13 @@ jobs: id: changelog uses: metcalfc/changelog-generator@v1.0.0 with: - myToken: ${{ secrets.GITHUB_TOKEN }} + myToken: ${{ secrets.PHPSTAN_BOT_TOKEN }} - name: "Create release" id: create-release uses: actions/create-release@v1 env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.PHPSTAN_BOT_TOKEN }} with: tag_name: ${{ github.ref }} release_name: ${{ github.ref }} From 96ee630098c49c1c25326ae51b027e469bfd2064 Mon Sep 17 00:00:00 2001 From: Mark Olree <55830693+YspMark@users.noreply.github.com> Date: Tue, 18 Jan 2022 17:08:22 +0100 Subject: [PATCH 046/208] Added CacheInterface stub --- extension.neon | 4 ++++ stubs/Psr/Cache/CacheItemInterface.stub | 7 ++++++ .../Contracts/Cache/CacheInterface.stub | 15 +++++++++++++ .../Contracts/Cache/CallbackInterface.stub | 16 ++++++++++++++ .../Contracts/Cache/ItemInterface.stub | 9 ++++++++ tests/Type/Symfony/ExtensionTest.php | 2 ++ tests/Type/Symfony/data/cache.php | 22 +++++++++++++++++++ 7 files changed, 75 insertions(+) create mode 100644 stubs/Psr/Cache/CacheItemInterface.stub create mode 100644 stubs/Symfony/Contracts/Cache/CacheInterface.stub create mode 100644 stubs/Symfony/Contracts/Cache/CallbackInterface.stub create mode 100644 stubs/Symfony/Contracts/Cache/ItemInterface.stub create mode 100644 tests/Type/Symfony/data/cache.php diff --git a/extension.neon b/extension.neon index 23008e5d..4870f24c 100644 --- a/extension.neon +++ b/extension.neon @@ -12,6 +12,7 @@ parameters: console_application_loader: null consoleApplicationLoader: null stubFiles: + - stubs/Psr/Cache/CacheItemInterface.stub - stubs/Symfony/Bundle/FrameworkBundle/KernelBrowser.stub - stubs/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.stub - stubs/Symfony/Bundle/FrameworkBundle/Test/TestContainer.stub @@ -51,6 +52,9 @@ parameters: - stubs/Symfony/Component/Validator/Constraint.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/Twig/Node/Node.stub parametersSchema: diff --git a/stubs/Psr/Cache/CacheItemInterface.stub b/stubs/Psr/Cache/CacheItemInterface.stub new file mode 100644 index 00000000..f595213d --- /dev/null +++ b/stubs/Psr/Cache/CacheItemInterface.stub @@ -0,0 +1,7 @@ +|callable(\Symfony\Contracts\Cache\ItemInterface): T $callback + * @param array $metadata + * @return T + */ + public function get(string $key, callable $callback, float $beta = null, array &$metadata = null); +} diff --git a/stubs/Symfony/Contracts/Cache/CallbackInterface.stub b/stubs/Symfony/Contracts/Cache/CallbackInterface.stub new file mode 100644 index 00000000..9b5a6e1a --- /dev/null +++ b/stubs/Symfony/Contracts/Cache/CallbackInterface.stub @@ -0,0 +1,16 @@ +gatherAssertTypes(__DIR__ . '/data/tree_builder.php'); + yield from $this->gatherAssertTypes(__DIR__ . '/data/ExampleBaseCommand.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/ExampleOptionCommand.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/ExampleOptionLazyCommand.php'); @@ -51,6 +52,7 @@ public function dataFileAsserts(): iterable yield from $this->gatherAssertTypes(__DIR__ . '/data/denormalizer.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/FormInterface_getErrors.php'); + yield from $this->gatherAssertTypes(__DIR__ . '/data/cache.php'); } /** diff --git a/tests/Type/Symfony/data/cache.php b/tests/Type/Symfony/data/cache.php new file mode 100644 index 00000000..6b0728d4 --- /dev/null +++ b/tests/Type/Symfony/data/cache.php @@ -0,0 +1,22 @@ +get('foo', function (): string { + return ''; + }); + + assertType('string', $result); +}; + +/** + * @param \Symfony\Contracts\Cache\CallbackInterface<\stdClass> $cb + */ + function testCacheCallbackInterface(\Symfony\Contracts\Cache\CacheInterface $cache, \Symfony\Contracts\Cache\CallbackInterface $cb): void { + $result = $cache->get('foo',$cb); + + assertType('stdClass', $result); +}; From b336d0aef4fdc4db6c44c455298d1130e1bb3df6 Mon Sep 17 00:00:00 2001 From: Thomas Gnandt Date: Mon, 24 Jan 2022 12:27:55 +0100 Subject: [PATCH 047/208] Add missing save parameter to CacheInterface stub --- stubs/Symfony/Contracts/Cache/CacheInterface.stub | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stubs/Symfony/Contracts/Cache/CacheInterface.stub b/stubs/Symfony/Contracts/Cache/CacheInterface.stub index 4b98f533..f4cc208d 100644 --- a/stubs/Symfony/Contracts/Cache/CacheInterface.stub +++ b/stubs/Symfony/Contracts/Cache/CacheInterface.stub @@ -7,7 +7,7 @@ interface CacheInterface /** * @template T * - * @param \Symfony\Contracts\Cache\CallbackInterface|callable(\Symfony\Contracts\Cache\ItemInterface): T $callback + * @param \Symfony\Contracts\Cache\CallbackInterface|callable(\Symfony\Contracts\Cache\ItemInterface, bool): T $callback * @param array $metadata * @return T */ From 784e7a06dcfadff7f79ec402402ee72e090245a8 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Sun, 30 Jan 2022 18:04:31 +0100 Subject: [PATCH 048/208] Update phpunit.xml --- phpunit.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpunit.xml b/phpunit.xml index f9f3afe2..2e2f6167 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -10,7 +10,7 @@ beStrictAboutTodoAnnotatedTests="true" failOnRisky="true" failOnWarning="true" - xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd" + xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xml" > From 11221afa283a3fa679a1678449fed0f5a81926b9 Mon Sep 17 00:00:00 2001 From: Fran Moreno Date: Tue, 1 Feb 2022 13:17:17 +0100 Subject: [PATCH 049/208] Add dynamic return type to ResponseHeaderBag::getCookies --- extension.neon | 5 ++ ...nseHeaderBagDynamicReturnTypeExtension.php | 65 +++++++++++++++++++ tests/Type/Symfony/ExtensionTest.php | 1 + .../data/response_header_bag_get_cookies.php | 12 ++++ 4 files changed, 83 insertions(+) create mode 100644 src/Type/Symfony/ResponseHeaderBagDynamicReturnTypeExtension.php create mode 100644 tests/Type/Symfony/data/response_header_bag_get_cookies.php diff --git a/extension.neon b/extension.neon index 4870f24c..cd18f378 100644 --- a/extension.neon +++ b/extension.neon @@ -273,3 +273,8 @@ services: - factory: PHPStan\Type\Symfony\CommandGetHelperDynamicReturnTypeExtension tags: [phpstan.broker.dynamicMethodReturnTypeExtension] + + # ResponseHeaderBag::getCookies() return type + - + factory: PHPStan\Type\Symfony\ResponseHeaderBagDynamicReturnTypeExtension + tags: [phpstan.broker.dynamicMethodReturnTypeExtension] diff --git a/src/Type/Symfony/ResponseHeaderBagDynamicReturnTypeExtension.php b/src/Type/Symfony/ResponseHeaderBagDynamicReturnTypeExtension.php new file mode 100644 index 00000000..a659c684 --- /dev/null +++ b/src/Type/Symfony/ResponseHeaderBagDynamicReturnTypeExtension.php @@ -0,0 +1,65 @@ +getName() === 'getCookies'; + } + + public function getTypeFromMethodCall( + MethodReflection $methodReflection, + MethodCall $methodCall, + Scope $scope + ): Type + { + if (isset($methodCall->getArgs()[0])) { + $node = $methodCall->getArgs()[0]->value; + + if ( + $node instanceof ClassConstFetch && + $node->class instanceof Name && + $node->name instanceof Identifier && + $node->class->toString() === ResponseHeaderBag::class && + $node->name->name === 'COOKIES_ARRAY' + ) { + return new ArrayType( + new StringType(), + new ArrayType( + new StringType(), + new ArrayType( + new StringType(), + new ObjectType(Cookie::class) + ) + ) + ); + } + } + + return new ArrayType(new IntegerType(), new ObjectType(Cookie::class)); + } + +} diff --git a/tests/Type/Symfony/ExtensionTest.php b/tests/Type/Symfony/ExtensionTest.php index 5da566b1..c0aa7f94 100644 --- a/tests/Type/Symfony/ExtensionTest.php +++ b/tests/Type/Symfony/ExtensionTest.php @@ -14,6 +14,7 @@ public function dataFileAsserts(): iterable { yield from $this->gatherAssertTypes(__DIR__ . '/data/envelope_all.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/header_bag_get.php'); + yield from $this->gatherAssertTypes(__DIR__ . '/data/response_header_bag_get_cookies.php'); if (class_exists('Symfony\Component\HttpFoundation\InputBag')) { yield from $this->gatherAssertTypes(__DIR__ . '/data/input_bag.php'); diff --git a/tests/Type/Symfony/data/response_header_bag_get_cookies.php b/tests/Type/Symfony/data/response_header_bag_get_cookies.php new file mode 100644 index 00000000..f07d5e5b --- /dev/null +++ b/tests/Type/Symfony/data/response_header_bag_get_cookies.php @@ -0,0 +1,12 @@ +setCookie(Cookie::create('cookie_name')); + +assertType('array', $headerBag->getCookies()); +assertType('array', $headerBag->getCookies(ResponseHeaderBag::COOKIES_FLAT)); +assertType('array>>', $headerBag->getCookies(ResponseHeaderBag::COOKIES_ARRAY)); From 26a44d1e960a1d2a70621e2efe6ae7b7fb5ea7bc Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Tue, 1 Feb 2022 23:53:55 +0000 Subject: [PATCH 050/208] Add renovate.json --- renovate.json | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 renovate.json diff --git a/renovate.json b/renovate.json new file mode 100644 index 00000000..f45d8f11 --- /dev/null +++ b/renovate.json @@ -0,0 +1,5 @@ +{ + "extends": [ + "config:base" + ] +} From 308ff0a9d33c98a50f43fa5ee3e099637ea7b755 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Mirtes?= Date: Wed, 2 Feb 2022 11:21:08 +0100 Subject: [PATCH 051/208] Update renovate.json --- renovate.json | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/renovate.json b/renovate.json index f45d8f11..597ad920 100644 --- a/renovate.json +++ b/renovate.json @@ -1,5 +1,23 @@ { "extends": [ "config:base" + ], + "rangeStrategy": "update-lockfile", + "packageRules": [ + { + "matchPaths": ["+(composer.json)"], + "enabled": true, + "groupName": "root-composer" + }, + { + "matchPaths": ["build-cs/**"], + "enabled": true, + "groupName": "build-cs" + }, + { + "matchPaths": [".github/**"], + "enabled": true, + "groupName": "github-actions" + } ] } From 35dcbe9bbb36b5d82540e1da7b1cb12e2b757bbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Mirtes?= Date: Wed, 2 Feb 2022 11:21:38 +0100 Subject: [PATCH 052/208] Rename renovate.json to .github/renovate.json --- renovate.json => .github/renovate.json | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename renovate.json => .github/renovate.json (100%) diff --git a/renovate.json b/.github/renovate.json similarity index 100% rename from renovate.json rename to .github/renovate.json From b31fa2ea20d5b91fad600fc6ae5c8936f702deb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Mirtes?= Date: Wed, 2 Feb 2022 11:21:47 +0100 Subject: [PATCH 053/208] Delete dependabot.yml --- .github/dependabot.yml | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 15b77335..00000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,17 +0,0 @@ -version: 2 -updates: - - package-ecosystem: composer - directory: "/" - schedule: - interval: monthly - open-pull-requests-limit: 10 - - package-ecosystem: composer - directory: "/build-cs" - schedule: - interval: monthly - open-pull-requests-limit: 10 - - package-ecosystem: github-actions - directory: "/" - schedule: - interval: monthly - open-pull-requests-limit: 10 From 1cb815f2cb6b49bec5ec04a8e9077c583aefac4b Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Wed, 2 Feb 2022 10:21:48 +0000 Subject: [PATCH 054/208] Update metcalfc/changelog-generator action to v1.0.1 --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5ed11761..24a1d301 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,7 +18,7 @@ jobs: - name: Generate changelog id: changelog - uses: metcalfc/changelog-generator@v1.0.0 + uses: metcalfc/changelog-generator@v1.0.1 with: myToken: ${{ secrets.PHPSTAN_BOT_TOKEN }} From ae9d31e6544dec1e91e821cef0d76a7a01dac368 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Wed, 2 Feb 2022 12:44:59 +0000 Subject: [PATCH 055/208] Update github-actions --- .github/workflows/lock-closed-issues.yml | 2 +- .github/workflows/release.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/lock-closed-issues.yml b/.github/workflows/lock-closed-issues.yml index 960c1ba5..9ea53272 100644 --- a/.github/workflows/lock-closed-issues.yml +++ b/.github/workflows/lock-closed-issues.yml @@ -8,7 +8,7 @@ jobs: lock: runs-on: ubuntu-latest steps: - - uses: dessant/lock-threads@v2 + - uses: dessant/lock-threads@v3 with: github-token: ${{ github.token }} issue-lock-inactive-days: '31' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 24a1d301..0ebed840 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,7 +18,7 @@ jobs: - name: Generate changelog id: changelog - uses: metcalfc/changelog-generator@v1.0.1 + uses: metcalfc/changelog-generator@v3.0.0 with: myToken: ${{ secrets.PHPSTAN_BOT_TOKEN }} From a3fe4eb01117b23a9b9ea4e987f1fc3e82ebf9cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Mirtes?= Date: Wed, 2 Feb 2022 15:43:18 +0100 Subject: [PATCH 056/208] Update lock-closed-issues.yml --- .github/workflows/lock-closed-issues.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/lock-closed-issues.yml b/.github/workflows/lock-closed-issues.yml index 9ea53272..a05d4173 100644 --- a/.github/workflows/lock-closed-issues.yml +++ b/.github/workflows/lock-closed-issues.yml @@ -11,11 +11,11 @@ jobs: - uses: dessant/lock-threads@v3 with: github-token: ${{ github.token }} - issue-lock-inactive-days: '31' - issue-exclude-created-before: '' - issue-exclude-labels: '' - issue-lock-labels: '' - issue-lock-comment: > + issue-inactive-days: '31' + exclude-issue-created-before: '' + exclude-any-issue-labels: '' + add-issue-labels: '' + issue-comment: > This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. From df5ce326a8935bf400eb59f7080e568c20ef6246 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Mirtes?= Date: Wed, 2 Feb 2022 16:35:49 +0100 Subject: [PATCH 057/208] Update renovate.json --- .github/renovate.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/renovate.json b/.github/renovate.json index 597ad920..b775cc18 100644 --- a/.github/renovate.json +++ b/.github/renovate.json @@ -1,6 +1,7 @@ { "extends": [ - "config:base" + "config:base", + "schedule:weekly" ], "rangeStrategy": "update-lockfile", "packageRules": [ From 173610b6eac80bb0b63eee2b181bd6f67bb3aad8 Mon Sep 17 00:00:00 2001 From: Fran Moreno Date: Wed, 2 Feb 2022 20:26:32 +0100 Subject: [PATCH 058/208] Specify type after calling InputBag::has --- extension.neon | 5 ++ .../InputBagTypeSpecifyingExtension.php | 68 +++++++++++++++++++ tests/Type/Symfony/data/input_bag.php | 6 ++ 3 files changed, 79 insertions(+) create mode 100644 src/Type/Symfony/InputBagTypeSpecifyingExtension.php diff --git a/extension.neon b/extension.neon index cd18f378..1aba591e 100644 --- a/extension.neon +++ b/extension.neon @@ -278,3 +278,8 @@ services: - factory: PHPStan\Type\Symfony\ResponseHeaderBagDynamicReturnTypeExtension tags: [phpstan.broker.dynamicMethodReturnTypeExtension] + + # InputBag::get() type specification + - + factory: PHPStan\Type\Symfony\InputBagTypeSpecifyingExtension + tags: [phpstan.typeSpecifier.methodTypeSpecifyingExtension] diff --git a/src/Type/Symfony/InputBagTypeSpecifyingExtension.php b/src/Type/Symfony/InputBagTypeSpecifyingExtension.php new file mode 100644 index 00000000..bb3d5b28 --- /dev/null +++ b/src/Type/Symfony/InputBagTypeSpecifyingExtension.php @@ -0,0 +1,68 @@ +reflectionProvider = $reflectionProvider; + } + + public function getClass(): string + { + return self::INPUT_BAG_CLASS; + } + + public function isMethodSupported(MethodReflection $methodReflection, MethodCall $node, TypeSpecifierContext $context): bool + { + return $methodReflection->getName() === self::HAS_METHOD_NAME && !$context->null(); + } + + public function specifyTypes(MethodReflection $methodReflection, MethodCall $node, Scope $scope, TypeSpecifierContext $context): SpecifiedTypes + { + $classReflection = $this->reflectionProvider->getClass(self::INPUT_BAG_CLASS); + $methodVariants = $classReflection->getNativeMethod(self::GET_METHOD_NAME)->getVariants(); + $returnType = ParametersAcceptorSelector::selectSingle($methodVariants)->getReturnType(); + + if (!TypeCombinator::containsNull($returnType)) { + return new SpecifiedTypes(); + } + + return $this->typeSpecifier->create( + new MethodCall($node->var, self::GET_METHOD_NAME, $node->getArgs()), + TypeCombinator::removeNull($returnType), + $context + ); + } + + public function setTypeSpecifier(TypeSpecifier $typeSpecifier): void + { + $this->typeSpecifier = $typeSpecifier; + } + +} diff --git a/tests/Type/Symfony/data/input_bag.php b/tests/Type/Symfony/data/input_bag.php index 4c836c5f..5420e789 100644 --- a/tests/Type/Symfony/data/input_bag.php +++ b/tests/Type/Symfony/data/input_bag.php @@ -5,6 +5,12 @@ $bag = new \Symfony\Component\HttpFoundation\InputBag(['foo' => 'bar', 'bar' => ['x']]); assertType('bool|float|int|string|null', $bag->get('foo')); + +if ($bag->has('foo')) { + assertType('bool|float|int|string', $bag->get('foo')); + assertType('bool|float|int|string|null', $bag->get('bar')); +} + assertType('bool|float|int|string|null', $bag->get('foo', null)); assertType('bool|float|int|string', $bag->get('foo', '')); assertType('bool|float|int|string', $bag->get('foo', 'baz')); From 51488c9c3000a07db3e543bc6e8f2de9c0a9037d Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Thu, 3 Feb 2022 12:35:02 +0100 Subject: [PATCH 059/208] Add support for env processor --- .../ParameterDynamicReturnTypeExtension.php | 51 ++++++++++++++++++- tests/Type/Symfony/container.xml | 13 +++++ .../data/ExampleAbstractController.php | 11 ++++ ...mpleAbstractControllerWithoutContainer.php | 15 ++++++ tests/Type/Symfony/data/ExampleController.php | 15 ++++++ .../ExampleControllerWithoutContainer.php | 15 ++++++ 6 files changed, 118 insertions(+), 2 deletions(-) diff --git a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php index 29b1a883..0de91de1 100644 --- a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php @@ -4,6 +4,7 @@ use PhpParser\Node\Expr\MethodCall; use PHPStan\Analyser\Scope; +use PHPStan\PhpDoc\TypeStringResolver; use PHPStan\Reflection\MethodReflection; use PHPStan\Reflection\ParametersAcceptorSelector; use PHPStan\ShouldNotHappenException; @@ -22,8 +23,10 @@ use PHPStan\Type\NullType; use PHPStan\Type\StringType; use PHPStan\Type\Type; +use PHPStan\Type\TypeCombinator; use PHPStan\Type\TypeTraverser; use PHPStan\Type\UnionType; +use Symfony\Component\DependencyInjection\EnvVarProcessor; use function in_array; final class ParameterDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension @@ -44,13 +47,24 @@ final class ParameterDynamicReturnTypeExtension implements DynamicMethodReturnTy /** @var \PHPStan\Symfony\ParameterMap */ private $parameterMap; - public function __construct(string $className, ?string $methodGet, ?string $methodHas, Configuration $configuration, ParameterMap $symfonyParameterMap) + /** @var \PHPStan\PhpDoc\TypeStringResolver */ + private $typeStringResolver; + + public function __construct( + string $className, + ?string $methodGet, + ?string $methodHas, + Configuration $configuration, + ParameterMap $symfonyParameterMap, + TypeStringResolver $typeStringResolver + ) { $this->className = $className; $this->methodGet = $methodGet; $this->methodHas = $methodHas; $this->constantHassers = $configuration->hasConstantHassers(); $this->parameterMap = $symfonyParameterMap; + $this->typeStringResolver = $typeStringResolver; } public function getClass(): string @@ -102,13 +116,46 @@ private function getGetTypeFromMethodCall( if ($parameterKey !== null) { $parameter = $this->parameterMap->getParameter($parameterKey); if ($parameter !== null) { - return $this->generalizeType($scope->getTypeFromValue($parameter->getValue())); + return $this->generalizeTypeFromValue($scope, $parameter->getValue()); } } return $returnType; } + /** + * @param Scope $scope + * @param array|bool|float|int|string $value + */ + private function generalizeTypeFromValue(Scope $scope, $value): Type + { + if (is_array($value) && $value !== []) { + return $this->generalizeType( + new ArrayType( + TypeCombinator::union(...array_map(function ($item) use ($scope): Type { + return $this->generalizeTypeFromValue($scope, $item); + }, array_keys($value))), + TypeCombinator::union(...array_map(function ($item) use ($scope): Type { + return $this->generalizeTypeFromValue($scope, $item); + }, array_values($value))) + ) + ); + } + + if ( + class_exists(EnvVarProcessor::class) + && is_string($value) + && preg_match('/%env\((.*)\:.*\)%/U', $value, $matches) === 1 + && strlen($matches[0]) === strlen($value) + ) { + $providedTypes = EnvVarProcessor::getProvidedTypes(); + + return $this->typeStringResolver->resolve($providedTypes[$matches[1]] ?? 'bool|int|float|string|array'); + } + + return $this->generalizeType($scope->getTypeFromValue($value)); + } + private function generalizeType(Type $type): Type { return TypeTraverser::map($type, function (Type $type, callable $traverse): Type { diff --git a/tests/Type/Symfony/container.xml b/tests/Type/Symfony/container.xml index f4240c3b..e6f8f03b 100644 --- a/tests/Type/Symfony/container.xml +++ b/tests/Type/Symfony/container.xml @@ -4,15 +4,28 @@ abcdef 123 123 + %env(int:APP_INT)% 123.45 123.45 + %env(float:APP_FLOAT)% true true + %env(bool:APP_BOOL)% en es fr + + 123 + 456 + 789 + + + %env(int:APP_INT)% + %env(int:APP_INT)% + %env(int:APP_INT)% + the name diff --git a/tests/Type/Symfony/data/ExampleAbstractController.php b/tests/Type/Symfony/data/ExampleAbstractController.php index 4dbaffa7..6dc6ef7e 100644 --- a/tests/Type/Symfony/data/ExampleAbstractController.php +++ b/tests/Type/Symfony/data/ExampleAbstractController.php @@ -39,6 +39,9 @@ public function parameters(ContainerInterface $container, ParameterBagInterface assertType("string", $container->getParameter('app.int_as_string')); assertType("string", $parameterBag->get('app.int_as_string')); assertType("string", $this->getParameter('app.int_as_string')); + assertType('int', $container->getParameter('app.int_as_processor')); + assertType('int', $parameterBag->get('app.int_as_processor')); + assertType('int', $this->getParameter('app.int_as_processor')); assertType('float', $container->getParameter('app.float')); assertType('float', $parameterBag->get('app.float')); assertType('float', $this->getParameter('app.float')); @@ -54,6 +57,12 @@ public function parameters(ContainerInterface $container, ParameterBagInterface assertType("array", $container->getParameter('app.list')); assertType("array", $parameterBag->get('app.list')); assertType("array", $this->getParameter('app.list')); + assertType("array", $container->getParameter('app.list_of_int')); + assertType("array", $parameterBag->get('app.list_of_int')); + assertType("array", $this->getParameter('app.list_of_int')); + assertType("array", $container->getParameter('app.list_of_int_as_processor')); + assertType("array", $parameterBag->get('app.list_of_int_as_processor')); + assertType("array", $this->getParameter('app.list_of_int_as_processor')); assertType("array>", $container->getParameter('app.list_of_list')); assertType("array>", $parameterBag->get('app.list_of_list')); assertType("array>", $this->getParameter('app.list_of_list')); @@ -77,6 +86,8 @@ public function parameters(ContainerInterface $container, ParameterBagInterface assertType('true', $parameterBag->has('app.int')); assertType('true', $container->hasParameter('app.int_as_string')); assertType('true', $parameterBag->has('app.int_as_string')); + assertType('true', $container->hasParameter('app.int_as_processor')); + assertType('true', $parameterBag->has('app.int_as_processor')); assertType('true', $container->hasParameter('app.float')); assertType('true', $parameterBag->has('app.float')); assertType('true', $container->hasParameter('app.float_as_string')); diff --git a/tests/Type/Symfony/data/ExampleAbstractControllerWithoutContainer.php b/tests/Type/Symfony/data/ExampleAbstractControllerWithoutContainer.php index 7ca06775..26d0f366 100644 --- a/tests/Type/Symfony/data/ExampleAbstractControllerWithoutContainer.php +++ b/tests/Type/Symfony/data/ExampleAbstractControllerWithoutContainer.php @@ -38,18 +38,27 @@ public function parameters(ContainerInterface $container, ParameterBagInterface assertType('array|bool|float|int|string|null', $container->getParameter('app.int_as_string')); assertType('array|bool|float|int|string|null', $parameterBag->get('app.int_as_string')); assertType('array|bool|float|int|string|null', $this->getParameter('app.int_as_string')); + assertType('array|bool|float|int|string|null', $container->getParameter('app.int_as_processor')); + assertType('array|bool|float|int|string|null', $parameterBag->get('app.int_as_processor')); + assertType('array|bool|float|int|string|null', $this->getParameter('app.int_as_processor')); assertType('array|bool|float|int|string|null', $container->getParameter('app.float')); assertType('array|bool|float|int|string|null', $parameterBag->get('app.float')); assertType('array|bool|float|int|string|null', $this->getParameter('app.float')); assertType('array|bool|float|int|string|null', $container->getParameter('app.float_as_string')); assertType('array|bool|float|int|string|null', $parameterBag->get('app.float_as_string')); assertType('array|bool|float|int|string|null', $this->getParameter('app.float_as_string')); + assertType('array|bool|float|int|string|null', $container->getParameter('app.float_as_processor')); + assertType('array|bool|float|int|string|null', $parameterBag->get('app.float_as_processor')); + assertType('array|bool|float|int|string|null', $this->getParameter('app.float_as_processor')); assertType('array|bool|float|int|string|null', $container->getParameter('app.boolean')); assertType('array|bool|float|int|string|null', $parameterBag->get('app.boolean')); assertType('array|bool|float|int|string|null', $this->getParameter('app.boolean')); assertType('array|bool|float|int|string|null', $container->getParameter('app.boolean_as_string')); assertType('array|bool|float|int|string|null', $parameterBag->get('app.boolean_as_string')); assertType('array|bool|float|int|string|null', $this->getParameter('app.boolean_as_string')); + assertType('array|bool|float|int|string|null', $container->getParameter('app.boolean_as_processor')); + assertType('array|bool|float|int|string|null', $parameterBag->get('app.boolean_as_processor')); + assertType('array|bool|float|int|string|null', $this->getParameter('app.boolean_as_processor')); assertType('array|bool|float|int|string|null', $container->getParameter('app.list')); assertType('array|bool|float|int|string|null', $parameterBag->get('app.list')); assertType('array|bool|float|int|string|null', $this->getParameter('app.list')); @@ -74,14 +83,20 @@ public function parameters(ContainerInterface $container, ParameterBagInterface assertType('bool', $parameterBag->has('app.int')); assertType('bool', $container->hasParameter('app.int_as_string')); assertType('bool', $parameterBag->has('app.int_as_string')); + assertType('bool', $container->hasParameter('app.int_as_processor')); + assertType('bool', $parameterBag->has('app.int_as_processor')); assertType('bool', $container->hasParameter('app.float')); assertType('bool', $parameterBag->has('app.float')); assertType('bool', $container->hasParameter('app.float_as_string')); assertType('bool', $parameterBag->has('app.float_as_string')); + assertType('bool', $container->hasParameter('app.float_as_processor')); + assertType('bool', $parameterBag->has('app.float_as_processor')); assertType('bool', $container->hasParameter('app.boolean')); assertType('bool', $parameterBag->has('app.boolean')); assertType('bool', $container->hasParameter('app.boolean_as_string')); assertType('bool', $parameterBag->has('app.boolean_as_string')); + assertType('bool', $container->hasParameter('app.boolean_as_processor')); + assertType('bool', $parameterBag->has('app.boolean_as_processor')); assertType('bool', $container->hasParameter('app.list')); assertType('bool', $parameterBag->has('app.list')); assertType('bool', $container->hasParameter('app.list_of_list')); diff --git a/tests/Type/Symfony/data/ExampleController.php b/tests/Type/Symfony/data/ExampleController.php index 1fe8c983..8cd0c93a 100644 --- a/tests/Type/Symfony/data/ExampleController.php +++ b/tests/Type/Symfony/data/ExampleController.php @@ -39,18 +39,27 @@ public function parameters(ContainerInterface $container, ParameterBagInterface assertType("string", $container->getParameter('app.int_as_string')); assertType("string", $parameterBag->get('app.int_as_string')); assertType("string", $this->getParameter('app.int_as_string')); + assertType('int', $container->getParameter('app.int_as_processor')); + assertType('int', $parameterBag->get('app.int_as_processor')); + assertType('int', $this->getParameter('app.int_as_processor')); assertType('float', $container->getParameter('app.float')); assertType('float', $parameterBag->get('app.float')); assertType('float', $this->getParameter('app.float')); assertType("string", $container->getParameter('app.float_as_string')); assertType("string", $parameterBag->get('app.float_as_string')); assertType("string", $this->getParameter('app.float_as_string')); + assertType('float', $container->getParameter('app.float_as_processor')); + assertType('float', $parameterBag->get('app.float_as_processor')); + assertType('float', $this->getParameter('app.float_as_processor')); assertType('bool', $container->getParameter('app.boolean')); assertType('bool', $parameterBag->get('app.boolean')); assertType('bool', $this->getParameter('app.boolean')); assertType("string", $container->getParameter('app.boolean_as_string')); assertType("string", $parameterBag->get('app.boolean_as_string')); assertType("string", $this->getParameter('app.boolean_as_string')); + assertType('bool', $container->getParameter('app.boolean_as_processor')); + assertType('bool', $parameterBag->get('app.boolean_as_processor')); + assertType('bool', $this->getParameter('app.boolean_as_processor')); assertType("array", $container->getParameter('app.list')); assertType("array", $parameterBag->get('app.list')); assertType("array", $this->getParameter('app.list')); @@ -75,14 +84,20 @@ public function parameters(ContainerInterface $container, ParameterBagInterface assertType('true', $parameterBag->has('app.int')); assertType('true', $container->hasParameter('app.int_as_string')); assertType('true', $parameterBag->has('app.int_as_string')); + assertType('true', $container->hasParameter('app.int_as_processor')); + assertType('true', $parameterBag->has('app.int_as_processor')); assertType('true', $container->hasParameter('app.float')); assertType('true', $parameterBag->has('app.float')); assertType('true', $container->hasParameter('app.float_as_string')); assertType('true', $parameterBag->has('app.float_as_string')); + assertType('true', $container->hasParameter('app.float_as_processor')); + assertType('true', $parameterBag->has('app.float_as_processor')); assertType('true', $container->hasParameter('app.boolean')); assertType('true', $parameterBag->has('app.boolean')); assertType('true', $container->hasParameter('app.boolean_as_string')); assertType('true', $parameterBag->has('app.boolean_as_string')); + assertType('true', $container->hasParameter('app.boolean_as_processor')); + assertType('true', $parameterBag->has('app.boolean_as_processor')); assertType('true', $container->hasParameter('app.list')); assertType('true', $parameterBag->has('app.list')); assertType('true', $container->hasParameter('app.list_of_list')); diff --git a/tests/Type/Symfony/data/ExampleControllerWithoutContainer.php b/tests/Type/Symfony/data/ExampleControllerWithoutContainer.php index 2e9b7ba2..fc48c0e5 100644 --- a/tests/Type/Symfony/data/ExampleControllerWithoutContainer.php +++ b/tests/Type/Symfony/data/ExampleControllerWithoutContainer.php @@ -38,18 +38,27 @@ public function parameters(ContainerInterface $container, ParameterBagInterface assertType('array|bool|float|int|string|null', $container->getParameter('app.int_as_string')); assertType('array|bool|float|int|string|null', $parameterBag->get('app.int_as_string')); assertType('array|bool|float|int|string|null', $this->getParameter('app.int_as_string')); + assertType('array|bool|float|int|string|null', $container->getParameter('app.int_as_processor')); + assertType('array|bool|float|int|string|null', $parameterBag->get('app.int_as_processor')); + assertType('array|bool|float|int|string|null', $this->getParameter('app.int_as_processor')); assertType('array|bool|float|int|string|null', $container->getParameter('app.float')); assertType('array|bool|float|int|string|null', $parameterBag->get('app.float')); assertType('array|bool|float|int|string|null', $this->getParameter('app.float')); assertType('array|bool|float|int|string|null', $container->getParameter('app.float_as_string')); assertType('array|bool|float|int|string|null', $parameterBag->get('app.float_as_string')); assertType('array|bool|float|int|string|null', $this->getParameter('app.float_as_string')); + assertType('array|bool|float|int|string|null', $container->getParameter('app.float_as_processor')); + assertType('array|bool|float|int|string|null', $parameterBag->get('app.float_as_processor')); + assertType('array|bool|float|int|string|null', $this->getParameter('app.float_as_processor')); assertType('array|bool|float|int|string|null', $container->getParameter('app.boolean')); assertType('array|bool|float|int|string|null', $parameterBag->get('app.boolean')); assertType('array|bool|float|int|string|null', $this->getParameter('app.boolean')); assertType('array|bool|float|int|string|null', $container->getParameter('app.boolean_as_string')); assertType('array|bool|float|int|string|null', $parameterBag->get('app.boolean_as_string')); assertType('array|bool|float|int|string|null', $this->getParameter('app.boolean_as_string')); + assertType('array|bool|float|int|string|null', $container->getParameter('app.boolean_as_processor')); + assertType('array|bool|float|int|string|null', $parameterBag->get('app.boolean_as_processor')); + assertType('array|bool|float|int|string|null', $this->getParameter('app.boolean_as_processor')); assertType('array|bool|float|int|string|null', $container->getParameter('app.list')); assertType('array|bool|float|int|string|null', $parameterBag->get('app.list')); assertType('array|bool|float|int|string|null', $this->getParameter('app.list')); @@ -74,14 +83,20 @@ public function parameters(ContainerInterface $container, ParameterBagInterface assertType('bool', $parameterBag->has('app.int')); assertType('bool', $container->hasParameter('app.int_as_string')); assertType('bool', $parameterBag->has('app.int_as_string')); + assertType('bool', $container->hasParameter('app.int_as_processor')); + assertType('bool', $parameterBag->has('app.int_as_processor')); assertType('bool', $container->hasParameter('app.float')); assertType('bool', $parameterBag->has('app.float')); assertType('bool', $container->hasParameter('app.float_as_string')); assertType('bool', $parameterBag->has('app.float_as_string')); + assertType('bool', $container->hasParameter('app.float_as_processor')); + assertType('bool', $parameterBag->has('app.float_as_processor')); assertType('bool', $container->hasParameter('app.boolean')); assertType('bool', $parameterBag->has('app.boolean')); assertType('bool', $container->hasParameter('app.boolean_as_string')); assertType('bool', $parameterBag->has('app.boolean_as_string')); + assertType('bool', $container->hasParameter('app.boolean_as_processor')); + assertType('bool', $parameterBag->has('app.boolean_as_processor')); assertType('bool', $container->hasParameter('app.list')); assertType('bool', $parameterBag->has('app.list')); assertType('bool', $container->hasParameter('app.list_of_list')); From 4e20d2e6b65a6eb2d0b8ff285c6c8c049faff03d Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Sat, 5 Feb 2022 12:34:02 +0100 Subject: [PATCH 060/208] Forward compatibility fix for extensions of generic methods --- .../InputBagTypeSpecifyingExtension.php | 24 +++---------------- .../RequestTypeSpecifyingExtension.php | 14 ++--------- tests/Type/Symfony/data/input_bag.php | 3 +++ 3 files changed, 8 insertions(+), 33 deletions(-) diff --git a/src/Type/Symfony/InputBagTypeSpecifyingExtension.php b/src/Type/Symfony/InputBagTypeSpecifyingExtension.php index bb3d5b28..d3ad578c 100644 --- a/src/Type/Symfony/InputBagTypeSpecifyingExtension.php +++ b/src/Type/Symfony/InputBagTypeSpecifyingExtension.php @@ -9,10 +9,8 @@ use PHPStan\Analyser\TypeSpecifierAwareExtension; use PHPStan\Analyser\TypeSpecifierContext; use PHPStan\Reflection\MethodReflection; -use PHPStan\Reflection\ParametersAcceptorSelector; -use PHPStan\Reflection\ReflectionProvider; use PHPStan\Type\MethodTypeSpecifyingExtension; -use PHPStan\Type\TypeCombinator; +use PHPStan\Type\NullType; use Symfony\Component\HttpFoundation\InputBag; final class InputBagTypeSpecifyingExtension implements MethodTypeSpecifyingExtension, TypeSpecifierAwareExtension @@ -22,17 +20,9 @@ final class InputBagTypeSpecifyingExtension implements MethodTypeSpecifyingExten private const HAS_METHOD_NAME = 'has'; private const GET_METHOD_NAME = 'get'; - /** @var ReflectionProvider */ - private $reflectionProvider; - /** @var TypeSpecifier */ private $typeSpecifier; - public function __construct(ReflectionProvider $reflectionProvider) - { - $this->reflectionProvider = $reflectionProvider; - } - public function getClass(): string { return self::INPUT_BAG_CLASS; @@ -45,18 +35,10 @@ public function isMethodSupported(MethodReflection $methodReflection, MethodCall public function specifyTypes(MethodReflection $methodReflection, MethodCall $node, Scope $scope, TypeSpecifierContext $context): SpecifiedTypes { - $classReflection = $this->reflectionProvider->getClass(self::INPUT_BAG_CLASS); - $methodVariants = $classReflection->getNativeMethod(self::GET_METHOD_NAME)->getVariants(); - $returnType = ParametersAcceptorSelector::selectSingle($methodVariants)->getReturnType(); - - if (!TypeCombinator::containsNull($returnType)) { - return new SpecifiedTypes(); - } - return $this->typeSpecifier->create( new MethodCall($node->var, self::GET_METHOD_NAME, $node->getArgs()), - TypeCombinator::removeNull($returnType), - $context + new NullType(), + $context->negate() ); } diff --git a/src/Type/Symfony/RequestTypeSpecifyingExtension.php b/src/Type/Symfony/RequestTypeSpecifyingExtension.php index 66372a7b..ffb2e8f3 100644 --- a/src/Type/Symfony/RequestTypeSpecifyingExtension.php +++ b/src/Type/Symfony/RequestTypeSpecifyingExtension.php @@ -8,7 +8,6 @@ use PHPStan\Analyser\TypeSpecifier; use PHPStan\Analyser\TypeSpecifierAwareExtension; use PHPStan\Analyser\TypeSpecifierContext; -use PHPStan\Broker\Broker; use PHPStan\Reflection\MethodReflection; use PHPStan\Reflection\ParametersAcceptorSelector; use PHPStan\Type\MethodTypeSpecifyingExtension; @@ -21,17 +20,9 @@ final class RequestTypeSpecifyingExtension implements MethodTypeSpecifyingExtens private const HAS_METHOD_NAME = 'hasSession'; private const GET_METHOD_NAME = 'getSession'; - /** @var Broker */ - private $broker; - /** @var TypeSpecifier */ private $typeSpecifier; - public function __construct(Broker $broker) - { - $this->broker = $broker; - } - public function getClass(): string { return self::REQUEST_CLASS; @@ -44,9 +35,8 @@ public function isMethodSupported(MethodReflection $methodReflection, MethodCall public function specifyTypes(MethodReflection $methodReflection, MethodCall $node, Scope $scope, TypeSpecifierContext $context): SpecifiedTypes { - $classReflection = $this->broker->getClass(self::REQUEST_CLASS); - $methodVariants = $classReflection->getNativeMethod(self::GET_METHOD_NAME)->getVariants(); - $returnType = ParametersAcceptorSelector::selectSingle($methodVariants)->getReturnType(); + $methodVariants = $methodReflection->getDeclaringClass()->getNativeMethod(self::GET_METHOD_NAME)->getVariants(); + $returnType = ParametersAcceptorSelector::selectFromArgs($scope, $node->getArgs(), $methodVariants)->getReturnType(); if (!TypeCombinator::containsNull($returnType)) { return new SpecifiedTypes(); diff --git a/tests/Type/Symfony/data/input_bag.php b/tests/Type/Symfony/data/input_bag.php index 5420e789..efd08461 100644 --- a/tests/Type/Symfony/data/input_bag.php +++ b/tests/Type/Symfony/data/input_bag.php @@ -9,6 +9,9 @@ if ($bag->has('foo')) { assertType('bool|float|int|string', $bag->get('foo')); assertType('bool|float|int|string|null', $bag->get('bar')); +} else { + assertType('null', $bag->get('foo')); + assertType('bool|float|int|string|null', $bag->get('bar')); } assertType('bool|float|int|string|null', $bag->get('foo', null)); From 463eac060bff830c6d86e4f6f8b4cd6bc6c02820 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Wed, 2 Feb 2022 10:21:52 +0000 Subject: [PATCH 061/208] Update dependency slevomat/coding-standard to v7 --- .gitignore | 2 +- build-cs/.gitignore | 1 - build-cs/composer.json | 2 +- build-cs/composer.lock | 327 ++++++++++++++++++ phpcs.xml | 172 +++++---- .../ContainerInterfacePrivateServiceRule.php | 10 +- .../ContainerInterfaceUnknownServiceRule.php | 13 +- .../InvalidArgumentDefaultValueRule.php | 11 +- .../Symfony/InvalidOptionDefaultValueRule.php | 11 +- src/Rules/Symfony/UndefinedArgumentRule.php | 14 +- src/Rules/Symfony/UndefinedOptionRule.php | 14 +- src/Symfony/ConsoleApplicationResolver.php | 8 +- src/Symfony/DefaultParameterMap.php | 6 +- src/Symfony/DefaultServiceMap.php | 6 +- src/Symfony/FakeParameterMap.php | 2 +- src/Symfony/FakeServiceMap.php | 2 +- src/Symfony/InputBagStubFilesExtension.php | 1 + src/Symfony/Parameter.php | 1 - src/Symfony/ParameterMap.php | 2 +- src/Symfony/ServiceMap.php | 2 +- .../XmlContainerNotExistsException.php | 4 +- src/Symfony/XmlParameterMapFactory.php | 19 +- src/Symfony/XmlServiceMapFactory.php | 8 +- .../ArgumentTypeSpecifyingExtension.php | 4 +- ...andGetHelperDynamicReturnTypeExtension.php | 2 +- ...ionPrototypeDynamicReturnTypeExtension.php | 2 + ...ParentObjectDynamicReturnTypeExtension.php | 2 +- ...ReturnParentDynamicReturnTypeExtension.php | 2 +- .../TreeBuilderDynamicReturnTypeExtension.php | 4 +- .../Symfony/EnvelopeReturnTypeExtension.php | 1 + .../InputBagDynamicReturnTypeExtension.php | 1 + ...eGetArgumentDynamicReturnTypeExtension.php | 2 +- ...aceGetOptionDynamicReturnTypeExtension.php | 2 +- ...eHasArgumentDynamicReturnTypeExtension.php | 2 +- ...aceHasOptionDynamicReturnTypeExtension.php | 2 +- .../Symfony/OptionTypeSpecifyingExtension.php | 4 +- .../ParameterDynamicReturnTypeExtension.php | 17 +- .../SerializerDynamicReturnTypeExtension.php | 1 + .../ServiceDynamicReturnTypeExtension.php | 2 +- .../ServiceTypeSpecifyingExtension.php | 4 +- tests/Rules/NonexistentInputBagClassTest.php | 2 +- ...nerInterfacePrivateServiceRuleFakeTest.php | 2 + ...ntainerInterfacePrivateServiceRuleTest.php | 2 + ...nerInterfaceUnknownServiceRuleFakeTest.php | 4 +- ...ntainerInterfaceUnknownServiceRuleTest.php | 2 + .../Symfony/ExampleAbstractController.php | 3 +- tests/Rules/Symfony/ExampleController.php | 3 +- .../Symfony/ExampleServiceSubscriber.php | 3 +- tests/Symfony/DefaultParameterMapTest.php | 28 +- tests/Symfony/DefaultServiceMapTest.php | 18 +- tests/Type/Symfony/ExtensionTest.php | 4 +- .../Symfony/ExtensionTestWithoutContainer.php | 3 +- .../ImpossibleCheckTypeMethodCallRuleTest.php | 5 +- .../Symfony/console_application_loader.php | 2 +- 54 files changed, 576 insertions(+), 197 deletions(-) create mode 100644 build-cs/composer.lock diff --git a/.gitignore b/.gitignore index d6a83e59..2db21315 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ /tests/tmp /vendor -composer.lock +/composer.lock .phpunit.result.cache diff --git a/build-cs/.gitignore b/build-cs/.gitignore index ff72e2d0..61ead866 100644 --- a/build-cs/.gitignore +++ b/build-cs/.gitignore @@ -1,2 +1 @@ -/composer.lock /vendor diff --git a/build-cs/composer.json b/build-cs/composer.json index cc6a4983..e3079710 100644 --- a/build-cs/composer.json +++ b/build-cs/composer.json @@ -2,7 +2,7 @@ "require-dev": { "consistence-community/coding-standard": "^3.10", "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", - "slevomat/coding-standard": "^6.4" + "slevomat/coding-standard": "^7.0" }, "config": { "allow-plugins": { diff --git a/build-cs/composer.lock b/build-cs/composer.lock new file mode 100644 index 00000000..70af78f9 --- /dev/null +++ b/build-cs/composer.lock @@ -0,0 +1,327 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "4485bbedba7bcc71ace5f69dbb9b6c47", + "packages": [], + "packages-dev": [ + { + "name": "consistence-community/coding-standard", + "version": "3.11.1", + "source": { + "type": "git", + "url": "https://github.com/consistence-community/coding-standard.git", + "reference": "4632fead8c9ee8f50044fcbce9f66c797b34c0df" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/consistence-community/coding-standard/zipball/4632fead8c9ee8f50044fcbce9f66c797b34c0df", + "reference": "4632fead8c9ee8f50044fcbce9f66c797b34c0df", + "shasum": "" + }, + "require": { + "php": ">=7.4", + "slevomat/coding-standard": "~7.0", + "squizlabs/php_codesniffer": "~3.6.0" + }, + "replace": { + "consistence/coding-standard": "3.10.*" + }, + "require-dev": { + "phing/phing": "2.16.4", + "php-parallel-lint/php-parallel-lint": "1.3.0", + "phpunit/phpunit": "9.5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Consistence\\": [ + "Consistence" + ] + }, + "classmap": [ + "Consistence" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Vašek Purchart", + "email": "me@vasekpurchart.cz", + "homepage": "http://vasekpurchart.cz" + } + ], + "description": "Consistence - Coding Standard - PHP Code Sniffer rules", + "keywords": [ + "Coding Standard", + "PHPCodeSniffer", + "codesniffer", + "coding", + "cs", + "phpcs", + "ruleset", + "sniffer", + "standard" + ], + "support": { + "issues": "https://github.com/consistence-community/coding-standard/issues", + "source": "https://github.com/consistence-community/coding-standard/tree/3.11.1" + }, + "time": "2021-05-03T18:13:22+00:00" + }, + { + "name": "dealerdirect/phpcodesniffer-composer-installer", + "version": "v0.7.2", + "source": { + "type": "git", + "url": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer.git", + "reference": "1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Dealerdirect/phpcodesniffer-composer-installer/zipball/1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db", + "reference": "1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0 || ^2.0", + "php": ">=5.3", + "squizlabs/php_codesniffer": "^2.0 || ^3.1.0 || ^4.0" + }, + "require-dev": { + "composer/composer": "*", + "php-parallel-lint/php-parallel-lint": "^1.3.1", + "phpcompatibility/php-compatibility": "^9.0" + }, + "type": "composer-plugin", + "extra": { + "class": "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin" + }, + "autoload": { + "psr-4": { + "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Franck Nijhof", + "email": "franck.nijhof@dealerdirect.com", + "homepage": "http://www.frenck.nl", + "role": "Developer / IT Manager" + }, + { + "name": "Contributors", + "homepage": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer/graphs/contributors" + } + ], + "description": "PHP_CodeSniffer Standards Composer Installer Plugin", + "homepage": "http://www.dealerdirect.com", + "keywords": [ + "PHPCodeSniffer", + "PHP_CodeSniffer", + "code quality", + "codesniffer", + "composer", + "installer", + "phpcbf", + "phpcs", + "plugin", + "qa", + "quality", + "standard", + "standards", + "style guide", + "stylecheck", + "tests" + ], + "support": { + "issues": "https://github.com/dealerdirect/phpcodesniffer-composer-installer/issues", + "source": "https://github.com/dealerdirect/phpcodesniffer-composer-installer" + }, + "time": "2022-02-04T12:51:07+00:00" + }, + { + "name": "phpstan/phpdoc-parser", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "dbc093d7af60eff5cd575d2ed761b15ed40bd08e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/dbc093d7af60eff5cd575d2ed761b15ed40bd08e", + "reference": "dbc093d7af60eff5cd575d2ed761b15ed40bd08e", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-strict-rules": "^1.0", + "phpunit/phpunit": "^9.5", + "symfony/process": "^5.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.2.0" + }, + "time": "2021-09-16T20:46:02+00:00" + }, + { + "name": "slevomat/coding-standard", + "version": "7.0.18", + "source": { + "type": "git", + "url": "https://github.com/slevomat/coding-standard.git", + "reference": "b81ac84f41a4797dc25c8ede1b0718e2a74be0fc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/b81ac84f41a4797dc25c8ede1b0718e2a74be0fc", + "reference": "b81ac84f41a4797dc25c8ede1b0718e2a74be0fc", + "shasum": "" + }, + "require": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7", + "php": "^7.1 || ^8.0", + "phpstan/phpdoc-parser": "^1.0.0", + "squizlabs/php_codesniffer": "^3.6.1" + }, + "require-dev": { + "phing/phing": "2.17.0", + "php-parallel-lint/php-parallel-lint": "1.3.1", + "phpstan/phpstan": "1.2.0", + "phpstan/phpstan-deprecation-rules": "1.0.0", + "phpstan/phpstan-phpunit": "1.0.0", + "phpstan/phpstan-strict-rules": "1.1.0", + "phpunit/phpunit": "7.5.20|8.5.21|9.5.10" + }, + "type": "phpcodesniffer-standard", + "extra": { + "branch-alias": { + "dev-master": "7.x-dev" + } + }, + "autoload": { + "psr-4": { + "SlevomatCodingStandard\\": "SlevomatCodingStandard" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.", + "support": { + "issues": "https://github.com/slevomat/coding-standard/issues", + "source": "https://github.com/slevomat/coding-standard/tree/7.0.18" + }, + "funding": [ + { + "url": "https://github.com/kukulich", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/slevomat/coding-standard", + "type": "tidelift" + } + ], + "time": "2021-12-07T17:19:06+00:00" + }, + { + "name": "squizlabs/php_codesniffer", + "version": "3.6.2", + "source": { + "type": "git", + "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", + "reference": "5e4e71592f69da17871dba6e80dd51bce74a351a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/5e4e71592f69da17871dba6e80dd51bce74a351a", + "reference": "5e4e71592f69da17871dba6e80dd51bce74a351a", + "shasum": "" + }, + "require": { + "ext-simplexml": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" + }, + "bin": [ + "bin/phpcs", + "bin/phpcbf" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Greg Sherwood", + "role": "lead" + } + ], + "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", + "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", + "keywords": [ + "phpcs", + "standards" + ], + "support": { + "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", + "source": "https://github.com/squizlabs/PHP_CodeSniffer", + "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" + }, + "time": "2021-12-12T21:44:58+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [], + "plugin-api-version": "2.2.0" +} diff --git a/phpcs.xml b/phpcs.xml index 2807efec..3412c956 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -1,66 +1,112 @@ - - - - - - - - src - tests - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - tests/tmp + + + + + + + + + src + tests + + + + + + + + + + + + + + + + + + + + + + + + + + + + 10 + + + + + + 10 + + + + + + + + 10 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + tests/*/data + tests/tmp diff --git a/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php b/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php index 4c9a6af1..0a3a3d3e 100644 --- a/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php +++ b/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php @@ -6,7 +6,7 @@ use PhpParser\Node\Expr\MethodCall; use PHPStan\Analyser\Scope; use PHPStan\Rules\Rule; -use PHPStan\ShouldNotHappenException; +use PHPStan\Rules\RuleError; use PHPStan\Symfony\ServiceMap; use PHPStan\TrinaryLogic; use PHPStan\Type\ObjectType; @@ -33,16 +33,10 @@ public function getNodeType(): string } /** - * @param \PhpParser\Node $node - * @param \PHPStan\Analyser\Scope $scope - * @return (string|\PHPStan\Rules\RuleError)[] errors + * @return (string|RuleError)[] errors */ public function processNode(Node $node, Scope $scope): array { - if (!$node instanceof MethodCall) { - throw new ShouldNotHappenException(); - } - if (!$node->name instanceof Node\Identifier) { return []; } diff --git a/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php b/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php index cb0ae4e6..417f51f5 100644 --- a/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php +++ b/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php @@ -7,10 +7,11 @@ use PhpParser\PrettyPrinter\Standard; use PHPStan\Analyser\Scope; use PHPStan\Rules\Rule; -use PHPStan\ShouldNotHappenException; +use PHPStan\Rules\RuleError; use PHPStan\Symfony\ServiceMap; use PHPStan\Type\ObjectType; use PHPStan\Type\Symfony\Helper; +use function sprintf; /** * @implements Rule @@ -21,7 +22,7 @@ final class ContainerInterfaceUnknownServiceRule implements Rule /** @var ServiceMap */ private $serviceMap; - /** @var \PhpParser\PrettyPrinter\Standard */ + /** @var Standard */ private $printer; public function __construct(ServiceMap $symfonyServiceMap, Standard $printer) @@ -36,16 +37,10 @@ public function getNodeType(): string } /** - * @param \PhpParser\Node $node - * @param \PHPStan\Analyser\Scope $scope - * @return (string|\PHPStan\Rules\RuleError)[] errors + * @return (string|RuleError)[] errors */ public function processNode(Node $node, Scope $scope): array { - if (!$node instanceof MethodCall) { - throw new ShouldNotHappenException(); - } - if (!$node->name instanceof Node\Identifier) { return []; } diff --git a/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php b/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php index c29f11ed..51d7c3bc 100644 --- a/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php +++ b/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php @@ -6,7 +6,7 @@ use PhpParser\Node\Expr\MethodCall; use PHPStan\Analyser\Scope; use PHPStan\Rules\Rule; -use PHPStan\ShouldNotHappenException; +use PHPStan\Rules\RuleError; use PHPStan\Type\ArrayType; use PHPStan\Type\Constant\ConstantIntegerType; use PHPStan\Type\IntegerType; @@ -16,6 +16,7 @@ use PHPStan\Type\TypeUtils; use PHPStan\Type\UnionType; use PHPStan\Type\VerbosityLevel; +use function count; use function sprintf; /** @@ -30,16 +31,10 @@ public function getNodeType(): string } /** - * @param \PhpParser\Node $node - * @param \PHPStan\Analyser\Scope $scope - * @return (string|\PHPStan\Rules\RuleError)[] errors + * @return (string|RuleError)[] errors */ public function processNode(Node $node, Scope $scope): array { - if (!$node instanceof MethodCall) { - throw new ShouldNotHappenException(); - }; - if (!(new ObjectType('Symfony\Component\Console\Command\Command'))->isSuperTypeOf($scope->getType($node->var))->yes()) { return []; } diff --git a/src/Rules/Symfony/InvalidOptionDefaultValueRule.php b/src/Rules/Symfony/InvalidOptionDefaultValueRule.php index 3eb2df1b..ab52d626 100644 --- a/src/Rules/Symfony/InvalidOptionDefaultValueRule.php +++ b/src/Rules/Symfony/InvalidOptionDefaultValueRule.php @@ -6,7 +6,7 @@ use PhpParser\Node\Expr\MethodCall; use PHPStan\Analyser\Scope; use PHPStan\Rules\Rule; -use PHPStan\ShouldNotHappenException; +use PHPStan\Rules\RuleError; use PHPStan\Type\ArrayType; use PHPStan\Type\BooleanType; use PHPStan\Type\Constant\ConstantIntegerType; @@ -18,6 +18,7 @@ use PHPStan\Type\TypeUtils; use PHPStan\Type\UnionType; use PHPStan\Type\VerbosityLevel; +use function count; use function sprintf; /** @@ -32,16 +33,10 @@ public function getNodeType(): string } /** - * @param \PhpParser\Node $node - * @param \PHPStan\Analyser\Scope $scope - * @return (string|\PHPStan\Rules\RuleError)[] errors + * @return (string|RuleError)[] errors */ public function processNode(Node $node, Scope $scope): array { - if (!$node instanceof MethodCall) { - throw new ShouldNotHappenException(); - }; - if (!(new ObjectType('Symfony\Component\Console\Command\Command'))->isSuperTypeOf($scope->getType($node->var))->yes()) { return []; } diff --git a/src/Rules/Symfony/UndefinedArgumentRule.php b/src/Rules/Symfony/UndefinedArgumentRule.php index 59f71ed6..52f90241 100644 --- a/src/Rules/Symfony/UndefinedArgumentRule.php +++ b/src/Rules/Symfony/UndefinedArgumentRule.php @@ -8,7 +8,7 @@ use PhpParser\PrettyPrinter\Standard; use PHPStan\Analyser\Scope; use PHPStan\Rules\Rule; -use PHPStan\ShouldNotHappenException; +use PHPStan\Rules\RuleError; use PHPStan\Symfony\ConsoleApplicationResolver; use PHPStan\Type\ObjectType; use PHPStan\Type\Symfony\Helper; @@ -22,10 +22,10 @@ final class UndefinedArgumentRule implements Rule { - /** @var \PHPStan\Symfony\ConsoleApplicationResolver */ + /** @var ConsoleApplicationResolver */ private $consoleApplicationResolver; - /** @var \PhpParser\PrettyPrinter\Standard */ + /** @var Standard */ private $printer; public function __construct(ConsoleApplicationResolver $consoleApplicationResolver, Standard $printer) @@ -40,16 +40,10 @@ public function getNodeType(): string } /** - * @param \PhpParser\Node $node - * @param \PHPStan\Analyser\Scope $scope - * @return (string|\PHPStan\Rules\RuleError)[] errors + * @return (string|RuleError)[] errors */ public function processNode(Node $node, Scope $scope): array { - if (!$node instanceof MethodCall) { - throw new ShouldNotHappenException(); - }; - $classReflection = $scope->getClassReflection(); if ($classReflection === null) { return []; diff --git a/src/Rules/Symfony/UndefinedOptionRule.php b/src/Rules/Symfony/UndefinedOptionRule.php index 29e08819..00dd750e 100644 --- a/src/Rules/Symfony/UndefinedOptionRule.php +++ b/src/Rules/Symfony/UndefinedOptionRule.php @@ -8,7 +8,7 @@ use PhpParser\PrettyPrinter\Standard; use PHPStan\Analyser\Scope; use PHPStan\Rules\Rule; -use PHPStan\ShouldNotHappenException; +use PHPStan\Rules\RuleError; use PHPStan\Symfony\ConsoleApplicationResolver; use PHPStan\Type\ObjectType; use PHPStan\Type\Symfony\Helper; @@ -22,10 +22,10 @@ final class UndefinedOptionRule implements Rule { - /** @var \PHPStan\Symfony\ConsoleApplicationResolver */ + /** @var ConsoleApplicationResolver */ private $consoleApplicationResolver; - /** @var \PhpParser\PrettyPrinter\Standard */ + /** @var Standard */ private $printer; public function __construct(ConsoleApplicationResolver $consoleApplicationResolver, Standard $printer) @@ -40,16 +40,10 @@ public function getNodeType(): string } /** - * @param \PhpParser\Node $node - * @param \PHPStan\Analyser\Scope $scope - * @return (string|\PHPStan\Rules\RuleError)[] errors + * @return (string|RuleError)[] errors */ public function processNode(Node $node, Scope $scope): array { - if (!$node instanceof MethodCall) { - throw new ShouldNotHappenException(); - }; - $classReflection = $scope->getClassReflection(); if ($classReflection === null) { return []; diff --git a/src/Symfony/ConsoleApplicationResolver.php b/src/Symfony/ConsoleApplicationResolver.php index efee6292..6d4ec810 100644 --- a/src/Symfony/ConsoleApplicationResolver.php +++ b/src/Symfony/ConsoleApplicationResolver.php @@ -5,16 +5,18 @@ use PHPStan\Reflection\ClassReflection; use PHPStan\ShouldNotHappenException; use PHPStan\Type\ObjectType; +use Symfony\Component\Console\Application; use Symfony\Component\Console\Command\Command; use function file_exists; use function get_class; use function is_readable; +use function method_exists; use function sprintf; final class ConsoleApplicationResolver { - /** @var \Symfony\Component\Console\Application|null */ + /** @var Application|null */ private $consoleApplication; public function __construct(Configuration $configuration) @@ -27,7 +29,7 @@ public function __construct(Configuration $configuration) } /** - * @return \Symfony\Component\Console\Application|null + * @return Application|null * @phpcsSuppress SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingNativeTypeHint */ private function loadConsoleApplication(string $consoleApplicationLoader) @@ -42,7 +44,7 @@ private function loadConsoleApplication(string $consoleApplicationLoader) } /** - * @return \Symfony\Component\Console\Command\Command[] + * @return Command[] */ public function findCommands(ClassReflection $classReflection): array { diff --git a/src/Symfony/DefaultParameterMap.php b/src/Symfony/DefaultParameterMap.php index a58ed9fc..fe29b34f 100644 --- a/src/Symfony/DefaultParameterMap.php +++ b/src/Symfony/DefaultParameterMap.php @@ -10,11 +10,11 @@ final class DefaultParameterMap implements ParameterMap { - /** @var \PHPStan\Symfony\ParameterDefinition[] */ + /** @var ParameterDefinition[] */ private $parameters; /** - * @param \PHPStan\Symfony\ParameterDefinition[] $parameters + * @param ParameterDefinition[] $parameters */ public function __construct(array $parameters) { @@ -22,7 +22,7 @@ public function __construct(array $parameters) } /** - * @return \PHPStan\Symfony\ParameterDefinition[] + * @return ParameterDefinition[] */ public function getParameters(): array { diff --git a/src/Symfony/DefaultServiceMap.php b/src/Symfony/DefaultServiceMap.php index 6339b450..cb8259d2 100644 --- a/src/Symfony/DefaultServiceMap.php +++ b/src/Symfony/DefaultServiceMap.php @@ -10,11 +10,11 @@ final class DefaultServiceMap implements ServiceMap { - /** @var \PHPStan\Symfony\ServiceDefinition[] */ + /** @var ServiceDefinition[] */ private $services; /** - * @param \PHPStan\Symfony\ServiceDefinition[] $services + * @param ServiceDefinition[] $services */ public function __construct(array $services) { @@ -22,7 +22,7 @@ public function __construct(array $services) } /** - * @return \PHPStan\Symfony\ServiceDefinition[] + * @return ServiceDefinition[] */ public function getServices(): array { diff --git a/src/Symfony/FakeParameterMap.php b/src/Symfony/FakeParameterMap.php index 7e723411..0bf450e7 100644 --- a/src/Symfony/FakeParameterMap.php +++ b/src/Symfony/FakeParameterMap.php @@ -9,7 +9,7 @@ final class FakeParameterMap implements ParameterMap { /** - * @return \PHPStan\Symfony\ParameterDefinition[] + * @return ParameterDefinition[] */ public function getParameters(): array { diff --git a/src/Symfony/FakeServiceMap.php b/src/Symfony/FakeServiceMap.php index d05fe8ed..88f9edac 100644 --- a/src/Symfony/FakeServiceMap.php +++ b/src/Symfony/FakeServiceMap.php @@ -9,7 +9,7 @@ final class FakeServiceMap implements ServiceMap { /** - * @return \PHPStan\Symfony\ServiceDefinition[] + * @return ServiceDefinition[] */ public function getServices(): array { diff --git a/src/Symfony/InputBagStubFilesExtension.php b/src/Symfony/InputBagStubFilesExtension.php index 94fd2104..834ca228 100644 --- a/src/Symfony/InputBagStubFilesExtension.php +++ b/src/Symfony/InputBagStubFilesExtension.php @@ -3,6 +3,7 @@ namespace PHPStan\Symfony; use PHPStan\PhpDoc\StubFilesExtension; +use function class_exists; class InputBagStubFilesExtension implements StubFilesExtension { diff --git a/src/Symfony/Parameter.php b/src/Symfony/Parameter.php index 1df679bc..8ff3f7a8 100644 --- a/src/Symfony/Parameter.php +++ b/src/Symfony/Parameter.php @@ -12,7 +12,6 @@ final class Parameter implements ParameterDefinition private $value; /** - * @param string $key * @param array|bool|float|int|string $value */ public function __construct( diff --git a/src/Symfony/ParameterMap.php b/src/Symfony/ParameterMap.php index fc14fb2c..c768e2f2 100644 --- a/src/Symfony/ParameterMap.php +++ b/src/Symfony/ParameterMap.php @@ -9,7 +9,7 @@ interface ParameterMap { /** - * @return \PHPStan\Symfony\ParameterDefinition[] + * @return ParameterDefinition[] */ public function getParameters(): array; diff --git a/src/Symfony/ServiceMap.php b/src/Symfony/ServiceMap.php index b954dc72..6665ede0 100644 --- a/src/Symfony/ServiceMap.php +++ b/src/Symfony/ServiceMap.php @@ -9,7 +9,7 @@ interface ServiceMap { /** - * @return \PHPStan\Symfony\ServiceDefinition[] + * @return ServiceDefinition[] */ public function getServices(): array; diff --git a/src/Symfony/XmlContainerNotExistsException.php b/src/Symfony/XmlContainerNotExistsException.php index 7238cc6e..59b600de 100644 --- a/src/Symfony/XmlContainerNotExistsException.php +++ b/src/Symfony/XmlContainerNotExistsException.php @@ -2,7 +2,9 @@ namespace PHPStan\Symfony; -final class XmlContainerNotExistsException extends \InvalidArgumentException +use InvalidArgumentException; + +final class XmlContainerNotExistsException extends InvalidArgumentException { } diff --git a/src/Symfony/XmlParameterMapFactory.php b/src/Symfony/XmlParameterMapFactory.php index 9b34d14d..0891423a 100644 --- a/src/Symfony/XmlParameterMapFactory.php +++ b/src/Symfony/XmlParameterMapFactory.php @@ -2,7 +2,14 @@ namespace PHPStan\Symfony; +use InvalidArgumentException; +use SimpleXMLElement; +use function base64_decode; +use function file_get_contents; +use function is_numeric; +use function simplexml_load_string; use function sprintf; +use function strpos; final class XmlParameterMapFactory implements ParameterMapFactory { @@ -31,10 +38,10 @@ public function create(): ParameterMap throw new XmlContainerNotExistsException(sprintf('Container %s cannot be parsed', $this->containerXml)); } - /** @var \PHPStan\Symfony\Parameter[] $parameters */ + /** @var Parameter[] $parameters */ $parameters = []; foreach ($xml->parameters->parameter as $def) { - /** @var \SimpleXMLElement $attrs */ + /** @var SimpleXMLElement $attrs */ $attrs = $def->attributes(); $parameter = new Parameter( @@ -51,9 +58,9 @@ public function create(): ParameterMap /** * @return array|bool|float|int|string */ - private function getNodeValue(\SimpleXMLElement $def) + private function getNodeValue(SimpleXMLElement $def) { - /** @var \SimpleXMLElement $attrs */ + /** @var SimpleXMLElement $attrs */ $attrs = $def->attributes(); $value = null; @@ -61,7 +68,7 @@ private function getNodeValue(\SimpleXMLElement $def) case 'collection': $value = []; foreach ($def->children() as $child) { - /** @var \SimpleXMLElement $childAttrs */ + /** @var SimpleXMLElement $childAttrs */ $childAttrs = $child->attributes(); if (isset($childAttrs->key)) { @@ -79,7 +86,7 @@ private function getNodeValue(\SimpleXMLElement $def) case 'binary': $value = base64_decode((string) $def, true); if ($value === false) { - throw new \InvalidArgumentException(sprintf('Parameter "%s" of binary type is not valid base64 encoded string.', (string) $attrs->key)); + throw new InvalidArgumentException(sprintf('Parameter "%s" of binary type is not valid base64 encoded string.', (string) $attrs->key)); } break; diff --git a/src/Symfony/XmlServiceMapFactory.php b/src/Symfony/XmlServiceMapFactory.php index 60af1326..ad52c373 100644 --- a/src/Symfony/XmlServiceMapFactory.php +++ b/src/Symfony/XmlServiceMapFactory.php @@ -2,6 +2,8 @@ namespace PHPStan\Symfony; +use SimpleXMLElement; +use function file_get_contents; use function simplexml_load_string; use function sprintf; use function strpos; @@ -34,12 +36,12 @@ public function create(): ServiceMap throw new XmlContainerNotExistsException(sprintf('Container %s cannot be parsed', $this->containerXml)); } - /** @var \PHPStan\Symfony\Service[] $services */ + /** @var Service[] $services */ $services = []; - /** @var \PHPStan\Symfony\Service[] $aliases */ + /** @var Service[] $aliases */ $aliases = []; foreach ($xml->services->service as $def) { - /** @var \SimpleXMLElement $attrs */ + /** @var SimpleXMLElement $attrs */ $attrs = $def->attributes(); if (!isset($attrs->id)) { continue; diff --git a/src/Type/Symfony/ArgumentTypeSpecifyingExtension.php b/src/Type/Symfony/ArgumentTypeSpecifyingExtension.php index 74b6981f..b57f3c35 100644 --- a/src/Type/Symfony/ArgumentTypeSpecifyingExtension.php +++ b/src/Type/Symfony/ArgumentTypeSpecifyingExtension.php @@ -15,10 +15,10 @@ final class ArgumentTypeSpecifyingExtension implements MethodTypeSpecifyingExtension, TypeSpecifierAwareExtension { - /** @var \PhpParser\PrettyPrinter\Standard */ + /** @var Standard */ private $printer; - /** @var \PHPStan\Analyser\TypeSpecifier */ + /** @var TypeSpecifier */ private $typeSpecifier; public function __construct(Standard $printer) diff --git a/src/Type/Symfony/CommandGetHelperDynamicReturnTypeExtension.php b/src/Type/Symfony/CommandGetHelperDynamicReturnTypeExtension.php index e9b6ed7e..db2348e3 100644 --- a/src/Type/Symfony/CommandGetHelperDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/CommandGetHelperDynamicReturnTypeExtension.php @@ -18,7 +18,7 @@ final class CommandGetHelperDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension { - /** @var \PHPStan\Symfony\ConsoleApplicationResolver */ + /** @var ConsoleApplicationResolver */ private $consoleApplicationResolver; public function __construct(ConsoleApplicationResolver $consoleApplicationResolver) diff --git a/src/Type/Symfony/Config/ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php index cd1d4e51..828b000e 100644 --- a/src/Type/Symfony/Config/ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/Config/ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php @@ -11,6 +11,8 @@ use PHPStan\Type\Type; use PHPStan\Type\TypeUtils; use PHPStan\Type\VerbosityLevel; +use function count; +use function in_array; final class ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension { diff --git a/src/Type/Symfony/Config/PassParentObjectDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/PassParentObjectDynamicReturnTypeExtension.php index 5d19b60f..6ef28bbd 100644 --- a/src/Type/Symfony/Config/PassParentObjectDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/Config/PassParentObjectDynamicReturnTypeExtension.php @@ -10,6 +10,7 @@ use PHPStan\Type\Symfony\Config\ValueObject\ParentObjectType; use PHPStan\Type\Type; use PHPStan\Type\VerbosityLevel; +use function in_array; final class PassParentObjectDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension { @@ -21,7 +22,6 @@ final class PassParentObjectDynamicReturnTypeExtension implements DynamicMethodR private $methods; /** - * @param string $className * @param string[] $methods */ public function __construct(string $className, array $methods) diff --git a/src/Type/Symfony/Config/ReturnParentDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/ReturnParentDynamicReturnTypeExtension.php index 8dd47a90..4babdcd7 100644 --- a/src/Type/Symfony/Config/ReturnParentDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/Config/ReturnParentDynamicReturnTypeExtension.php @@ -9,6 +9,7 @@ use PHPStan\Type\DynamicMethodReturnTypeExtension; use PHPStan\Type\Symfony\Config\ValueObject\ParentObjectType; use PHPStan\Type\Type; +use function in_array; final class ReturnParentDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension { @@ -20,7 +21,6 @@ final class ReturnParentDynamicReturnTypeExtension implements DynamicMethodRetur private $methods; /** - * @param string $className * @param string[] $methods */ public function __construct(string $className, array $methods) diff --git a/src/Type/Symfony/Config/TreeBuilderDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/TreeBuilderDynamicReturnTypeExtension.php index a5f4f724..8cf3170a 100644 --- a/src/Type/Symfony/Config/TreeBuilderDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/Config/TreeBuilderDynamicReturnTypeExtension.php @@ -6,10 +6,12 @@ use PhpParser\Node\Name; use PHPStan\Analyser\Scope; use PHPStan\Reflection\MethodReflection; +use PHPStan\ShouldNotHappenException; use PHPStan\Type\DynamicStaticMethodReturnTypeExtension; use PHPStan\Type\Symfony\Config\ValueObject\TreeBuilderType; use PHPStan\Type\Type; use PHPStan\Type\TypeUtils; +use function count; final class TreeBuilderDynamicReturnTypeExtension implements DynamicStaticMethodReturnTypeExtension { @@ -37,7 +39,7 @@ public function isStaticMethodSupported(MethodReflection $methodReflection): boo public function getTypeFromStaticMethodCall(MethodReflection $methodReflection, StaticCall $methodCall, Scope $scope): Type { if (!$methodCall->class instanceof Name) { - throw new \PHPStan\ShouldNotHappenException(); + throw new ShouldNotHappenException(); } $className = $scope->resolveName($methodCall->class); diff --git a/src/Type/Symfony/EnvelopeReturnTypeExtension.php b/src/Type/Symfony/EnvelopeReturnTypeExtension.php index e4e6a34c..5ac61c2f 100644 --- a/src/Type/Symfony/EnvelopeReturnTypeExtension.php +++ b/src/Type/Symfony/EnvelopeReturnTypeExtension.php @@ -11,6 +11,7 @@ use PHPStan\Type\MixedType; use PHPStan\Type\ObjectType; use PHPStan\Type\Type; +use function count; final class EnvelopeReturnTypeExtension implements DynamicMethodReturnTypeExtension { diff --git a/src/Type/Symfony/InputBagDynamicReturnTypeExtension.php b/src/Type/Symfony/InputBagDynamicReturnTypeExtension.php index b4875829..af35a548 100644 --- a/src/Type/Symfony/InputBagDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/InputBagDynamicReturnTypeExtension.php @@ -18,6 +18,7 @@ use PHPStan\Type\Type; use PHPStan\Type\TypeCombinator; use PHPStan\Type\UnionType; +use function in_array; final class InputBagDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension { diff --git a/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php index 15e75d0e..a8c35200 100644 --- a/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php @@ -20,7 +20,7 @@ final class InputInterfaceGetArgumentDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension { - /** @var \PHPStan\Symfony\ConsoleApplicationResolver */ + /** @var ConsoleApplicationResolver */ private $consoleApplicationResolver; public function __construct(ConsoleApplicationResolver $consoleApplicationResolver) diff --git a/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php index fb397311..7b543a16 100644 --- a/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php @@ -22,7 +22,7 @@ final class InputInterfaceGetOptionDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension { - /** @var \PHPStan\Symfony\ConsoleApplicationResolver */ + /** @var ConsoleApplicationResolver */ private $consoleApplicationResolver; public function __construct(ConsoleApplicationResolver $consoleApplicationResolver) diff --git a/src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php index 112a2bcf..dc933e14 100644 --- a/src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php @@ -18,7 +18,7 @@ final class InputInterfaceHasArgumentDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension { - /** @var \PHPStan\Symfony\ConsoleApplicationResolver */ + /** @var ConsoleApplicationResolver */ private $consoleApplicationResolver; public function __construct(ConsoleApplicationResolver $consoleApplicationResolver) diff --git a/src/Type/Symfony/InputInterfaceHasOptionDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceHasOptionDynamicReturnTypeExtension.php index eec7201c..3f239fe6 100644 --- a/src/Type/Symfony/InputInterfaceHasOptionDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/InputInterfaceHasOptionDynamicReturnTypeExtension.php @@ -18,7 +18,7 @@ final class InputInterfaceHasOptionDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension { - /** @var \PHPStan\Symfony\ConsoleApplicationResolver */ + /** @var ConsoleApplicationResolver */ private $consoleApplicationResolver; public function __construct(ConsoleApplicationResolver $consoleApplicationResolver) diff --git a/src/Type/Symfony/OptionTypeSpecifyingExtension.php b/src/Type/Symfony/OptionTypeSpecifyingExtension.php index 347cf591..98e0e3fd 100644 --- a/src/Type/Symfony/OptionTypeSpecifyingExtension.php +++ b/src/Type/Symfony/OptionTypeSpecifyingExtension.php @@ -15,10 +15,10 @@ final class OptionTypeSpecifyingExtension implements MethodTypeSpecifyingExtension, TypeSpecifierAwareExtension { - /** @var \PhpParser\PrettyPrinter\Standard */ + /** @var Standard */ private $printer; - /** @var \PHPStan\Analyser\TypeSpecifier */ + /** @var TypeSpecifier */ private $typeSpecifier; public function __construct(Standard $printer) diff --git a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php index 0de91de1..0fa8e7bc 100644 --- a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php @@ -27,7 +27,17 @@ use PHPStan\Type\TypeTraverser; use PHPStan\Type\UnionType; use Symfony\Component\DependencyInjection\EnvVarProcessor; +use function array_filter; +use function array_keys; +use function array_map; +use function array_values; +use function class_exists; +use function count; use function in_array; +use function is_array; +use function is_string; +use function preg_match; +use function strlen; final class ParameterDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension { @@ -44,10 +54,10 @@ final class ParameterDynamicReturnTypeExtension implements DynamicMethodReturnTy /** @var bool */ private $constantHassers; - /** @var \PHPStan\Symfony\ParameterMap */ + /** @var ParameterMap */ private $parameterMap; - /** @var \PHPStan\PhpDoc\TypeStringResolver */ + /** @var TypeStringResolver */ private $typeStringResolver; public function __construct( @@ -74,7 +84,7 @@ public function getClass(): string public function isMethodSupported(MethodReflection $methodReflection): bool { - $methods = array_filter([$this->methodGet, $this->methodHas], function (?string $method): bool { + $methods = array_filter([$this->methodGet, $this->methodHas], static function (?string $method): bool { return $method !== null; }); @@ -124,7 +134,6 @@ private function getGetTypeFromMethodCall( } /** - * @param Scope $scope * @param array|bool|float|int|string $value */ private function generalizeTypeFromValue(Scope $scope, $value): Type diff --git a/src/Type/Symfony/SerializerDynamicReturnTypeExtension.php b/src/Type/Symfony/SerializerDynamicReturnTypeExtension.php index fda9b048..5525a770 100755 --- a/src/Type/Symfony/SerializerDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/SerializerDynamicReturnTypeExtension.php @@ -11,6 +11,7 @@ use PHPStan\Type\MixedType; use PHPStan\Type\ObjectType; use PHPStan\Type\Type; +use function substr; class SerializerDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension { diff --git a/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php b/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php index 41c3a394..04608c17 100644 --- a/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php @@ -24,7 +24,7 @@ final class ServiceDynamicReturnTypeExtension implements DynamicMethodReturnType /** @var bool */ private $constantHassers; - /** @var \PHPStan\Symfony\ServiceMap */ + /** @var ServiceMap */ private $serviceMap; public function __construct(string $className, Configuration $configuration, ServiceMap $symfonyServiceMap) diff --git a/src/Type/Symfony/ServiceTypeSpecifyingExtension.php b/src/Type/Symfony/ServiceTypeSpecifyingExtension.php index a5aad54e..b52c1531 100644 --- a/src/Type/Symfony/ServiceTypeSpecifyingExtension.php +++ b/src/Type/Symfony/ServiceTypeSpecifyingExtension.php @@ -18,10 +18,10 @@ final class ServiceTypeSpecifyingExtension implements MethodTypeSpecifyingExtens /** @var string */ private $className; - /** @var \PhpParser\PrettyPrinter\Standard */ + /** @var Standard */ private $printer; - /** @var \PHPStan\Analyser\TypeSpecifier */ + /** @var TypeSpecifier */ private $typeSpecifier; public function __construct(string $className, Standard $printer) diff --git a/tests/Rules/NonexistentInputBagClassTest.php b/tests/Rules/NonexistentInputBagClassTest.php index 6619a8c7..5dc674d4 100644 --- a/tests/Rules/NonexistentInputBagClassTest.php +++ b/tests/Rules/NonexistentInputBagClassTest.php @@ -11,7 +11,7 @@ class NonexistentInputBagClassTest extends RuleTestCase { - protected function getRule(): \PHPStan\Rules\Rule + protected function getRule(): Rule { return self::getContainer()->getByType(CallMethodsRule::class); } diff --git a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleFakeTest.php b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleFakeTest.php index 23bdd4e6..4551accd 100644 --- a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleFakeTest.php +++ b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleFakeTest.php @@ -6,6 +6,8 @@ use PHPStan\Symfony\Configuration; use PHPStan\Symfony\XmlServiceMapFactory; use PHPStan\Testing\RuleTestCase; +use function class_exists; +use function interface_exists; /** * @extends RuleTestCase diff --git a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php index c349b695..49afaff5 100644 --- a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php +++ b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php @@ -6,6 +6,8 @@ use PHPStan\Symfony\Configuration; use PHPStan\Symfony\XmlServiceMapFactory; use PHPStan\Testing\RuleTestCase; +use function class_exists; +use function interface_exists; /** * @extends RuleTestCase diff --git a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php index 46ea3b63..f975a179 100644 --- a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php +++ b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php @@ -7,7 +7,9 @@ use PHPStan\Symfony\Configuration; use PHPStan\Symfony\XmlServiceMapFactory; use PHPStan\Testing\RuleTestCase; +use PHPStan\Type\MethodTypeSpecifyingExtension; use PHPStan\Type\Symfony\ServiceTypeSpecifyingExtension; +use function class_exists; /** * @extends RuleTestCase @@ -21,7 +23,7 @@ protected function getRule(): Rule } /** - * @return \PHPStan\Type\MethodTypeSpecifyingExtension[] + * @return MethodTypeSpecifyingExtension[] */ protected function getMethodTypeSpecifyingExtensions(): array { diff --git a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php index e433a233..12893bab 100644 --- a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php +++ b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php @@ -7,6 +7,8 @@ use PHPStan\Symfony\Configuration; use PHPStan\Symfony\XmlServiceMapFactory; use PHPStan\Testing\RuleTestCase; +use function class_exists; +use function interface_exists; /** * @extends RuleTestCase diff --git a/tests/Rules/Symfony/ExampleAbstractController.php b/tests/Rules/Symfony/ExampleAbstractController.php index 4f0d2abd..22e5900e 100644 --- a/tests/Rules/Symfony/ExampleAbstractController.php +++ b/tests/Rules/Symfony/ExampleAbstractController.php @@ -3,6 +3,7 @@ namespace PHPStan\Rules\Symfony; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Bundle\FrameworkBundle\Test\TestContainer; final class ExampleAbstractController extends AbstractController { @@ -14,7 +15,7 @@ public function privateService(): void public function privateServiceInTestContainer(): void { - /** @var \Symfony\Bundle\FrameworkBundle\Test\TestContainer $container */ + /** @var TestContainer $container */ $container = doFoo(); $container->get('private'); } diff --git a/tests/Rules/Symfony/ExampleController.php b/tests/Rules/Symfony/ExampleController.php index edbdaaf5..5b9e1ca4 100644 --- a/tests/Rules/Symfony/ExampleController.php +++ b/tests/Rules/Symfony/ExampleController.php @@ -3,6 +3,7 @@ namespace PHPStan\Rules\Symfony; use Symfony\Bundle\FrameworkBundle\Controller\Controller; +use Symfony\Bundle\FrameworkBundle\Test\TestContainer; final class ExampleController extends Controller { @@ -14,7 +15,7 @@ public function privateService(): void public function privateServiceInTestContainer(): void { - /** @var \Symfony\Bundle\FrameworkBundle\Test\TestContainer $container */ + /** @var TestContainer $container */ $container = doFoo(); $container->get('private'); } diff --git a/tests/Rules/Symfony/ExampleServiceSubscriber.php b/tests/Rules/Symfony/ExampleServiceSubscriber.php index 3d7f294d..9005cf60 100644 --- a/tests/Rules/Symfony/ExampleServiceSubscriber.php +++ b/tests/Rules/Symfony/ExampleServiceSubscriber.php @@ -3,6 +3,7 @@ namespace PHPStan\Rules\Symfony; use Psr\Container\ContainerInterface; +use Symfony\Component\DependencyInjection\ParameterBag\ContainerBag; use Symfony\Contracts\Service\ServiceSubscriberInterface; final class ExampleServiceSubscriber implements ServiceSubscriberInterface @@ -24,7 +25,7 @@ public function privateService(): void public function containerParameter(): void { - /** @var \Symfony\Component\DependencyInjection\ParameterBag\ContainerBag $containerBag */ + /** @var ContainerBag $containerBag */ $containerBag = doFoo(); $containerBag->get('parameter_name'); } diff --git a/tests/Symfony/DefaultParameterMapTest.php b/tests/Symfony/DefaultParameterMapTest.php index caab6f97..8e2e5b58 100644 --- a/tests/Symfony/DefaultParameterMapTest.php +++ b/tests/Symfony/DefaultParameterMapTest.php @@ -26,19 +26,19 @@ public function testGetParameterEscapedPath(): void } /** - * @return \Iterator + * @return Iterator */ public function getParameterProvider(): Iterator { yield [ 'unknown', - function (?Parameter $parameter): void { + static function (?Parameter $parameter): void { self::assertNull($parameter); }, ]; yield [ 'app.string', - function (?Parameter $parameter): void { + static function (?Parameter $parameter): void { self::assertNotNull($parameter); self::assertSame('app.string', $parameter->getKey()); self::assertSame('abcdef', $parameter->getValue()); @@ -46,7 +46,7 @@ function (?Parameter $parameter): void { ]; yield [ 'app.int', - function (?Parameter $parameter): void { + static function (?Parameter $parameter): void { self::assertNotNull($parameter); self::assertSame('app.int', $parameter->getKey()); self::assertSame(123, $parameter->getValue()); @@ -54,7 +54,7 @@ function (?Parameter $parameter): void { ]; yield [ 'app.int_as_string', - function (?Parameter $parameter): void { + static function (?Parameter $parameter): void { self::assertNotNull($parameter); self::assertSame('app.int_as_string', $parameter->getKey()); self::assertSame('123', $parameter->getValue()); @@ -62,7 +62,7 @@ function (?Parameter $parameter): void { ]; yield [ 'app.float', - function (?Parameter $parameter): void { + static function (?Parameter $parameter): void { self::assertNotNull($parameter); self::assertSame('app.float', $parameter->getKey()); self::assertSame(123.45, $parameter->getValue()); @@ -70,7 +70,7 @@ function (?Parameter $parameter): void { ]; yield [ 'app.float_as_string', - function (?Parameter $parameter): void { + static function (?Parameter $parameter): void { self::assertNotNull($parameter); self::assertSame('app.float_as_string', $parameter->getKey()); self::assertSame('123.45', $parameter->getValue()); @@ -78,7 +78,7 @@ function (?Parameter $parameter): void { ]; yield [ 'app.boolean', - function (?Parameter $parameter): void { + static function (?Parameter $parameter): void { self::assertNotNull($parameter); self::assertSame('app.boolean', $parameter->getKey()); self::assertTrue($parameter->getValue()); @@ -86,7 +86,7 @@ function (?Parameter $parameter): void { ]; yield [ 'app.boolean_as_string', - function (?Parameter $parameter): void { + static function (?Parameter $parameter): void { self::assertNotNull($parameter); self::assertSame('app.boolean_as_string', $parameter->getKey()); self::assertSame('true', $parameter->getValue()); @@ -94,7 +94,7 @@ function (?Parameter $parameter): void { ]; yield [ 'app.list', - function (?Parameter $parameter): void { + static function (?Parameter $parameter): void { self::assertNotNull($parameter); self::assertSame('app.list', $parameter->getKey()); self::assertEquals(['en', 'es', 'fr'], $parameter->getValue()); @@ -102,7 +102,7 @@ function (?Parameter $parameter): void { ]; yield [ 'app.list_of_list', - function (?Parameter $parameter): void { + static function (?Parameter $parameter): void { self::assertNotNull($parameter); self::assertSame('app.list_of_list', $parameter->getKey()); self::assertEquals([ @@ -113,7 +113,7 @@ function (?Parameter $parameter): void { ]; yield [ 'app.map', - function (?Parameter $parameter): void { + static function (?Parameter $parameter): void { self::assertNotNull($parameter); self::assertSame('app.map', $parameter->getKey()); self::assertEquals([ @@ -125,7 +125,7 @@ function (?Parameter $parameter): void { ]; yield [ 'app.binary', - function (?Parameter $parameter): void { + static function (?Parameter $parameter): void { self::assertNotNull($parameter); self::assertSame('app.binary', $parameter->getKey()); self::assertSame('This is a Bell char ', $parameter->getValue()); @@ -133,7 +133,7 @@ function (?Parameter $parameter): void { ]; yield [ 'app.constant', - function (?Parameter $parameter): void { + static function (?Parameter $parameter): void { self::assertNotNull($parameter); self::assertSame('app.constant', $parameter->getKey()); self::assertSame('Y-m-d\TH:i:sP', $parameter->getValue()); diff --git a/tests/Symfony/DefaultServiceMapTest.php b/tests/Symfony/DefaultServiceMapTest.php index 0ffbb457..dc8487e5 100644 --- a/tests/Symfony/DefaultServiceMapTest.php +++ b/tests/Symfony/DefaultServiceMapTest.php @@ -26,19 +26,19 @@ public function testGetContainerEscapedPath(): void } /** - * @return \Iterator + * @return Iterator */ public function getServiceProvider(): Iterator { yield [ 'unknown', - function (?Service $service): void { + static function (?Service $service): void { self::assertNull($service); }, ]; yield [ 'withoutClass', - function (?Service $service): void { + static function (?Service $service): void { self::assertNotNull($service); self::assertSame('withoutClass', $service->getId()); self::assertNull($service->getClass()); @@ -49,7 +49,7 @@ function (?Service $service): void { ]; yield [ 'withClass', - function (?Service $service): void { + static function (?Service $service): void { self::assertNotNull($service); self::assertSame('withClass', $service->getId()); self::assertSame('Foo', $service->getClass()); @@ -60,7 +60,7 @@ function (?Service $service): void { ]; yield [ 'withoutPublic', - function (?Service $service): void { + static function (?Service $service): void { self::assertNotNull($service); self::assertSame('withoutPublic', $service->getId()); self::assertSame('Foo', $service->getClass()); @@ -71,7 +71,7 @@ function (?Service $service): void { ]; yield [ 'publicNotTrue', - function (?Service $service): void { + static function (?Service $service): void { self::assertNotNull($service); self::assertSame('publicNotTrue', $service->getId()); self::assertSame('Foo', $service->getClass()); @@ -82,7 +82,7 @@ function (?Service $service): void { ]; yield [ 'public', - function (?Service $service): void { + static function (?Service $service): void { self::assertNotNull($service); self::assertSame('public', $service->getId()); self::assertSame('Foo', $service->getClass()); @@ -93,7 +93,7 @@ function (?Service $service): void { ]; yield [ 'synthetic', - function (?Service $service): void { + static function (?Service $service): void { self::assertNotNull($service); self::assertSame('synthetic', $service->getId()); self::assertSame('Foo', $service->getClass()); @@ -104,7 +104,7 @@ function (?Service $service): void { ]; yield [ 'alias', - function (?Service $service): void { + static function (?Service $service): void { self::assertNotNull($service); self::assertSame('alias', $service->getId()); self::assertSame('Foo', $service->getClass()); diff --git a/tests/Type/Symfony/ExtensionTest.php b/tests/Type/Symfony/ExtensionTest.php index c0aa7f94..a0bb8115 100644 --- a/tests/Type/Symfony/ExtensionTest.php +++ b/tests/Type/Symfony/ExtensionTest.php @@ -5,6 +5,8 @@ use PHPStan\Testing\TypeInferenceTestCase; use ReflectionMethod; use Symfony\Component\HttpFoundation\Request; +use function class_exists; +use function strpos; class ExtensionTest extends TypeInferenceTestCase { @@ -58,8 +60,6 @@ public function dataFileAsserts(): iterable /** * @dataProvider dataFileAsserts - * @param string $assertType - * @param string $file * @param mixed ...$args */ public function testFileAsserts( diff --git a/tests/Type/Symfony/ExtensionTestWithoutContainer.php b/tests/Type/Symfony/ExtensionTestWithoutContainer.php index 997b7e02..fd1785c7 100644 --- a/tests/Type/Symfony/ExtensionTestWithoutContainer.php +++ b/tests/Type/Symfony/ExtensionTestWithoutContainer.php @@ -3,6 +3,7 @@ namespace PHPStan\Type\Symfony; use PHPStan\Testing\TypeInferenceTestCase; +use function class_exists; class ExtensionTestWithoutContainer extends TypeInferenceTestCase { @@ -30,8 +31,6 @@ public function dataAbstractController(): iterable /** * @dataProvider dataExampleController * @dataProvider dataAbstractController - * @param string $assertType - * @param string $file * @param mixed ...$args */ public function testFileAsserts( diff --git a/tests/Type/Symfony/ImpossibleCheckTypeMethodCallRuleTest.php b/tests/Type/Symfony/ImpossibleCheckTypeMethodCallRuleTest.php index ab247b9f..bde62b57 100644 --- a/tests/Type/Symfony/ImpossibleCheckTypeMethodCallRuleTest.php +++ b/tests/Type/Symfony/ImpossibleCheckTypeMethodCallRuleTest.php @@ -4,11 +4,12 @@ use PHPStan\Rules\Comparison\ImpossibleCheckTypeMethodCallRule; use PHPStan\Rules\Rule; +use PHPStan\Testing\RuleTestCase; /** - * @extends \PHPStan\Testing\RuleTestCase + * @extends RuleTestCase */ -class ImpossibleCheckTypeMethodCallRuleTest extends \PHPStan\Testing\RuleTestCase +class ImpossibleCheckTypeMethodCallRuleTest extends RuleTestCase { protected function getRule(): Rule diff --git a/tests/Type/Symfony/console_application_loader.php b/tests/Type/Symfony/console_application_loader.php index 0ef2e0ae..b6755aea 100644 --- a/tests/Type/Symfony/console_application_loader.php +++ b/tests/Type/Symfony/console_application_loader.php @@ -15,7 +15,7 @@ $application->add(new ExampleOptionCommand()); if (class_exists(LazyCommand::class)) { - $application->add(new LazyCommand('lazy-example-option', [], '', false, function () { + $application->add(new LazyCommand('lazy-example-option', [], '', false, static function () { return new ExampleOptionLazyCommand(); })); } else { From 5fe3c2668a88738e1696264bf49aa5513b5216c3 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Tue, 8 Feb 2022 12:55:19 +0100 Subject: [PATCH 062/208] Fix --- .../Symfony/ContainerInterfacePrivateServiceRuleTest.php | 2 +- .../Symfony/ContainerInterfaceUnknownServiceRuleTest.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php index 49afaff5..51513b09 100644 --- a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php +++ b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php @@ -32,7 +32,7 @@ public function testGetPrivateService(): void [ [ 'Service "private" is private.', - 12, + 13, ], ] ); diff --git a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php index 12893bab..4bd233e9 100644 --- a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php +++ b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php @@ -33,7 +33,7 @@ public function testGetPrivateService(): void [ [ 'Service "unknown" is not registered in the container.', - 24, + 25, ], ] ); @@ -52,7 +52,7 @@ public function testGetPrivateServiceInAbstractController(): void [ [ 'Service "unknown" is not registered in the container.', - 24, + 25, ], ] ); From 261955fc667e8c133077eb87863d74e0bb6d8b43 Mon Sep 17 00:00:00 2001 From: Grzegorz Korba Date: Fri, 25 Feb 2022 14:16:34 +0100 Subject: [PATCH 063/208] Support for parameterised service class --- composer.json | 2 ++ .../ServiceDynamicReturnTypeExtension.php | 32 +++++++++++++++++-- tests/Type/Symfony/container.xml | 3 ++ .../data/ExampleAbstractController.php | 2 ++ tests/Type/Symfony/data/ExampleController.php | 2 ++ 5 files changed, 39 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 08f5b685..5b5438ff 100644 --- a/composer.json +++ b/composer.json @@ -26,8 +26,10 @@ "phpstan/phpstan-phpunit": "^1.0", "phpstan/phpstan-strict-rules": "^1.0", "phpunit/phpunit": "^9.5", + "psr/container": "1.0 || 1.1.1", "symfony/config": "^4.2 || ^5.0", "symfony/console": "^4.0 || ^5.0", + "symfony/dependency-injection": "^4.0 || ^5.0", "symfony/form": "^4.0 || ^5.0", "symfony/framework-bundle": "^4.4 || ^5.0", "symfony/http-foundation": "^5.1", diff --git a/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php b/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php index 04608c17..cd3d2a46 100644 --- a/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php @@ -8,11 +8,14 @@ use PHPStan\Reflection\ParametersAcceptorSelector; use PHPStan\ShouldNotHappenException; use PHPStan\Symfony\Configuration; +use PHPStan\Symfony\ParameterMap; +use PHPStan\Symfony\ServiceDefinition; use PHPStan\Symfony\ServiceMap; use PHPStan\Type\Constant\ConstantBooleanType; use PHPStan\Type\DynamicMethodReturnTypeExtension; use PHPStan\Type\ObjectType; use PHPStan\Type\Type; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; use function in_array; final class ServiceDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension @@ -27,11 +30,20 @@ final class ServiceDynamicReturnTypeExtension implements DynamicMethodReturnType /** @var ServiceMap */ private $serviceMap; - public function __construct(string $className, Configuration $configuration, ServiceMap $symfonyServiceMap) + /** @var ParameterBag */ + private $parameterBag; + + public function __construct( + string $className, + Configuration $configuration, + ServiceMap $symfonyServiceMap, + ParameterMap $symfonyParameterMap + ) { $this->className = $className; $this->constantHassers = $configuration->hasConstantHassers(); $this->serviceMap = $symfonyServiceMap; + $this->parameterBag = $this->createParameterBag($symfonyParameterMap); } public function getClass(): string @@ -70,7 +82,7 @@ private function getGetTypeFromMethodCall( if ($serviceId !== null) { $service = $this->serviceMap->getService($serviceId); if ($service !== null && (!$service->isSynthetic() || $service->getClass() !== null)) { - return new ObjectType($service->getClass() ?? $serviceId); + return new ObjectType($this->determineServiceClass($service) ?? $serviceId); } } @@ -97,4 +109,20 @@ private function getHasTypeFromMethodCall( return $returnType; } + private function determineServiceClass(ServiceDefinition $service): ?string + { + return $this->parameterBag->resolveValue($service->getClass()); + } + + private function createParameterBag(ParameterMap $symfonyParameterMap): ParameterBag + { + $parameters = []; + + foreach ($symfonyParameterMap->getParameters() as $parameterDefinition) { + $parameters[$parameterDefinition->getKey()] = $parameterDefinition->getValue(); + } + + return new ParameterBag($parameters); + } + } diff --git a/tests/Type/Symfony/container.xml b/tests/Type/Symfony/container.xml index e6f8f03b..992289f2 100644 --- a/tests/Type/Symfony/container.xml +++ b/tests/Type/Symfony/container.xml @@ -1,6 +1,7 @@ + Foo abcdef 123 123 @@ -51,6 +52,8 @@ + + diff --git a/tests/Type/Symfony/data/ExampleAbstractController.php b/tests/Type/Symfony/data/ExampleAbstractController.php index 6dc6ef7e..6fdb281d 100644 --- a/tests/Type/Symfony/data/ExampleAbstractController.php +++ b/tests/Type/Symfony/data/ExampleAbstractController.php @@ -13,6 +13,8 @@ final class ExampleAbstractController extends AbstractController public function services(): void { assertType('Foo', $this->get('foo')); + assertType('Foo', $this->get('parameterised_foo')); + assertType('Foo\Bar', $this->get('parameterised_bar')); assertType('Synthetic', $this->get('synthetic')); assertType('object', $this->get('bar')); assertType('object', $this->get(doFoo())); diff --git a/tests/Type/Symfony/data/ExampleController.php b/tests/Type/Symfony/data/ExampleController.php index 8cd0c93a..b09cc25d 100644 --- a/tests/Type/Symfony/data/ExampleController.php +++ b/tests/Type/Symfony/data/ExampleController.php @@ -13,6 +13,8 @@ final class ExampleController extends Controller public function services(): void { assertType('Foo', $this->get('foo')); + assertType('Foo', $this->get('parameterised_foo')); + assertType('Foo\Bar', $this->get('parameterised_bar')); assertType('Synthetic', $this->get('synthetic')); assertType('object', $this->get('bar')); assertType('object', $this->get(doFoo())); From 8ef80f5c4f70ebd31a55647bc082ea9df4bf0132 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Fri, 25 Feb 2022 20:36:13 +0100 Subject: [PATCH 064/208] Fix for ParameterBag class not present --- .../ServiceDynamicReturnTypeExtension.php | 54 +++++++++++++------ 1 file changed, 38 insertions(+), 16 deletions(-) diff --git a/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php b/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php index cd3d2a46..a2168a91 100644 --- a/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php @@ -16,6 +16,7 @@ use PHPStan\Type\ObjectType; use PHPStan\Type\Type; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; +use function class_exists; use function in_array; final class ServiceDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension @@ -30,7 +31,10 @@ final class ServiceDynamicReturnTypeExtension implements DynamicMethodReturnType /** @var ServiceMap */ private $serviceMap; - /** @var ParameterBag */ + /** @var ParameterMap */ + private $parameterMap; + + /** @var ParameterBag|null */ private $parameterBag; public function __construct( @@ -43,7 +47,7 @@ public function __construct( $this->className = $className; $this->constantHassers = $configuration->hasConstantHassers(); $this->serviceMap = $symfonyServiceMap; - $this->parameterBag = $this->createParameterBag($symfonyParameterMap); + $this->parameterMap = $symfonyParameterMap; } public function getClass(): string @@ -78,17 +82,46 @@ private function getGetTypeFromMethodCall( return $returnType; } + $parameterBag = $this->tryGetParameterBag(); + if ($parameterBag === null) { + return $returnType; + } + $serviceId = $this->serviceMap::getServiceIdFromNode($methodCall->getArgs()[0]->value, $scope); if ($serviceId !== null) { $service = $this->serviceMap->getService($serviceId); if ($service !== null && (!$service->isSynthetic() || $service->getClass() !== null)) { - return new ObjectType($this->determineServiceClass($service) ?? $serviceId); + return new ObjectType($this->determineServiceClass($parameterBag, $service) ?? $serviceId); } } return $returnType; } + private function tryGetParameterBag(): ?ParameterBag + { + if ($this->parameterBag !== null) { + return $this->parameterBag; + } + + return $this->parameterBag = $this->tryCreateParameterBag(); + } + + private function tryCreateParameterBag(): ?ParameterBag + { + if (!class_exists(ParameterBag::class)) { + return null; + } + + $parameters = []; + + foreach ($this->parameterMap->getParameters() as $parameterDefinition) { + $parameters[$parameterDefinition->getKey()] = $parameterDefinition->getValue(); + } + + return new ParameterBag($parameters); + } + private function getHasTypeFromMethodCall( MethodReflection $methodReflection, MethodCall $methodCall, @@ -109,20 +142,9 @@ private function getHasTypeFromMethodCall( return $returnType; } - private function determineServiceClass(ServiceDefinition $service): ?string - { - return $this->parameterBag->resolveValue($service->getClass()); - } - - private function createParameterBag(ParameterMap $symfonyParameterMap): ParameterBag + private function determineServiceClass(ParameterBag $parameterBag, ServiceDefinition $service): ?string { - $parameters = []; - - foreach ($symfonyParameterMap->getParameters() as $parameterDefinition) { - $parameters[$parameterDefinition->getKey()] = $parameterDefinition->getValue(); - } - - return new ParameterBag($parameters); + return $parameterBag->resolveValue($service->getClass()); } } From b6bcbea8204fd6e09c5587228ff448d0414de54c Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Mon, 7 Mar 2022 01:48:35 +0000 Subject: [PATCH 065/208] Update actions/checkout action to v3 --- .github/workflows/build.yml | 8 ++++---- .github/workflows/release.yml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bccd397b..7afaf7bf 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -25,7 +25,7 @@ jobs: steps: - name: "Checkout" - uses: "actions/checkout@v2" + uses: actions/checkout@v3 - name: "Install PHP" uses: "shivammathur/setup-php@v2" @@ -53,7 +53,7 @@ jobs: steps: - name: "Checkout" - uses: "actions/checkout@v2" + uses: actions/checkout@v3 - name: "Install PHP" uses: "shivammathur/setup-php@v2" @@ -96,7 +96,7 @@ jobs: steps: - name: "Checkout" - uses: "actions/checkout@v2" + uses: actions/checkout@v3 - name: "Install PHP" uses: "shivammathur/setup-php@v2" @@ -139,7 +139,7 @@ jobs: steps: - name: "Checkout" - uses: "actions/checkout@v2" + uses: actions/checkout@v3 - name: "Install PHP" uses: "shivammathur/setup-php@v2" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0ebed840..5fed0458 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,7 +14,7 @@ jobs: steps: - name: "Checkout" - uses: "actions/checkout@v2" + uses: actions/checkout@v3 - name: Generate changelog id: changelog From 79a26b97a85af36367fc90fe2cef481e66c27bf3 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Mon, 7 Mar 2022 01:48:32 +0000 Subject: [PATCH 066/208] Update dependency slevomat/coding-standard to v7.0.19 --- build-cs/composer.lock | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/build-cs/composer.lock b/build-cs/composer.lock index 70af78f9..96177edd 100644 --- a/build-cs/composer.lock +++ b/build-cs/composer.lock @@ -200,32 +200,32 @@ }, { "name": "slevomat/coding-standard", - "version": "7.0.18", + "version": "7.0.19", "source": { "type": "git", "url": "https://github.com/slevomat/coding-standard.git", - "reference": "b81ac84f41a4797dc25c8ede1b0718e2a74be0fc" + "reference": "bef66a43815bbf9b5f49775e9ded3f7c6ba0cc37" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/b81ac84f41a4797dc25c8ede1b0718e2a74be0fc", - "reference": "b81ac84f41a4797dc25c8ede1b0718e2a74be0fc", + "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/bef66a43815bbf9b5f49775e9ded3f7c6ba0cc37", + "reference": "bef66a43815bbf9b5f49775e9ded3f7c6ba0cc37", "shasum": "" }, "require": { "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7", "php": "^7.1 || ^8.0", "phpstan/phpdoc-parser": "^1.0.0", - "squizlabs/php_codesniffer": "^3.6.1" + "squizlabs/php_codesniffer": "^3.6.2" }, "require-dev": { - "phing/phing": "2.17.0", - "php-parallel-lint/php-parallel-lint": "1.3.1", - "phpstan/phpstan": "1.2.0", + "phing/phing": "2.17.2", + "php-parallel-lint/php-parallel-lint": "1.3.2", + "phpstan/phpstan": "1.4.6", "phpstan/phpstan-deprecation-rules": "1.0.0", "phpstan/phpstan-phpunit": "1.0.0", "phpstan/phpstan-strict-rules": "1.1.0", - "phpunit/phpunit": "7.5.20|8.5.21|9.5.10" + "phpunit/phpunit": "7.5.20|8.5.21|9.5.16" }, "type": "phpcodesniffer-standard", "extra": { @@ -245,7 +245,7 @@ "description": "Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.", "support": { "issues": "https://github.com/slevomat/coding-standard/issues", - "source": "https://github.com/slevomat/coding-standard/tree/7.0.18" + "source": "https://github.com/slevomat/coding-standard/tree/7.0.19" }, "funding": [ { @@ -257,7 +257,7 @@ "type": "tidelift" } ], - "time": "2021-12-07T17:19:06+00:00" + "time": "2022-03-01T18:01:41+00:00" }, { "name": "squizlabs/php_codesniffer", From 4bdffcf1033b6c60662a28419d0192e0dda5684f Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Tue, 22 Mar 2022 22:18:26 +0100 Subject: [PATCH 067/208] Update DenormalizerInterface::denormalize --- .../Component/Serializer/Normalizer/DenormalizerInterface.stub | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stubs/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.stub b/stubs/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.stub index 83c6ddff..da4ab673 100644 --- a/stubs/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.stub +++ b/stubs/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.stub @@ -9,7 +9,7 @@ interface DenormalizerInterface * @param string $type * @param string|null $format * @param array $context - * @return object|array + * @return mixed */ public function denormalize($data, $type, $format = null, array $context = []); From 31a8e6366a628ce4a788cee06f2292a965832fe0 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Mon, 28 Mar 2022 09:40:14 +0000 Subject: [PATCH 068/208] Update dependency slevomat/coding-standard to v7.0.20 --- build-cs/composer.lock | 37 ++++++++++++++++--------------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/build-cs/composer.lock b/build-cs/composer.lock index 96177edd..54c9a22c 100644 --- a/build-cs/composer.lock +++ b/build-cs/composer.lock @@ -151,35 +151,30 @@ }, { "name": "phpstan/phpdoc-parser", - "version": "1.2.0", + "version": "1.3.0", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "dbc093d7af60eff5cd575d2ed761b15ed40bd08e" + "reference": "c08946968bda74869e696982b0af40a9a8784c84" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/dbc093d7af60eff5cd575d2ed761b15ed40bd08e", - "reference": "dbc093d7af60eff5cd575d2ed761b15ed40bd08e", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/c08946968bda74869e696982b0af40a9a8784c84", + "reference": "c08946968bda74869e696982b0af40a9a8784c84", "shasum": "" }, "require": { - "php": "^7.1 || ^8.0" + "php": "^7.2 || ^8.0" }, "require-dev": { "php-parallel-lint/php-parallel-lint": "^1.2", "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^1.0", + "phpstan/phpstan": "^1.5", "phpstan/phpstan-strict-rules": "^1.0", "phpunit/phpunit": "^9.5", "symfony/process": "^5.2" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, "autoload": { "psr-4": { "PHPStan\\PhpDocParser\\": [ @@ -194,22 +189,22 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.2.0" + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.3.0" }, - "time": "2021-09-16T20:46:02+00:00" + "time": "2022-03-28T07:53:31+00:00" }, { "name": "slevomat/coding-standard", - "version": "7.0.19", + "version": "7.0.20", "source": { "type": "git", "url": "https://github.com/slevomat/coding-standard.git", - "reference": "bef66a43815bbf9b5f49775e9ded3f7c6ba0cc37" + "reference": "cbfadfe34c2c29473bf1e891306b3950b3b4350b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/bef66a43815bbf9b5f49775e9ded3f7c6ba0cc37", - "reference": "bef66a43815bbf9b5f49775e9ded3f7c6ba0cc37", + "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/cbfadfe34c2c29473bf1e891306b3950b3b4350b", + "reference": "cbfadfe34c2c29473bf1e891306b3950b3b4350b", "shasum": "" }, "require": { @@ -221,11 +216,11 @@ "require-dev": { "phing/phing": "2.17.2", "php-parallel-lint/php-parallel-lint": "1.3.2", - "phpstan/phpstan": "1.4.6", + "phpstan/phpstan": "1.4.10|1.5.0", "phpstan/phpstan-deprecation-rules": "1.0.0", "phpstan/phpstan-phpunit": "1.0.0", "phpstan/phpstan-strict-rules": "1.1.0", - "phpunit/phpunit": "7.5.20|8.5.21|9.5.16" + "phpunit/phpunit": "7.5.20|8.5.21|9.5.19" }, "type": "phpcodesniffer-standard", "extra": { @@ -245,7 +240,7 @@ "description": "Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.", "support": { "issues": "https://github.com/slevomat/coding-standard/issues", - "source": "https://github.com/slevomat/coding-standard/tree/7.0.19" + "source": "https://github.com/slevomat/coding-standard/tree/7.0.20" }, "funding": [ { @@ -257,7 +252,7 @@ "type": "tidelift" } ], - "time": "2022-03-01T18:01:41+00:00" + "time": "2022-03-25T09:43:20+00:00" }, { "name": "squizlabs/php_codesniffer", From 02dceb254659bd9e7db4dee623add012853b0b42 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Mon, 28 Mar 2022 11:52:17 +0200 Subject: [PATCH 069/208] Drop support for PHP 7.1, require PHPStan 1.5.0 --- .github/workflows/build.yml | 12 +++--------- composer.json | 4 ++-- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7afaf7bf..8e19c3ed 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,7 +16,6 @@ jobs: strategy: matrix: php-version: - - "7.1" - "7.2" - "7.3" - "7.4" @@ -40,7 +39,7 @@ jobs: run: "composer install --no-interaction --no-progress" - name: "Downgrade PHPUnit" - if: matrix.php-version == '7.1' || matrix.php-version == '7.2' || matrix.php-version == '7.3' + if: matrix.php-version == '7.2' || matrix.php-version == '7.3' run: "composer require --dev phpunit/phpunit:^7.5.20 --update-with-dependencies" - name: "Lint" @@ -81,7 +80,6 @@ jobs: fail-fast: false matrix: php-version: - - "7.1" - "7.2" - "7.3" - "7.4" @@ -90,9 +88,6 @@ jobs: dependencies: - "lowest" - "highest" - exclude: - - php-version: "7.1" - dependencies: "highest" steps: - name: "Checkout" @@ -113,7 +108,7 @@ jobs: run: "composer update --no-interaction --no-progress" - name: "Downgrade PHPUnit" - if: matrix.php-version == '7.1' || matrix.php-version == '7.2' || matrix.php-version == '7.3' + if: matrix.php-version == '7.2' || matrix.php-version == '7.3' run: "composer require --dev phpunit/phpunit:^7.5.20 --update-with-dependencies" - name: "Tests" @@ -127,7 +122,6 @@ jobs: fail-fast: false matrix: php-version: - - "7.1" - "7.2" - "7.3" - "7.4" @@ -158,7 +152,7 @@ jobs: run: "composer update --no-interaction --no-progress" - name: "Downgrade PHPUnit" - if: matrix.php-version == '7.1' || matrix.php-version == '7.2' || matrix.php-version == '7.3' + if: matrix.php-version == '7.2' || matrix.php-version == '7.3' run: "composer require --dev phpunit/phpunit:^7.5.20 --update-with-dependencies" - name: "PHPStan" diff --git a/composer.json b/composer.json index 5b5438ff..7abefc05 100644 --- a/composer.json +++ b/composer.json @@ -13,9 +13,9 @@ } ], "require": { - "php": "^7.1 || ^8.0", + "php": "^7.2 || ^8.0", "ext-simplexml": "*", - "phpstan/phpstan": "^1.4" + "phpstan/phpstan": "^1.5" }, "conflict": { "symfony/framework-bundle": "<3.0" From 31d7dad0da29322d556524d3645d4e768f098dfe Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Mon, 28 Mar 2022 11:53:32 +0200 Subject: [PATCH 070/208] Update workflow --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8e19c3ed..38309f6a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,7 +6,7 @@ on: pull_request: push: branches: - - "master" + - "1.2.x" jobs: lint: From 513f395f79da61e6c74cf92e6e3842db1f96673e Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Mon, 28 Mar 2022 11:53:49 +0200 Subject: [PATCH 071/208] Drop alias --- composer.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/composer.json b/composer.json index 7abefc05..723c1163 100644 --- a/composer.json +++ b/composer.json @@ -44,9 +44,6 @@ "sort-packages": true }, "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - }, "phpstan": { "includes": [ "extension.neon", From 7953474b9af0872baaa469ddd041887aba0a3a63 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Mon, 4 Apr 2022 01:16:16 +0000 Subject: [PATCH 072/208] Update dependency slevomat/coding-standard to v7.1 --- build-cs/composer.lock | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/build-cs/composer.lock b/build-cs/composer.lock index 54c9a22c..5a10fffd 100644 --- a/build-cs/composer.lock +++ b/build-cs/composer.lock @@ -151,16 +151,16 @@ }, { "name": "phpstan/phpdoc-parser", - "version": "1.3.0", + "version": "1.4.2", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "c08946968bda74869e696982b0af40a9a8784c84" + "reference": "4cb3021a4e10ffe3d5f94a4c34cf4b3f6de2fa3d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/c08946968bda74869e696982b0af40a9a8784c84", - "reference": "c08946968bda74869e696982b0af40a9a8784c84", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/4cb3021a4e10ffe3d5f94a4c34cf4b3f6de2fa3d", + "reference": "4cb3021a4e10ffe3d5f94a4c34cf4b3f6de2fa3d", "shasum": "" }, "require": { @@ -189,36 +189,36 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.3.0" + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.4.2" }, - "time": "2022-03-28T07:53:31+00:00" + "time": "2022-03-30T13:33:37+00:00" }, { "name": "slevomat/coding-standard", - "version": "7.0.20", + "version": "7.1", "source": { "type": "git", "url": "https://github.com/slevomat/coding-standard.git", - "reference": "cbfadfe34c2c29473bf1e891306b3950b3b4350b" + "reference": "b521bd358b5f7a7d69e9637fd139e036d8adeb6f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/cbfadfe34c2c29473bf1e891306b3950b3b4350b", - "reference": "cbfadfe34c2c29473bf1e891306b3950b3b4350b", + "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/b521bd358b5f7a7d69e9637fd139e036d8adeb6f", + "reference": "b521bd358b5f7a7d69e9637fd139e036d8adeb6f", "shasum": "" }, "require": { "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7", - "php": "^7.1 || ^8.0", - "phpstan/phpdoc-parser": "^1.0.0", + "php": "^7.2 || ^8.0", + "phpstan/phpdoc-parser": "^1.4.1", "squizlabs/php_codesniffer": "^3.6.2" }, "require-dev": { "phing/phing": "2.17.2", "php-parallel-lint/php-parallel-lint": "1.3.2", - "phpstan/phpstan": "1.4.10|1.5.0", + "phpstan/phpstan": "1.4.10|1.5.2", "phpstan/phpstan-deprecation-rules": "1.0.0", - "phpstan/phpstan-phpunit": "1.0.0", + "phpstan/phpstan-phpunit": "1.0.0|1.1.0", "phpstan/phpstan-strict-rules": "1.1.0", "phpunit/phpunit": "7.5.20|8.5.21|9.5.19" }, @@ -240,7 +240,7 @@ "description": "Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.", "support": { "issues": "https://github.com/slevomat/coding-standard/issues", - "source": "https://github.com/slevomat/coding-standard/tree/7.0.20" + "source": "https://github.com/slevomat/coding-standard/tree/7.1" }, "funding": [ { @@ -252,7 +252,7 @@ "type": "tidelift" } ], - "time": "2022-03-25T09:43:20+00:00" + "time": "2022-03-29T12:44:16+00:00" }, { "name": "squizlabs/php_codesniffer", @@ -318,5 +318,5 @@ "prefer-lowest": false, "platform": [], "platform-dev": [], - "plugin-api-version": "2.2.0" + "plugin-api-version": "2.3.0" } From e32e40e7f82b0fcb3e0dd4a38362034f895a11c5 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Mon, 18 Apr 2022 21:35:06 +0200 Subject: [PATCH 073/208] Require PHPStan 1.6.0 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 723c1163..a563528a 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ "require": { "php": "^7.2 || ^8.0", "ext-simplexml": "*", - "phpstan/phpstan": "^1.5" + "phpstan/phpstan": "^1.6" }, "conflict": { "symfony/framework-bundle": "<3.0" From 811fb232c30000b7572c715f049a7fe6b9676d31 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Mon, 18 Apr 2022 21:35:14 +0200 Subject: [PATCH 074/208] Fix --- src/Symfony/XmlParameterMapFactory.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Symfony/XmlParameterMapFactory.php b/src/Symfony/XmlParameterMapFactory.php index 0891423a..1467d050 100644 --- a/src/Symfony/XmlParameterMapFactory.php +++ b/src/Symfony/XmlParameterMapFactory.php @@ -3,6 +3,7 @@ namespace PHPStan\Symfony; use InvalidArgumentException; +use PHPStan\ShouldNotHappenException; use SimpleXMLElement; use function base64_decode; use function file_get_contents; @@ -67,7 +68,11 @@ private function getNodeValue(SimpleXMLElement $def) switch ((string) $attrs->type) { case 'collection': $value = []; - foreach ($def->children() as $child) { + $children = $def->children(); + if ($children === null) { + throw new ShouldNotHappenException(); + } + foreach ($children as $child) { /** @var SimpleXMLElement $childAttrs */ $childAttrs = $child->attributes(); From b1c515245615797c081105be04d5741c3deda843 Mon Sep 17 00:00:00 2001 From: Thomas Gnandt Date: Fri, 29 Apr 2022 14:56:07 +0200 Subject: [PATCH 075/208] add documentation to load DI helper --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 7fe1a8bb..663e0381 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,9 @@ parameters: # If you're using PHP config files for Symfony 5.3+, you also need this for auto-loading of `Symfony\Config`: scanDirectories: - var/cache/dev/Symfony/Config + # If you're using PHP config files you need this to load the helper functions (i.e. service()) + scanFiles: + - vendor/symfony/dependency-injection/Loader/Configurator/ContainerConfigurator.php ``` ## Constant hassers From 94e6c34d81ee842d9ace775698c86596aaed3ffc Mon Sep 17 00:00:00 2001 From: Thomas Gnandt Date: Thu, 5 May 2022 15:58:38 +0200 Subject: [PATCH 076/208] Revert "add documentation to load DI helper" This reverts commit b1c515245615797c081105be04d5741c3deda843. --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index 663e0381..7fe1a8bb 100644 --- a/README.md +++ b/README.md @@ -69,9 +69,6 @@ parameters: # If you're using PHP config files for Symfony 5.3+, you also need this for auto-loading of `Symfony\Config`: scanDirectories: - var/cache/dev/Symfony/Config - # If you're using PHP config files you need this to load the helper functions (i.e. service()) - scanFiles: - - vendor/symfony/dependency-injection/Loader/Configurator/ContainerConfigurator.php ``` ## Constant hassers From 063ce41831590e55994c47ba734f3defacb14f4a Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Mon, 9 May 2022 01:47:25 +0000 Subject: [PATCH 077/208] Update dependency slevomat/coding-standard to v7.2.0 --- build-cs/composer.lock | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/build-cs/composer.lock b/build-cs/composer.lock index 5a10fffd..6e7fb818 100644 --- a/build-cs/composer.lock +++ b/build-cs/composer.lock @@ -151,16 +151,16 @@ }, { "name": "phpstan/phpdoc-parser", - "version": "1.4.2", + "version": "1.5.1", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "4cb3021a4e10ffe3d5f94a4c34cf4b3f6de2fa3d" + "reference": "981cc368a216c988e862a75e526b6076987d1b50" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/4cb3021a4e10ffe3d5f94a4c34cf4b3f6de2fa3d", - "reference": "4cb3021a4e10ffe3d5f94a4c34cf4b3f6de2fa3d", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/981cc368a216c988e862a75e526b6076987d1b50", + "reference": "981cc368a216c988e862a75e526b6076987d1b50", "shasum": "" }, "require": { @@ -189,38 +189,38 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.4.2" + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.5.1" }, - "time": "2022-03-30T13:33:37+00:00" + "time": "2022-05-05T11:32:40+00:00" }, { "name": "slevomat/coding-standard", - "version": "7.1", + "version": "7.2.0", "source": { "type": "git", "url": "https://github.com/slevomat/coding-standard.git", - "reference": "b521bd358b5f7a7d69e9637fd139e036d8adeb6f" + "reference": "b4f96a8beea515d2d89141b7b9ad72f526d84071" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/b521bd358b5f7a7d69e9637fd139e036d8adeb6f", - "reference": "b521bd358b5f7a7d69e9637fd139e036d8adeb6f", + "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/b4f96a8beea515d2d89141b7b9ad72f526d84071", + "reference": "b4f96a8beea515d2d89141b7b9ad72f526d84071", "shasum": "" }, "require": { "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7", "php": "^7.2 || ^8.0", - "phpstan/phpdoc-parser": "^1.4.1", + "phpstan/phpdoc-parser": "^1.5.1", "squizlabs/php_codesniffer": "^3.6.2" }, "require-dev": { - "phing/phing": "2.17.2", + "phing/phing": "2.17.3", "php-parallel-lint/php-parallel-lint": "1.3.2", - "phpstan/phpstan": "1.4.10|1.5.2", + "phpstan/phpstan": "1.4.10|1.6.7", "phpstan/phpstan-deprecation-rules": "1.0.0", - "phpstan/phpstan-phpunit": "1.0.0|1.1.0", - "phpstan/phpstan-strict-rules": "1.1.0", - "phpunit/phpunit": "7.5.20|8.5.21|9.5.19" + "phpstan/phpstan-phpunit": "1.0.0|1.1.1", + "phpstan/phpstan-strict-rules": "1.2.3", + "phpunit/phpunit": "7.5.20|8.5.21|9.5.20" }, "type": "phpcodesniffer-standard", "extra": { @@ -240,7 +240,7 @@ "description": "Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.", "support": { "issues": "https://github.com/slevomat/coding-standard/issues", - "source": "https://github.com/slevomat/coding-standard/tree/7.1" + "source": "https://github.com/slevomat/coding-standard/tree/7.2.0" }, "funding": [ { @@ -252,7 +252,7 @@ "type": "tidelift" } ], - "time": "2022-03-29T12:44:16+00:00" + "time": "2022-05-06T10:58:42+00:00" }, { "name": "squizlabs/php_codesniffer", From 5d2d5ad60dbdefd234b5e58ef43fc3f077a312ed Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Fri, 27 May 2022 10:37:19 +0200 Subject: [PATCH 078/208] Fix formInterface stub --- stubs/Symfony/Component/Form/FormInterface.stub | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/stubs/Symfony/Component/Form/FormInterface.stub b/stubs/Symfony/Component/Form/FormInterface.stub index 868c4ae2..6960cce5 100644 --- a/stubs/Symfony/Component/Form/FormInterface.stub +++ b/stubs/Symfony/Component/Form/FormInterface.stub @@ -3,9 +3,10 @@ namespace Symfony\Component\Form; /** - * @extends \Traversable + * @extends \ArrayAccess + * @extends \Traversable */ -interface FormInterface extends \Traversable +interface FormInterface extends \ArrayAccess, \Traversable, \Countable { } From 2063d60f1bfe97c5702a839b3bc68d7f3fa75318 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Sat, 28 May 2022 17:11:34 +0200 Subject: [PATCH 079/208] DIC parameters: do not generalize string-indexed arrays --- .../ParameterDynamicReturnTypeExtension.php | 24 +++++++++++++++ tests/Type/Symfony/container.xml | 30 +++++++++++++++++++ .../data/ExampleAbstractController.php | 12 ++++++-- ...mpleAbstractControllerWithoutContainer.php | 3 ++ tests/Type/Symfony/data/ExampleController.php | 12 ++++++-- .../ExampleControllerWithoutContainer.php | 3 ++ 6 files changed, 78 insertions(+), 6 deletions(-) diff --git a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php index 0fa8e7bc..6a197a8f 100644 --- a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php @@ -14,6 +14,8 @@ use PHPStan\Type\BooleanType; use PHPStan\Type\Constant\ConstantArrayType; use PHPStan\Type\Constant\ConstantBooleanType; +use PHPStan\Type\Constant\ConstantIntegerType; +use PHPStan\Type\Constant\ConstantStringType; use PHPStan\Type\ConstantType; use PHPStan\Type\DynamicMethodReturnTypeExtension; use PHPStan\Type\FloatType; @@ -35,6 +37,7 @@ use function count; use function in_array; use function is_array; +use function is_int; use function is_string; use function preg_match; use function strlen; @@ -139,6 +142,27 @@ private function getGetTypeFromMethodCall( private function generalizeTypeFromValue(Scope $scope, $value): Type { if (is_array($value) && $value !== []) { + $hasOnlyStringKey = true; + foreach (array_keys($value) as $key) { + if (is_int($key)) { + $hasOnlyStringKey = false; + break; + } + } + + if ($hasOnlyStringKey) { + $keyTypes = []; + $valueTypes = []; + foreach ($value as $key => $element) { + /** @var ConstantIntegerType|ConstantStringType $keyType */ + $keyType = $scope->getTypeFromValue($key); + $keyTypes[] = $keyType; + $valueTypes[] = $this->generalizeTypeFromValue($scope, $element); + } + + return new ConstantArrayType($keyTypes, $valueTypes); + } + return $this->generalizeType( new ArrayType( TypeCombinator::union(...array_map(function ($item) use ($scope): Type { diff --git a/tests/Type/Symfony/container.xml b/tests/Type/Symfony/container.xml index 992289f2..5a14f9b5 100644 --- a/tests/Type/Symfony/container.xml +++ b/tests/Type/Symfony/container.xml @@ -37,6 +37,36 @@ another value + + + the name + the value + + + another name + another value + + + + %env(string:APP_STRING)% + %env(string:APP_STRING)% + %env(string:APP_STRING)% + + + %env(string:APP_STRING)% + %env(string:APP_STRING)% + %env(string:APP_STRING)% + + + + %env(string:APP_STRING)% + + %env(string:APP_STRING)% + %env(string:APP_STRING)% + %env(string:APP_STRING)% + + + value of a value of b diff --git a/tests/Type/Symfony/data/ExampleAbstractController.php b/tests/Type/Symfony/data/ExampleAbstractController.php index 6fdb281d..8f599d7b 100644 --- a/tests/Type/Symfony/data/ExampleAbstractController.php +++ b/tests/Type/Symfony/data/ExampleAbstractController.php @@ -68,9 +68,15 @@ public function parameters(ContainerInterface $container, ParameterBagInterface assertType("array>", $container->getParameter('app.list_of_list')); assertType("array>", $parameterBag->get('app.list_of_list')); assertType("array>", $this->getParameter('app.list_of_list')); - assertType("array", $container->getParameter('app.map')); - assertType("array", $parameterBag->get('app.map')); - assertType("array", $this->getParameter('app.map')); + assertType("array>", $container->getParameter('app.array_of_list')); + assertType("array>", $parameterBag->get('app.array_of_list')); + assertType("array>", $this->getParameter('app.array_of_list')); + assertType("array{url: string, endpoint: string, version: string, payment: array{default: array{username: string, password: string, signature: string}}, api: array{mode: string, default: array{username: string, password: string, signature: string}}}", $container->getParameter('app.list_of_things')); + assertType("array{url: string, endpoint: string, version: string, payment: array{default: array{username: string, password: string, signature: string}}, api: array{mode: string, default: array{username: string, password: string, signature: string}}}", $parameterBag->get('app.list_of_things')); + assertType("array{url: string, endpoint: string, version: string, payment: array{default: array{username: string, password: string, signature: string}}, api: array{mode: string, default: array{username: string, password: string, signature: string}}}", $this->getParameter('app.list_of_things')); + assertType("array{a: string, b: string, c: string}", $container->getParameter('app.map')); + assertType("array{a: string, b: string, c: string}", $parameterBag->get('app.map')); + assertType("array{a: string, b: string, c: string}", $this->getParameter('app.map')); assertType("string", $container->getParameter('app.binary')); assertType("string", $parameterBag->get('app.binary')); assertType("string", $this->getParameter('app.binary')); diff --git a/tests/Type/Symfony/data/ExampleAbstractControllerWithoutContainer.php b/tests/Type/Symfony/data/ExampleAbstractControllerWithoutContainer.php index 26d0f366..edc6438a 100644 --- a/tests/Type/Symfony/data/ExampleAbstractControllerWithoutContainer.php +++ b/tests/Type/Symfony/data/ExampleAbstractControllerWithoutContainer.php @@ -65,6 +65,9 @@ public function parameters(ContainerInterface $container, ParameterBagInterface assertType('array|bool|float|int|string|null', $container->getParameter('app.list_of_list')); assertType('array|bool|float|int|string|null', $parameterBag->get('app.list_of_list')); assertType('array|bool|float|int|string|null', $this->getParameter('app.list_of_list')); + assertType('array|bool|float|int|string|null', $container->getParameter('app.array_of_list')); + assertType('array|bool|float|int|string|null', $parameterBag->get('app.array_of_list')); + assertType('array|bool|float|int|string|null', $this->getParameter('app.array_of_list')); assertType('array|bool|float|int|string|null', $container->getParameter('app.map')); assertType('array|bool|float|int|string|null', $parameterBag->get('app.map')); assertType('array|bool|float|int|string|null', $this->getParameter('app.map')); diff --git a/tests/Type/Symfony/data/ExampleController.php b/tests/Type/Symfony/data/ExampleController.php index b09cc25d..89d2b5f8 100644 --- a/tests/Type/Symfony/data/ExampleController.php +++ b/tests/Type/Symfony/data/ExampleController.php @@ -68,9 +68,15 @@ public function parameters(ContainerInterface $container, ParameterBagInterface assertType("array>", $container->getParameter('app.list_of_list')); assertType("array>", $parameterBag->get('app.list_of_list')); assertType("array>", $this->getParameter('app.list_of_list')); - assertType("array", $container->getParameter('app.map')); - assertType("array", $parameterBag->get('app.map')); - assertType("array", $this->getParameter('app.map')); + assertType("array>", $container->getParameter('app.array_of_list')); + assertType("array>", $parameterBag->get('app.array_of_list')); + assertType("array>", $this->getParameter('app.array_of_list')); + assertType("array{url: string, endpoint: string, version: string, payment: array{default: array{username: string, password: string, signature: string}}, api: array{mode: string, default: array{username: string, password: string, signature: string}}}", $container->getParameter('app.list_of_things')); + assertType("array{url: string, endpoint: string, version: string, payment: array{default: array{username: string, password: string, signature: string}}, api: array{mode: string, default: array{username: string, password: string, signature: string}}}", $parameterBag->get('app.list_of_things')); + assertType("array{url: string, endpoint: string, version: string, payment: array{default: array{username: string, password: string, signature: string}}, api: array{mode: string, default: array{username: string, password: string, signature: string}}}", $this->getParameter('app.list_of_things')); + assertType("array{a: string, b: string, c: string}", $container->getParameter('app.map')); + assertType("array{a: string, b: string, c: string}", $parameterBag->get('app.map')); + assertType("array{a: string, b: string, c: string}", $this->getParameter('app.map')); assertType("string", $container->getParameter('app.binary')); assertType("string", $parameterBag->get('app.binary')); assertType("string", $this->getParameter('app.binary')); diff --git a/tests/Type/Symfony/data/ExampleControllerWithoutContainer.php b/tests/Type/Symfony/data/ExampleControllerWithoutContainer.php index fc48c0e5..2e48dc80 100644 --- a/tests/Type/Symfony/data/ExampleControllerWithoutContainer.php +++ b/tests/Type/Symfony/data/ExampleControllerWithoutContainer.php @@ -65,6 +65,9 @@ public function parameters(ContainerInterface $container, ParameterBagInterface assertType('array|bool|float|int|string|null', $container->getParameter('app.list_of_list')); assertType('array|bool|float|int|string|null', $parameterBag->get('app.list_of_list')); assertType('array|bool|float|int|string|null', $this->getParameter('app.list_of_list')); + assertType('array|bool|float|int|string|null', $container->getParameter('app.array_of_list')); + assertType('array|bool|float|int|string|null', $parameterBag->get('app.array_of_list')); + assertType('array|bool|float|int|string|null', $this->getParameter('app.array_of_list')); assertType('array|bool|float|int|string|null', $container->getParameter('app.map')); assertType('array|bool|float|int|string|null', $parameterBag->get('app.map')); assertType('array|bool|float|int|string|null', $this->getParameter('app.map')); From 30f12aeab960c7f324eee3b39645655cf8a84146 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Sat, 28 May 2022 17:18:51 +0200 Subject: [PATCH 080/208] Try to support union of strings as parameter name --- src/Symfony/DefaultParameterMap.php | 10 ++-- src/Symfony/FakeParameterMap.php | 4 +- src/Symfony/ParameterMap.php | 5 +- .../ParameterDynamicReturnTypeExtension.php | 46 +++++++++++++------ tests/Type/Symfony/data/ExampleController.php | 21 +++++++++ 5 files changed, 67 insertions(+), 19 deletions(-) diff --git a/src/Symfony/DefaultParameterMap.php b/src/Symfony/DefaultParameterMap.php index fe29b34f..26317468 100644 --- a/src/Symfony/DefaultParameterMap.php +++ b/src/Symfony/DefaultParameterMap.php @@ -4,8 +4,9 @@ use PhpParser\Node\Expr; use PHPStan\Analyser\Scope; +use PHPStan\Type\Type; use PHPStan\Type\TypeUtils; -use function count; +use function array_map; final class DefaultParameterMap implements ParameterMap { @@ -34,10 +35,13 @@ public function getParameter(string $key): ?ParameterDefinition return $this->parameters[$key] ?? null; } - public static function getParameterKeyFromNode(Expr $node, Scope $scope): ?string + public static function getParameterKeysFromNode(Expr $node, Scope $scope): array { $strings = TypeUtils::getConstantStrings($scope->getType($node)); - return count($strings) === 1 ? $strings[0]->getValue() : null; + + return array_map(static function (Type $type) { + return $type->getValue(); + }, $strings); } } diff --git a/src/Symfony/FakeParameterMap.php b/src/Symfony/FakeParameterMap.php index 0bf450e7..53acdc3c 100644 --- a/src/Symfony/FakeParameterMap.php +++ b/src/Symfony/FakeParameterMap.php @@ -21,9 +21,9 @@ public function getParameter(string $key): ?ParameterDefinition return null; } - public static function getParameterKeyFromNode(Expr $node, Scope $scope): ?string + public static function getParameterKeysFromNode(Expr $node, Scope $scope): array { - return null; + return []; } } diff --git a/src/Symfony/ParameterMap.php b/src/Symfony/ParameterMap.php index c768e2f2..ff0f5224 100644 --- a/src/Symfony/ParameterMap.php +++ b/src/Symfony/ParameterMap.php @@ -15,6 +15,9 @@ public function getParameters(): array; public function getParameter(string $key): ?ParameterDefinition; - public static function getParameterKeyFromNode(Expr $node, Scope $scope): ?string; + /** + * @return array + */ + public static function getParameterKeysFromNode(Expr $node, Scope $scope): array; } diff --git a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php index 6a197a8f..b18a386f 100644 --- a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php @@ -113,7 +113,7 @@ private function getGetTypeFromMethodCall( { // We don't use the method's return type because this won't work properly with lowest and // highest versions of Symfony ("mixed" for lowest, "array|bool|float|integer|string|null" for highest). - $returnType = new UnionType([ + $defaultReturnType = new UnionType([ new ArrayType(new MixedType(), new MixedType()), new BooleanType(), new FloatType(), @@ -122,18 +122,25 @@ private function getGetTypeFromMethodCall( new NullType(), ]); if (!isset($methodCall->getArgs()[0])) { - return $returnType; + return $defaultReturnType; } - $parameterKey = $this->parameterMap::getParameterKeyFromNode($methodCall->getArgs()[0]->value, $scope); - if ($parameterKey !== null) { + $parameterKeys = $this->parameterMap::getParameterKeysFromNode($methodCall->getArgs()[0]->value, $scope); + if ($parameterKeys === []) { + return $defaultReturnType; + } + + $returnTypes = []; + foreach ($parameterKeys as $parameterKey) { $parameter = $this->parameterMap->getParameter($parameterKey); - if ($parameter !== null) { - return $this->generalizeTypeFromValue($scope, $parameter->getValue()); + if ($parameter === null) { + return $defaultReturnType; } + + $returnTypes[] = $this->generalizeTypeFromValue($scope, $parameter->getValue()); } - return $returnType; + return TypeCombinator::union(...$returnTypes); } /** @@ -211,18 +218,31 @@ private function getHasTypeFromMethodCall( Scope $scope ): Type { - $returnType = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType(); + $defaultReturnType = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType(); if (!isset($methodCall->getArgs()[0]) || !$this->constantHassers) { - return $returnType; + return $defaultReturnType; } - $parameterKey = $this->parameterMap::getParameterKeyFromNode($methodCall->getArgs()[0]->value, $scope); - if ($parameterKey !== null) { + $parameterKeys = $this->parameterMap::getParameterKeysFromNode($methodCall->getArgs()[0]->value, $scope); + if ($parameterKeys === []) { + return $defaultReturnType; + } + + $has = null; + foreach ($parameterKeys as $parameterKey) { $parameter = $this->parameterMap->getParameter($parameterKey); - return new ConstantBooleanType($parameter !== null); + + if ($has === null) { + $has = $parameter !== null; + } elseif ( + ($has === true && $parameter === null) + || ($has === false && $parameter !== null) + ) { + return $defaultReturnType; + } } - return $returnType; + return new ConstantBooleanType($has); } } diff --git a/tests/Type/Symfony/data/ExampleController.php b/tests/Type/Symfony/data/ExampleController.php index 89d2b5f8..528b06a7 100644 --- a/tests/Type/Symfony/data/ExampleController.php +++ b/tests/Type/Symfony/data/ExampleController.php @@ -116,6 +116,27 @@ public function parameters(ContainerInterface $container, ParameterBagInterface assertType('true', $parameterBag->has('app.binary')); assertType('true', $container->hasParameter('app.constant')); assertType('true', $parameterBag->has('app.constant')); + + $key = rand(0, 1) ? 'app.string' : 'app.int'; + assertType("int|string", $container->getParameter($key)); + assertType("int|string", $parameterBag->get($key)); + assertType("int|string", $this->getParameter($key)); + assertType('true', $container->hasParameter($key)); + assertType('true', $parameterBag->has($key)); + + $key = rand(0, 1) ? 'app.string' : 'app.foo'; + assertType("array|bool|float|int|string|null", $container->getParameter($key)); + assertType("array|bool|float|int|string|null", $parameterBag->get($key)); + assertType("array|bool|float|int|string|null", $this->getParameter($key)); + assertType('bool', $container->hasParameter($key)); + assertType('bool', $parameterBag->has($key)); + + $key = rand(0, 1) ? 'app.bar' : 'app.foo'; + assertType("array|bool|float|int|string|null", $container->getParameter($key)); + assertType("array|bool|float|int|string|null", $parameterBag->get($key)); + assertType("array|bool|float|int|string|null", $this->getParameter($key)); + assertType('false', $container->hasParameter($key)); + assertType('false', $parameterBag->has($key)); } } From 26b650a7eb5b2d4ac63e4164c470d6949c240787 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Mon, 30 May 2022 00:04:50 +0000 Subject: [PATCH 081/208] Update dependency slevomat/coding-standard to v7.2.1 --- build-cs/composer.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/build-cs/composer.lock b/build-cs/composer.lock index 6e7fb818..4bcc8de4 100644 --- a/build-cs/composer.lock +++ b/build-cs/composer.lock @@ -195,16 +195,16 @@ }, { "name": "slevomat/coding-standard", - "version": "7.2.0", + "version": "7.2.1", "source": { "type": "git", "url": "https://github.com/slevomat/coding-standard.git", - "reference": "b4f96a8beea515d2d89141b7b9ad72f526d84071" + "reference": "aff06ae7a84e4534bf6f821dc982a93a5d477c90" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/b4f96a8beea515d2d89141b7b9ad72f526d84071", - "reference": "b4f96a8beea515d2d89141b7b9ad72f526d84071", + "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/aff06ae7a84e4534bf6f821dc982a93a5d477c90", + "reference": "aff06ae7a84e4534bf6f821dc982a93a5d477c90", "shasum": "" }, "require": { @@ -216,7 +216,7 @@ "require-dev": { "phing/phing": "2.17.3", "php-parallel-lint/php-parallel-lint": "1.3.2", - "phpstan/phpstan": "1.4.10|1.6.7", + "phpstan/phpstan": "1.4.10|1.7.1", "phpstan/phpstan-deprecation-rules": "1.0.0", "phpstan/phpstan-phpunit": "1.0.0|1.1.1", "phpstan/phpstan-strict-rules": "1.2.3", @@ -240,7 +240,7 @@ "description": "Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.", "support": { "issues": "https://github.com/slevomat/coding-standard/issues", - "source": "https://github.com/slevomat/coding-standard/tree/7.2.0" + "source": "https://github.com/slevomat/coding-standard/tree/7.2.1" }, "funding": [ { @@ -252,7 +252,7 @@ "type": "tidelift" } ], - "time": "2022-05-06T10:58:42+00:00" + "time": "2022-05-25T10:58:12+00:00" }, { "name": "squizlabs/php_codesniffer", From 7c24540375a53538190190496f077f76b14c63fb Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Fri, 3 Jun 2022 15:22:15 +0200 Subject: [PATCH 082/208] Fix stub --- stubs/Symfony/Component/HttpFoundation/Request.stub | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stubs/Symfony/Component/HttpFoundation/Request.stub b/stubs/Symfony/Component/HttpFoundation/Request.stub index f32365db..e554ec95 100644 --- a/stubs/Symfony/Component/HttpFoundation/Request.stub +++ b/stubs/Symfony/Component/HttpFoundation/Request.stub @@ -29,6 +29,6 @@ class Request /** * @return \Symfony\Component\HttpFoundation\Session\Session */ - public function getSession(): SessionInterface; + public function getSession(); } From 9451f103bfbd7bac5d236405eb97106a37930197 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Thu, 9 Jun 2022 14:00:34 +0200 Subject: [PATCH 083/208] ParameterDynamicReturnTypeExtension: Only generalizeType for keys --- .../ParameterDynamicReturnTypeExtension.php | 19 ++++++++----------- tests/Type/Symfony/container.xml | 10 ++++++++++ .../data/ExampleAbstractController.php | 15 +++++++++------ tests/Type/Symfony/data/ExampleController.php | 15 +++++++++------ 4 files changed, 36 insertions(+), 23 deletions(-) diff --git a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php index b18a386f..b160ddc8 100644 --- a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php @@ -14,7 +14,6 @@ use PHPStan\Type\BooleanType; use PHPStan\Type\Constant\ConstantArrayType; use PHPStan\Type\Constant\ConstantBooleanType; -use PHPStan\Type\Constant\ConstantIntegerType; use PHPStan\Type\Constant\ConstantStringType; use PHPStan\Type\ConstantType; use PHPStan\Type\DynamicMethodReturnTypeExtension; @@ -161,7 +160,7 @@ private function generalizeTypeFromValue(Scope $scope, $value): Type $keyTypes = []; $valueTypes = []; foreach ($value as $key => $element) { - /** @var ConstantIntegerType|ConstantStringType $keyType */ + /** @var ConstantStringType $keyType */ $keyType = $scope->getTypeFromValue($key); $keyTypes[] = $keyType; $valueTypes[] = $this->generalizeTypeFromValue($scope, $element); @@ -170,15 +169,13 @@ private function generalizeTypeFromValue(Scope $scope, $value): Type return new ConstantArrayType($keyTypes, $valueTypes); } - return $this->generalizeType( - new ArrayType( - TypeCombinator::union(...array_map(function ($item) use ($scope): Type { - return $this->generalizeTypeFromValue($scope, $item); - }, array_keys($value))), - TypeCombinator::union(...array_map(function ($item) use ($scope): Type { - return $this->generalizeTypeFromValue($scope, $item); - }, array_values($value))) - ) + return new ArrayType( + TypeCombinator::union(...array_map(function ($item) use ($scope): Type { + return $this->generalizeTypeFromValue($scope, $item); + }, array_keys($value))), + TypeCombinator::union(...array_map(function ($item) use ($scope): Type { + return $this->generalizeTypeFromValue($scope, $item); + }, array_values($value))) ); } diff --git a/tests/Type/Symfony/container.xml b/tests/Type/Symfony/container.xml index 5a14f9b5..e1ef6d89 100644 --- a/tests/Type/Symfony/container.xml +++ b/tests/Type/Symfony/container.xml @@ -37,6 +37,16 @@ another value + + + the name + the value + + + 12 + 32 + + the name diff --git a/tests/Type/Symfony/data/ExampleAbstractController.php b/tests/Type/Symfony/data/ExampleAbstractController.php index 8f599d7b..d9857a53 100644 --- a/tests/Type/Symfony/data/ExampleAbstractController.php +++ b/tests/Type/Symfony/data/ExampleAbstractController.php @@ -65,12 +65,15 @@ public function parameters(ContainerInterface $container, ParameterBagInterface assertType("array", $container->getParameter('app.list_of_int_as_processor')); assertType("array", $parameterBag->get('app.list_of_int_as_processor')); assertType("array", $this->getParameter('app.list_of_int_as_processor')); - assertType("array>", $container->getParameter('app.list_of_list')); - assertType("array>", $parameterBag->get('app.list_of_list')); - assertType("array>", $this->getParameter('app.list_of_list')); - assertType("array>", $container->getParameter('app.array_of_list')); - assertType("array>", $parameterBag->get('app.array_of_list')); - assertType("array>", $this->getParameter('app.array_of_list')); + assertType("array", $container->getParameter('app.list_of_list')); + assertType("array", $parameterBag->get('app.list_of_list')); + assertType("array", $this->getParameter('app.list_of_list')); + assertType("array", $container->getParameter('app.list_of_different_list')); + assertType("array", $parameterBag->get('app.list_of_different_list')); + assertType("array", $this->getParameter('app.list_of_different_list')); + assertType("array", $container->getParameter('app.array_of_list')); + assertType("array", $parameterBag->get('app.array_of_list')); + assertType("array", $this->getParameter('app.array_of_list')); assertType("array{url: string, endpoint: string, version: string, payment: array{default: array{username: string, password: string, signature: string}}, api: array{mode: string, default: array{username: string, password: string, signature: string}}}", $container->getParameter('app.list_of_things')); assertType("array{url: string, endpoint: string, version: string, payment: array{default: array{username: string, password: string, signature: string}}, api: array{mode: string, default: array{username: string, password: string, signature: string}}}", $parameterBag->get('app.list_of_things')); assertType("array{url: string, endpoint: string, version: string, payment: array{default: array{username: string, password: string, signature: string}}, api: array{mode: string, default: array{username: string, password: string, signature: string}}}", $this->getParameter('app.list_of_things')); diff --git a/tests/Type/Symfony/data/ExampleController.php b/tests/Type/Symfony/data/ExampleController.php index 528b06a7..c7563537 100644 --- a/tests/Type/Symfony/data/ExampleController.php +++ b/tests/Type/Symfony/data/ExampleController.php @@ -65,12 +65,15 @@ public function parameters(ContainerInterface $container, ParameterBagInterface assertType("array", $container->getParameter('app.list')); assertType("array", $parameterBag->get('app.list')); assertType("array", $this->getParameter('app.list')); - assertType("array>", $container->getParameter('app.list_of_list')); - assertType("array>", $parameterBag->get('app.list_of_list')); - assertType("array>", $this->getParameter('app.list_of_list')); - assertType("array>", $container->getParameter('app.array_of_list')); - assertType("array>", $parameterBag->get('app.array_of_list')); - assertType("array>", $this->getParameter('app.array_of_list')); + assertType("array", $container->getParameter('app.list_of_list')); + assertType("array", $parameterBag->get('app.list_of_list')); + assertType("array", $this->getParameter('app.list_of_list')); + assertType("array", $container->getParameter('app.list_of_different_list')); + assertType("array", $parameterBag->get('app.list_of_different_list')); + assertType("array", $this->getParameter('app.list_of_different_list')); + assertType("array", $container->getParameter('app.array_of_list')); + assertType("array", $parameterBag->get('app.array_of_list')); + assertType("array", $this->getParameter('app.array_of_list')); assertType("array{url: string, endpoint: string, version: string, payment: array{default: array{username: string, password: string, signature: string}}, api: array{mode: string, default: array{username: string, password: string, signature: string}}}", $container->getParameter('app.list_of_things')); assertType("array{url: string, endpoint: string, version: string, payment: array{default: array{username: string, password: string, signature: string}}, api: array{mode: string, default: array{username: string, password: string, signature: string}}}", $parameterBag->get('app.list_of_things')); assertType("array{url: string, endpoint: string, version: string, payment: array{default: array{username: string, password: string, signature: string}}, api: array{mode: string, default: array{username: string, password: string, signature: string}}}", $this->getParameter('app.list_of_things')); From 85be852a17fd5a6b67d4fc6daed21e794f935b2d Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Fri, 10 Jun 2022 09:46:26 +0200 Subject: [PATCH 084/208] Type array in request methods --- .../Component/HttpFoundation/Request.stub | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/stubs/Symfony/Component/HttpFoundation/Request.stub b/stubs/Symfony/Component/HttpFoundation/Request.stub index e554ec95..861769df 100644 --- a/stubs/Symfony/Component/HttpFoundation/Request.stub +++ b/stubs/Symfony/Component/HttpFoundation/Request.stub @@ -31,4 +31,47 @@ class Request */ public function getSession(); + /** + * @return string[] + */ + public static function getTrustedProxies(): array; + + /** + * @return string[] + */ + public static function getTrustedHosts(): array; + + /** + * @param string $format + * + * @return string[] + */ + public static function getMimeTypes($format): array; + + /** + * @param string|null $format + * @param string|string[] $mimeTypes + */ + public function setFormat($format, $mimeTypes): void; + + /** + * @return string[] + */ + public function getLanguages(): array; + + /** + * @return string[] + */ + public function getCharsets(): array; + + /** + * @return string[] + */ + public function getEncodings(): array; + + /** + * @return string[] + */ + public function getAcceptableContentTypes(): array; + } From 7d81c0875c400c3da0ede7eac75ea4ca88e81f57 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Tue, 28 Jun 2022 23:07:02 +0200 Subject: [PATCH 085/208] Require PHPStan 1.8.0 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index a563528a..9029bd42 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ "require": { "php": "^7.2 || ^8.0", "ext-simplexml": "*", - "phpstan/phpstan": "^1.6" + "phpstan/phpstan": "^1.8" }, "conflict": { "symfony/framework-bundle": "<3.0" From f5ad07e74a7e9ece0362232c724a6b1feb42bcfc Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Tue, 28 Jun 2022 23:07:29 +0200 Subject: [PATCH 086/208] PHPStan baseline --- Makefile | 4 ++++ phpstan-baseline.neon | 11 +++++++++++ phpstan.neon | 1 + 3 files changed, 16 insertions(+) create mode 100644 phpstan-baseline.neon diff --git a/Makefile b/Makefile index fe917d3b..b34d0fec 100644 --- a/Makefile +++ b/Makefile @@ -21,3 +21,7 @@ cs-fix: .PHONY: phpstan phpstan: php vendor/bin/phpstan analyse -l 8 -c phpstan.neon src tests + +.PHONY: phpstan-generate-baseline +phpstan-generate-baseline: + php vendor/bin/phpstan analyse -l 8 -c phpstan.neon src tests -b phpstan-baseline.neon diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon new file mode 100644 index 00000000..a86a4a2d --- /dev/null +++ b/phpstan-baseline.neon @@ -0,0 +1,11 @@ +parameters: + ignoreErrors: + - + message: "#^Accessing PHPStan\\\\Rules\\\\Methods\\\\CallMethodsRule\\:\\:class is not covered by backward compatibility promise\\. The class might change in a minor PHPStan version\\.$#" + count: 1 + path: tests/Rules/NonexistentInputBagClassTest.php + + - + message: "#^Accessing PHPStan\\\\Rules\\\\Comparison\\\\ImpossibleCheckTypeMethodCallRule\\:\\:class is not covered by backward compatibility promise\\. The class might change in a minor PHPStan version\\.$#" + count: 1 + path: tests/Type/Symfony/ImpossibleCheckTypeMethodCallRuleTest.php diff --git a/phpstan.neon b/phpstan.neon index 39998f59..f13073e1 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -5,6 +5,7 @@ includes: - vendor/phpstan/phpstan-phpunit/rules.neon - vendor/phpstan/phpstan-strict-rules/rules.neon - phar://phpstan.phar/conf/bleedingEdge.neon + - phpstan-baseline.neon parameters: excludePaths: From a5359244a932729613044fa306bbdeba16694253 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Tue, 28 Jun 2022 23:12:12 +0200 Subject: [PATCH 087/208] Update .gitattributes --- .gitattributes | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitattributes b/.gitattributes index 17405d6e..184bf6ae 100644 --- a/.gitattributes +++ b/.gitattributes @@ -9,4 +9,5 @@ tmp export-ignore Makefile export-ignore phpcs.xml export-ignore phpstan.neon export-ignore +phpstan-baseline.neon export-ignore phpunit.xml export-ignore From 401247f8d2aa3bddfa18cf94bec12693464fe2a6 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Wed, 13 Jul 2022 12:54:15 +0200 Subject: [PATCH 088/208] Create tag workflow --- .github/workflows/create-tag.yml | 53 ++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 .github/workflows/create-tag.yml diff --git a/.github/workflows/create-tag.yml b/.github/workflows/create-tag.yml new file mode 100644 index 00000000..8452d986 --- /dev/null +++ b/.github/workflows/create-tag.yml @@ -0,0 +1,53 @@ +# https://help.github.com/en/categories/automating-your-workflow-with-github-actions + +name: "Create tag" + +on: + # https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_dispatch + workflow_dispatch: + inputs: + version: + description: 'Next version' + required: true + default: 'patch' + type: choice + options: + - patch + - minor + +jobs: + create-tag: + name: "Create tag" + runs-on: "ubuntu-latest" + steps: + - name: "Checkout" + uses: actions/checkout@v3 + with: + fetch-depth: 0 + token: ${{ secrets.PHPSTAN_BOT_TOKEN }} + + - name: 'Get Previous tag' + id: previoustag + uses: "WyriHaximus/github-action-get-previous-tag@v1" + env: + GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + + - name: 'Get next versions' + id: semvers + uses: "WyriHaximus/github-action-next-semvers@v1" + with: + version: ${{ steps.previoustag.outputs.tag }} + + - name: "Create new minor tag" + uses: rickstaa/action-create-tag@v1 + if: inputs.version == 'minor' + with: + tag: ${{ steps.semvers.outputs.minor }} + message: ${{ steps.semvers.outputs.minor }} + + - name: "Create new patch tag" + uses: rickstaa/action-create-tag@v1 + if: inputs.version == 'patch' + with: + tag: ${{ steps.semvers.outputs.patch }} + message: ${{ steps.semvers.outputs.patch }} From 5f4d20e20bec093ae636384a39a76df91f4d2452 Mon Sep 17 00:00:00 2001 From: Mathieu Rochette Date: Wed, 13 Jul 2022 14:32:58 +0200 Subject: [PATCH 089/208] Add example to load single command application --- README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/README.md b/README.md index 7fe1a8bb..78f02320 100644 --- a/README.md +++ b/README.md @@ -132,6 +132,22 @@ $kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']); return new Application($kernel); ``` +[Single Command Application](https://symfony.com/doc/current/components/console/single_command_tool.html): + +```php +// tests/console-application.php + +use App\Application; // where Application extends Symfony\Component\Console\SingleCommandApplication +use Symfony\Component\Console; + +require __DIR__ . '/../vendor/autoload.php'; + +$application = new Console\Application(); +$application->add(new Application()); + +return $application; +``` + You may then encounter an error with PhpParser: > Compile Error: Cannot Declare interface PhpParser\NodeVisitor, because the name is already in use From b3df0da98adc391d23204d843b6320d480084ee1 Mon Sep 17 00:00:00 2001 From: fluffycondor <62219548+fluffycondor@users.noreply.github.com> Date: Mon, 18 Jul 2022 15:46:26 +0000 Subject: [PATCH 090/208] Add stub for write and writeln console methods --- extension.neon | 2 ++ stubs/Php/Stringable.stub | 5 ++++ .../Console/Output/OutputInterface.stub | 30 +++++++++++++++++++ 3 files changed, 37 insertions(+) create mode 100644 stubs/Php/Stringable.stub create mode 100644 stubs/Symfony/Component/Console/Output/OutputInterface.stub diff --git a/extension.neon b/extension.neon index 1aba591e..40e4e8be 100644 --- a/extension.neon +++ b/extension.neon @@ -12,12 +12,14 @@ parameters: console_application_loader: null consoleApplicationLoader: null stubFiles: + - stubs/Php/Stringable.stub - stubs/Psr/Cache/CacheItemInterface.stub - stubs/Symfony/Bundle/FrameworkBundle/KernelBrowser.stub - stubs/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.stub - stubs/Symfony/Bundle/FrameworkBundle/Test/TestContainer.stub - stubs/Symfony/Component/Console/Command.stub - stubs/Symfony/Component/Console/Helper/HelperInterface.stub + - stubs/Symfony/Component/Console/Output/OutputInterface.stub - stubs/Symfony/Component/Form/ChoiceList/Loader/ChoiceLoaderInterface.stub - stubs/Symfony/Component/DependencyInjection/ContainerBuilder.stub - stubs/Symfony/Component/DependencyInjection/Extension/ExtensionInterface.stub diff --git a/stubs/Php/Stringable.stub b/stubs/Php/Stringable.stub new file mode 100644 index 00000000..81e1c5d5 --- /dev/null +++ b/stubs/Php/Stringable.stub @@ -0,0 +1,5 @@ + $messages + * @param int-mask-of $options + */ + public function write($messages, bool $newline = false, int $options = 0): void; + + /** + * @param string|Stringable|iterable $messages + * @param int-mask-of $options + */ + public function writeln($messages, int $options = 0): void; +} From 240c15b678b00a12868baa0e5aa97cc84f53f4fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Mirtes?= Date: Tue, 19 Jul 2022 11:17:59 +0200 Subject: [PATCH 091/208] Require PHPStan 1.8.2 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 9029bd42..21a245dc 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ "require": { "php": "^7.2 || ^8.0", "ext-simplexml": "*", - "phpstan/phpstan": "^1.8" + "phpstan/phpstan": "^1.8.2" }, "conflict": { "symfony/framework-bundle": "<3.0" From c90de3df26ea64590214798db4e4f2d7fd67e32f Mon Sep 17 00:00:00 2001 From: Alexander Schranz Date: Tue, 19 Jul 2022 11:23:36 +0200 Subject: [PATCH 092/208] Add stub for Options interface --- extension.neon | 4 ++ .../Component/OptionsResolver/Options.stub | 41 +++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 stubs/Symfony/Component/OptionsResolver/Options.stub diff --git a/extension.neon b/extension.neon index 40e4e8be..7ea78616 100644 --- a/extension.neon +++ b/extension.neon @@ -11,6 +11,9 @@ parameters: constantHassers: true console_application_loader: null consoleApplicationLoader: null + featureToggles: + skipCheckGenericClasses: + - Symfony\Component\OptionsResolver\Options stubFiles: - stubs/Php/Stringable.stub - stubs/Psr/Cache/CacheItemInterface.stub @@ -38,6 +41,7 @@ parameters: - stubs/Symfony/Component/HttpFoundation/Session.stub - stubs/Symfony/Component/Messenger/StampInterface.stub - stubs/Symfony/Component/Messenger/Envelope.stub + - stubs/Symfony/Component/OptionsResolver/Options.stub - stubs/Symfony/Component/Process/Process.stub - stubs/Symfony/Component/PropertyAccess/PropertyPathInterface.stub - stubs/Symfony/Component/Security/Acl/Model/AclInterface.stub diff --git a/stubs/Symfony/Component/OptionsResolver/Options.stub b/stubs/Symfony/Component/OptionsResolver/Options.stub new file mode 100644 index 00000000..31ca0207 --- /dev/null +++ b/stubs/Symfony/Component/OptionsResolver/Options.stub @@ -0,0 +1,41 @@ +, value-of> + */ +interface Options extends \ArrayAccess, \Countable +{ + /** + * @param key-of $offset + * + * @return bool + */ + public function offsetExists($offset); + + /** + * @template TOffset of key-of + * @param TOffset $offset + * @return TArray[TOffset] + */ + public function offsetGet($offset); + + /** + * @template TOffset of key-of + * @param TOffset|null $offset + * @param TArray[TOffset] $value + * + * @return void + */ + public function offsetSet($offset, $value); + + /** + * @template TOffset of key-of + * @param TOffset $offset + * + * @return void + */ + public function offsetUnset($offset); +} From c612f674fb04909e1c01657c57757b913ea70432 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Mirtes?= Date: Tue, 19 Jul 2022 13:18:16 +0200 Subject: [PATCH 093/208] Update build.yml --- .github/workflows/build.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 38309f6a..4070c178 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -21,6 +21,7 @@ jobs: - "7.4" - "8.0" - "8.1" + - "8.2" steps: - name: "Checkout" @@ -85,6 +86,7 @@ jobs: - "7.4" - "8.0" - "8.1" + - "8.2" dependencies: - "lowest" - "highest" @@ -127,6 +129,7 @@ jobs: - "7.4" - "8.0" - "8.1" + - "8.2" dependencies: - "lowest" - "highest" From 28fe677ec3ad43eb4c0946c502535365e3040ecf Mon Sep 17 00:00:00 2001 From: Semyon <7ionmail@gmail.com> Date: Wed, 20 Jul 2022 14:51:09 +0300 Subject: [PATCH 094/208] Remove Stringable from the signature, it's actually not supported --- extension.neon | 1 - stubs/Php/Stringable.stub | 5 ----- stubs/Symfony/Component/Console/Output/OutputInterface.stub | 4 ++-- 3 files changed, 2 insertions(+), 8 deletions(-) delete mode 100644 stubs/Php/Stringable.stub diff --git a/extension.neon b/extension.neon index 7ea78616..b4c3ee38 100644 --- a/extension.neon +++ b/extension.neon @@ -15,7 +15,6 @@ parameters: skipCheckGenericClasses: - Symfony\Component\OptionsResolver\Options stubFiles: - - stubs/Php/Stringable.stub - stubs/Psr/Cache/CacheItemInterface.stub - stubs/Symfony/Bundle/FrameworkBundle/KernelBrowser.stub - stubs/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.stub diff --git a/stubs/Php/Stringable.stub b/stubs/Php/Stringable.stub deleted file mode 100644 index 81e1c5d5..00000000 --- a/stubs/Php/Stringable.stub +++ /dev/null @@ -1,5 +0,0 @@ - $messages + * @param string|iterable $messages * @param int-mask-of $options */ public function write($messages, bool $newline = false, int $options = 0): void; /** - * @param string|Stringable|iterable $messages + * @param string|iterable $messages * @param int-mask-of $options */ public function writeln($messages, int $options = 0): void; From 140f41035420a74eb7387406661a1d2d4289e905 Mon Sep 17 00:00:00 2001 From: Semyon <7ionmail@gmail.com> Date: Wed, 20 Jul 2022 15:07:44 +0300 Subject: [PATCH 095/208] Remove redundant use --- stubs/Symfony/Component/Console/Output/OutputInterface.stub | 2 -- 1 file changed, 2 deletions(-) diff --git a/stubs/Symfony/Component/Console/Output/OutputInterface.stub b/stubs/Symfony/Component/Console/Output/OutputInterface.stub index 805c72ca..6e4acd3b 100644 --- a/stubs/Symfony/Component/Console/Output/OutputInterface.stub +++ b/stubs/Symfony/Component/Console/Output/OutputInterface.stub @@ -2,8 +2,6 @@ namespace Symfony\Component\Console\Output; -use Stringable; - interface OutputInterface { public const VERBOSITY_QUIET = 16; From f4cb3b8915d3656e780f305f01c86b70ff933272 Mon Sep 17 00:00:00 2001 From: Yassine Guedidi Date: Fri, 15 Jul 2022 13:16:39 +0200 Subject: [PATCH 096/208] Add stub for VoterInterface --- extension.neon | 2 ++ .../Authentication/Token/TokenInterface.stub | 7 +++++++ .../Core/Authorization/Voter/VoterInterface.stub | 16 ++++++++++++++++ 3 files changed, 25 insertions(+) create mode 100644 stubs/Symfony/Component/Security/Core/Authentication/Token/TokenInterface.stub create mode 100644 stubs/Symfony/Component/Security/Core/Authorization/Voter/VoterInterface.stub diff --git a/extension.neon b/extension.neon index b4c3ee38..ff6fb339 100644 --- a/extension.neon +++ b/extension.neon @@ -45,6 +45,8 @@ parameters: - 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/VoterInterface.stub - stubs/Symfony/Component/Serializer/Encoder/ContextAwareDecoderInterface.stub - stubs/Symfony/Component/Serializer/Encoder/DecoderInterface.stub - stubs/Symfony/Component/Serializer/Encoder/EncoderInterface.stub diff --git a/stubs/Symfony/Component/Security/Core/Authentication/Token/TokenInterface.stub b/stubs/Symfony/Component/Security/Core/Authentication/Token/TokenInterface.stub new file mode 100644 index 00000000..204a9c40 --- /dev/null +++ b/stubs/Symfony/Component/Security/Core/Authentication/Token/TokenInterface.stub @@ -0,0 +1,7 @@ + Date: Thu, 25 Aug 2022 16:58:42 +0200 Subject: [PATCH 097/208] Infer correct type for `$input->getOptions()` call --- extension.neon | 7 ++ src/Type/Symfony/GetOptionTypeHelper.php | 35 +++++++++ ...aceGetOptionDynamicReturnTypeExtension.php | 25 ++----- ...ceGetOptionsDynamicReturnTypeExtension.php | 71 +++++++++++++++++++ .../Symfony/data/ExampleOptionCommand.php | 2 + .../Symfony/data/ExampleOptionLazyCommand.php | 2 + 6 files changed, 123 insertions(+), 19 deletions(-) create mode 100644 src/Type/Symfony/GetOptionTypeHelper.php create mode 100644 src/Type/Symfony/InputInterfaceGetOptionsDynamicReturnTypeExtension.php diff --git a/extension.neon b/extension.neon index ff6fb339..f4c0dbf4 100644 --- a/extension.neon +++ b/extension.neon @@ -175,10 +175,17 @@ services: tags: [phpstan.broker.dynamicMethodReturnTypeExtension] # InputInterface::getOption() return type + - + factory: PHPStan\Type\Symfony\GetOptionTypeHelper - factory: PHPStan\Type\Symfony\InputInterfaceGetOptionDynamicReturnTypeExtension tags: [phpstan.broker.dynamicMethodReturnTypeExtension] + # InputInterface::getOptions() return type + - + factory: PHPStan\Type\Symfony\InputInterfaceGetOptionsDynamicReturnTypeExtension + tags: [phpstan.broker.dynamicMethodReturnTypeExtension] + # InputInterface::hasOption() type specification - factory: PHPStan\Type\Symfony\OptionTypeSpecifyingExtension diff --git a/src/Type/Symfony/GetOptionTypeHelper.php b/src/Type/Symfony/GetOptionTypeHelper.php new file mode 100644 index 00000000..a67606e1 --- /dev/null +++ b/src/Type/Symfony/GetOptionTypeHelper.php @@ -0,0 +1,35 @@ +acceptValue()) { + return new BooleanType(); + } + + $optType = TypeCombinator::union(new StringType(), new NullType()); + if ($option->isValueRequired() && ($option->isArray() || $option->getDefault() !== null)) { + $optType = TypeCombinator::removeNull($optType); + } + if ($option->isArray()) { + $optType = new ArrayType(new IntegerType(), $optType); + } + + return TypeCombinator::union($optType, $scope->getTypeFromValue($option->getDefault())); + } + +} diff --git a/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php index 7b543a16..ad2bc6c6 100644 --- a/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php @@ -8,12 +8,7 @@ use PHPStan\Reflection\MethodReflection; use PHPStan\Reflection\ParametersAcceptorSelector; use PHPStan\Symfony\ConsoleApplicationResolver; -use PHPStan\Type\ArrayType; -use PHPStan\Type\BooleanType; use PHPStan\Type\DynamicMethodReturnTypeExtension; -use PHPStan\Type\IntegerType; -use PHPStan\Type\NullType; -use PHPStan\Type\StringType; use PHPStan\Type\Type; use PHPStan\Type\TypeCombinator; use PHPStan\Type\TypeUtils; @@ -25,9 +20,13 @@ final class InputInterfaceGetOptionDynamicReturnTypeExtension implements Dynamic /** @var ConsoleApplicationResolver */ private $consoleApplicationResolver; - public function __construct(ConsoleApplicationResolver $consoleApplicationResolver) + /** @var GetOptionTypeHelper */ + private $getOptionTypeHelper; + + public function __construct(ConsoleApplicationResolver $consoleApplicationResolver, GetOptionTypeHelper $getOptionTypeHelper) { $this->consoleApplicationResolver = $consoleApplicationResolver; + $this->getOptionTypeHelper = $getOptionTypeHelper; } public function getClass(): string @@ -64,19 +63,7 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method try { $command->mergeApplicationDefinition(); $option = $command->getDefinition()->getOption($optName); - if (!$option->acceptValue()) { - $optType = new BooleanType(); - } else { - $optType = TypeCombinator::union(new StringType(), new NullType()); - if ($option->isValueRequired() && ($option->isArray() || $option->getDefault() !== null)) { - $optType = TypeCombinator::removeNull($optType); - } - if ($option->isArray()) { - $optType = new ArrayType(new IntegerType(), $optType); - } - $optType = TypeCombinator::union($optType, $scope->getTypeFromValue($option->getDefault())); - } - $optTypes[] = $optType; + $optTypes[] = $this->getOptionTypeHelper->getOptionType($scope, $option); } catch (InvalidArgumentException $e) { // noop } diff --git a/src/Type/Symfony/InputInterfaceGetOptionsDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceGetOptionsDynamicReturnTypeExtension.php new file mode 100644 index 00000000..80c6d4e2 --- /dev/null +++ b/src/Type/Symfony/InputInterfaceGetOptionsDynamicReturnTypeExtension.php @@ -0,0 +1,71 @@ +consoleApplicationResolver = $consoleApplicationResolver; + $this->getOptionTypeHelper = $getOptionTypeHelper; + } + + public function getClass(): string + { + return 'Symfony\Component\Console\Input\InputInterface'; + } + + public function isMethodSupported(MethodReflection $methodReflection): bool + { + return $methodReflection->getName() === 'getOptions'; + } + + public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type + { + $defaultReturnType = ParametersAcceptorSelector::selectFromArgs($scope, $methodCall->getArgs(), $methodReflection->getVariants())->getReturnType(); + $classReflection = $scope->getClassReflection(); + if ($classReflection === null) { + return $defaultReturnType; + } + + $optTypes = []; + foreach ($this->consoleApplicationResolver->findCommands($classReflection) as $command) { + try { + $command->mergeApplicationDefinition(); + $options = $command->getDefinition()->getOptions(); + $builder = ConstantArrayTypeBuilder::createEmpty(); + foreach ($options as $name => $option) { + $optionType = $this->getOptionTypeHelper->getOptionType($scope, $option); + $builder->setOffsetValueType(new ConstantStringType($name), $optionType); + } + + $optTypes[] = $builder->getArray(); + } catch (InvalidArgumentException $e) { + // noop + } + } + + return count($optTypes) > 0 ? TypeCombinator::union(...$optTypes) : $defaultReturnType; + } + +} diff --git a/tests/Type/Symfony/data/ExampleOptionCommand.php b/tests/Type/Symfony/data/ExampleOptionCommand.php index b6a07574..00c35894 100644 --- a/tests/Type/Symfony/data/ExampleOptionCommand.php +++ b/tests/Type/Symfony/data/ExampleOptionCommand.php @@ -40,6 +40,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int assertType('1|string', $input->getOption('cc')); assertType('array', $input->getOption('dd')); assertType('array', $input->getOption('ee')); + + assertType('array{a: bool, b: string|null, c: string|null, d: array, e: array, bb: 1|string|null, cc: 1|string, dd: array, ee: array, help: bool, quiet: bool, verbose: bool, version: bool, ansi: bool, no-interaction: bool}', $input->getOptions()); } } diff --git a/tests/Type/Symfony/data/ExampleOptionLazyCommand.php b/tests/Type/Symfony/data/ExampleOptionLazyCommand.php index 7933cd41..1aaebf20 100644 --- a/tests/Type/Symfony/data/ExampleOptionLazyCommand.php +++ b/tests/Type/Symfony/data/ExampleOptionLazyCommand.php @@ -42,6 +42,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int assertType('1|string', $input->getOption('cc')); assertType('array', $input->getOption('dd')); assertType('array', $input->getOption('ee')); + + assertType('array{a: bool, b: string|null, c: string|null, d: array, e: array, bb: 1|string|null, cc: 1|string, dd: array, ee: array, help: bool, quiet: bool, verbose: bool, version: bool, ansi: bool, no-interaction: bool}', $input->getOptions()); } } From 298f7aee5f36f7d72e575970013b46965456b7f4 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Thu, 25 Aug 2022 17:24:08 +0200 Subject: [PATCH 098/208] Do not longer test unsupported versions of Symfony --- composer.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/composer.json b/composer.json index 21a245dc..8ab6d172 100644 --- a/composer.json +++ b/composer.json @@ -27,15 +27,15 @@ "phpstan/phpstan-strict-rules": "^1.0", "phpunit/phpunit": "^9.5", "psr/container": "1.0 || 1.1.1", - "symfony/config": "^4.2 || ^5.0", - "symfony/console": "^4.0 || ^5.0", - "symfony/dependency-injection": "^4.0 || ^5.0", - "symfony/form": "^4.0 || ^5.0", - "symfony/framework-bundle": "^4.4 || ^5.0", - "symfony/http-foundation": "^5.1", - "symfony/messenger": "^4.2 || ^5.0", + "symfony/config": "^5.4", + "symfony/console": "^5.4", + "symfony/dependency-injection": "^5.4", + "symfony/form": "^5.4", + "symfony/framework-bundle": "^5.4", + "symfony/http-foundation": "^5.4", + "symfony/messenger": "^5.4", "symfony/polyfill-php80": "^1.24", - "symfony/serializer": "^4.0 || ^5.0" + "symfony/serializer": "^5.4" }, "config": { "platform": { From 120d3a8f3b16f79a570e4ef68a96fe9f85186fd1 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Thu, 25 Aug 2022 17:24:53 +0200 Subject: [PATCH 099/208] Test Symfony ^6.1 --- composer.json | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/composer.json b/composer.json index 8ab6d172..65dfa3df 100644 --- a/composer.json +++ b/composer.json @@ -27,20 +27,17 @@ "phpstan/phpstan-strict-rules": "^1.0", "phpunit/phpunit": "^9.5", "psr/container": "1.0 || 1.1.1", - "symfony/config": "^5.4", - "symfony/console": "^5.4", - "symfony/dependency-injection": "^5.4", - "symfony/form": "^5.4", - "symfony/framework-bundle": "^5.4", - "symfony/http-foundation": "^5.4", + "symfony/config": "^5.4 || ^6.1", + "symfony/console": "^5.4 || ^6.1", + "symfony/dependency-injection": "^5.4 || ^6.1", + "symfony/form": "^5.4 || ^6.1", + "symfony/framework-bundle": "^5.4 || ^6.1", + "symfony/http-foundation": "^5.4 || ^6.1", "symfony/messenger": "^5.4", "symfony/polyfill-php80": "^1.24", "symfony/serializer": "^5.4" }, "config": { - "platform": { - "php": "7.4.6" - }, "sort-packages": true }, "extra": { From 82d54e8d9070a131770732afd0492b9acff73227 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Thu, 25 Aug 2022 17:30:20 +0200 Subject: [PATCH 100/208] Fix build by downgrading PHPUnit --- .github/workflows/build.yml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4070c178..1045f11c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -33,16 +33,16 @@ jobs: coverage: "none" php-version: "${{ matrix.php-version }}" + - name: "Downgrade PHPUnit" + if: matrix.php-version == '7.2' + run: "composer require --dev phpunit/phpunit:^8.5.29 --no-update --update-with-dependencies" + - name: "Validate Composer" run: "composer validate" - name: "Install dependencies" run: "composer install --no-interaction --no-progress" - - name: "Downgrade PHPUnit" - if: matrix.php-version == '7.2' || matrix.php-version == '7.3' - run: "composer require --dev phpunit/phpunit:^7.5.20 --update-with-dependencies" - - name: "Lint" run: "make lint" @@ -101,6 +101,10 @@ jobs: coverage: "none" php-version: "${{ matrix.php-version }}" + - name: "Downgrade PHPUnit" + if: matrix.php-version == '7.2' + run: "composer require --dev phpunit/phpunit:^8.5.29 --no-update --update-with-dependencies" + - name: "Install lowest dependencies" if: ${{ matrix.dependencies == 'lowest' }} run: "composer update --prefer-lowest --no-interaction --no-progress" @@ -109,10 +113,6 @@ jobs: if: ${{ matrix.dependencies == 'highest' }} run: "composer update --no-interaction --no-progress" - - name: "Downgrade PHPUnit" - if: matrix.php-version == '7.2' || matrix.php-version == '7.3' - run: "composer require --dev phpunit/phpunit:^7.5.20 --update-with-dependencies" - - name: "Tests" run: "make tests" @@ -146,6 +146,10 @@ jobs: extensions: mbstring tools: composer:v2 + - name: "Downgrade PHPUnit" + if: matrix.php-version == '7.2' + run: "composer require --dev phpunit/phpunit:^8.5.29 --no-update --update-with-dependencies" + - name: "Install lowest dependencies" if: ${{ matrix.dependencies == 'lowest' }} run: "composer update --prefer-lowest --no-interaction --no-progress" @@ -154,9 +158,5 @@ jobs: if: ${{ matrix.dependencies == 'highest' }} run: "composer update --no-interaction --no-progress" - - name: "Downgrade PHPUnit" - if: matrix.php-version == '7.2' || matrix.php-version == '7.3' - run: "composer require --dev phpunit/phpunit:^7.5.20 --update-with-dependencies" - - name: "PHPStan" run: "make phpstan" From a14d467083e8a4ca6860921e68f646831a88260c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20H=C3=A4u=C3=9Fler?= Date: Thu, 25 Aug 2022 19:30:02 +0200 Subject: [PATCH 101/208] Add support for negatable input options Since symfony/console 5.3, input options can be marked as negatable. The appropriate return type when calling `$input->getOption()` can be either a boolean or null. With this change, support for return types of such negatable input options is added. --- src/Type/Symfony/GetOptionTypeHelper.php | 5 +++++ tests/Type/Symfony/data/ExampleOptionCommand.php | 4 +++- tests/Type/Symfony/data/ExampleOptionLazyCommand.php | 4 +++- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/Type/Symfony/GetOptionTypeHelper.php b/src/Type/Symfony/GetOptionTypeHelper.php index a67606e1..9406698e 100644 --- a/src/Type/Symfony/GetOptionTypeHelper.php +++ b/src/Type/Symfony/GetOptionTypeHelper.php @@ -10,6 +10,7 @@ use PHPStan\Type\StringType; use PHPStan\Type\Type; use PHPStan\Type\TypeCombinator; +use PHPStan\Type\UnionType; use Symfony\Component\Console\Input\InputOption; class GetOptionTypeHelper @@ -18,6 +19,10 @@ class GetOptionTypeHelper public function getOptionType(Scope $scope, InputOption $option): Type { if (!$option->acceptValue()) { + if ($option->isNegatable()) { + return new UnionType([new BooleanType(), new NullType()]); + } + return new BooleanType(); } diff --git a/tests/Type/Symfony/data/ExampleOptionCommand.php b/tests/Type/Symfony/data/ExampleOptionCommand.php index 00c35894..b880173d 100644 --- a/tests/Type/Symfony/data/ExampleOptionCommand.php +++ b/tests/Type/Symfony/data/ExampleOptionCommand.php @@ -21,6 +21,7 @@ protected function configure(): void $this->addOption('c', null, InputOption::VALUE_REQUIRED); $this->addOption('d', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL); $this->addOption('e', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED); + $this->addOption('f', null, InputOption::VALUE_NEGATABLE); $this->addOption('bb', null, InputOption::VALUE_OPTIONAL, '', 1); $this->addOption('cc', null, InputOption::VALUE_REQUIRED, '', 1); @@ -35,13 +36,14 @@ protected function execute(InputInterface $input, OutputInterface $output): int assertType('string|null', $input->getOption('c')); assertType('array', $input->getOption('d')); assertType('array', $input->getOption('e')); + assertType('bool|null', $input->getOption('f')); assertType('1|string|null', $input->getOption('bb')); assertType('1|string', $input->getOption('cc')); assertType('array', $input->getOption('dd')); assertType('array', $input->getOption('ee')); - assertType('array{a: bool, b: string|null, c: string|null, d: array, e: array, bb: 1|string|null, cc: 1|string, dd: array, ee: array, help: bool, quiet: bool, verbose: bool, version: bool, ansi: bool, no-interaction: bool}', $input->getOptions()); + assertType('array{a: bool, b: string|null, c: string|null, d: array, e: array, f: bool|null, bb: 1|string|null, cc: 1|string, dd: array, ee: array, help: bool, quiet: bool, verbose: bool, version: bool, ansi: bool|null, no-interaction: bool}', $input->getOptions()); } } diff --git a/tests/Type/Symfony/data/ExampleOptionLazyCommand.php b/tests/Type/Symfony/data/ExampleOptionLazyCommand.php index 1aaebf20..433e1cfe 100644 --- a/tests/Type/Symfony/data/ExampleOptionLazyCommand.php +++ b/tests/Type/Symfony/data/ExampleOptionLazyCommand.php @@ -23,6 +23,7 @@ protected function configure(): void $this->addOption('c', null, InputOption::VALUE_REQUIRED); $this->addOption('d', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL); $this->addOption('e', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED); + $this->addOption('f', null, InputOption::VALUE_NEGATABLE); $this->addOption('bb', null, InputOption::VALUE_OPTIONAL, '', 1); $this->addOption('cc', null, InputOption::VALUE_REQUIRED, '', 1); @@ -37,13 +38,14 @@ protected function execute(InputInterface $input, OutputInterface $output): int assertType('string|null', $input->getOption('c')); assertType('array', $input->getOption('d')); assertType('array', $input->getOption('e')); + assertType('bool|null', $input->getOption('f')); assertType('1|string|null', $input->getOption('bb')); assertType('1|string', $input->getOption('cc')); assertType('array', $input->getOption('dd')); assertType('array', $input->getOption('ee')); - assertType('array{a: bool, b: string|null, c: string|null, d: array, e: array, bb: 1|string|null, cc: 1|string, dd: array, ee: array, help: bool, quiet: bool, verbose: bool, version: bool, ansi: bool, no-interaction: bool}', $input->getOptions()); + assertType('array{a: bool, b: string|null, c: string|null, d: array, e: array, f: bool|null, bb: 1|string|null, cc: 1|string, dd: array, ee: array, help: bool, quiet: bool, verbose: bool, version: bool, ansi: bool|null, no-interaction: bool}', $input->getOptions()); } } From e7767d9cf191e6865a2205f77ecac3716872e055 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20H=C3=A4u=C3=9Fler?= Date: Fri, 26 Aug 2022 16:27:59 +0200 Subject: [PATCH 102/208] Skip check for negatable input options on symfony/console < 5.3 Related: #300 --- phpstan-baseline.neon | 5 +++++ src/Type/Symfony/GetOptionTypeHelper.php | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index a86a4a2d..4abdaa0a 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,5 +1,10 @@ parameters: ignoreErrors: + - + message: "#^Call to function method_exists\\(\\) with Symfony\\\\Component\\\\Console\\\\Input\\\\InputOption and 'isNegatable' will always evaluate to true\\.$#" + count: 1 + path: src/Type/Symfony/GetOptionTypeHelper.php + - message: "#^Accessing PHPStan\\\\Rules\\\\Methods\\\\CallMethodsRule\\:\\:class is not covered by backward compatibility promise\\. The class might change in a minor PHPStan version\\.$#" count: 1 diff --git a/src/Type/Symfony/GetOptionTypeHelper.php b/src/Type/Symfony/GetOptionTypeHelper.php index 9406698e..6ddf87d8 100644 --- a/src/Type/Symfony/GetOptionTypeHelper.php +++ b/src/Type/Symfony/GetOptionTypeHelper.php @@ -12,6 +12,7 @@ use PHPStan\Type\TypeCombinator; use PHPStan\Type\UnionType; use Symfony\Component\Console\Input\InputOption; +use function method_exists; class GetOptionTypeHelper { @@ -19,7 +20,7 @@ class GetOptionTypeHelper public function getOptionType(Scope $scope, InputOption $option): Type { if (!$option->acceptValue()) { - if ($option->isNegatable()) { + if (method_exists($option, 'isNegatable') && $option->isNegatable()) { return new UnionType([new BooleanType(), new NullType()]); } From 016e441a19a2af79ca0c60920ba0d61747b4e855 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Thu, 18 Aug 2022 12:23:40 +0200 Subject: [PATCH 103/208] Add @throws annotation --- extension.neon | 5 ++++- stubs/Symfony/Component/Form/DataTransformerInterface.stub | 4 ++++ .../Component/Form/Exception/ExceptionInterface.stub | 7 +++++++ .../Symfony/Component/Form/Exception/RuntimeException.stub | 7 +++++++ .../Form/Exception/TransformationFailedException.stub | 7 +++++++ 5 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 stubs/Symfony/Component/Form/Exception/ExceptionInterface.stub create mode 100644 stubs/Symfony/Component/Form/Exception/RuntimeException.stub create mode 100644 stubs/Symfony/Component/Form/Exception/TransformationFailedException.stub diff --git a/extension.neon b/extension.neon index f4c0dbf4..30f2139d 100644 --- a/extension.neon +++ b/extension.neon @@ -22,12 +22,15 @@ parameters: - stubs/Symfony/Component/Console/Command.stub - stubs/Symfony/Component/Console/Helper/HelperInterface.stub - stubs/Symfony/Component/Console/Output/OutputInterface.stub - - stubs/Symfony/Component/Form/ChoiceList/Loader/ChoiceLoaderInterface.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/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/FormInterface.stub diff --git a/stubs/Symfony/Component/Form/DataTransformerInterface.stub b/stubs/Symfony/Component/Form/DataTransformerInterface.stub index edd4411e..393fa803 100644 --- a/stubs/Symfony/Component/Form/DataTransformerInterface.stub +++ b/stubs/Symfony/Component/Form/DataTransformerInterface.stub @@ -14,6 +14,8 @@ interface DataTransformerInterface * @phpstan-param T|null $value The value in the original representation * * @phpstan-return R|null The value in the transformed representation + * + * @throws TransformationFailedException */ public function transform($value); @@ -21,6 +23,8 @@ interface DataTransformerInterface * @phpstan-param R|null $value The value in the transformed representation * * @phpstan-return T|null The value in the original representation + * + * @throws TransformationFailedException */ public function reverseTransform($value); } diff --git a/stubs/Symfony/Component/Form/Exception/ExceptionInterface.stub b/stubs/Symfony/Component/Form/Exception/ExceptionInterface.stub new file mode 100644 index 00000000..306015dc --- /dev/null +++ b/stubs/Symfony/Component/Form/Exception/ExceptionInterface.stub @@ -0,0 +1,7 @@ + Date: Wed, 5 Oct 2022 13:17:24 +0200 Subject: [PATCH 104/208] ConsoleApplicationResolver made lazy --- src/Symfony/ConsoleApplicationResolver.php | 32 ++++++++++++++-------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/src/Symfony/ConsoleApplicationResolver.php b/src/Symfony/ConsoleApplicationResolver.php index 6d4ec810..941e8fef 100644 --- a/src/Symfony/ConsoleApplicationResolver.php +++ b/src/Symfony/ConsoleApplicationResolver.php @@ -16,31 +16,38 @@ final class ConsoleApplicationResolver { + /** @var string|null */ + private $consoleApplicationLoader; + /** @var Application|null */ private $consoleApplication; public function __construct(Configuration $configuration) { - $consoleApplicationLoader = $configuration->getConsoleApplicationLoader(); - if ($consoleApplicationLoader === null) { - return; - } - $this->consoleApplication = $this->loadConsoleApplication($consoleApplicationLoader); + $this->consoleApplicationLoader = $configuration->getConsoleApplicationLoader(); } /** * @return Application|null * @phpcsSuppress SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingNativeTypeHint */ - private function loadConsoleApplication(string $consoleApplicationLoader) + private function getConsoleApplication() { - if (!file_exists($consoleApplicationLoader) - || !is_readable($consoleApplicationLoader) + if ($this->consoleApplicationLoader === null) { + return null; + } + + if ($this->consoleApplication !== null) { + return $this->consoleApplication; + } + + if (!file_exists($this->consoleApplicationLoader) + || !is_readable($this->consoleApplicationLoader) ) { - throw new ShouldNotHappenException(sprintf('Cannot load console application. Check the parameters.symfony.consoleApplicationLoader setting in PHPStan\'s config. The offending value is "%s".', $consoleApplicationLoader)); + throw new ShouldNotHappenException(sprintf('Cannot load console application. Check the parameters.symfony.consoleApplicationLoader setting in PHPStan\'s config. The offending value is "%s".', $this->consoleApplicationLoader)); } - return require $consoleApplicationLoader; + return $this->consoleApplication = require $this->consoleApplicationLoader; } /** @@ -48,7 +55,8 @@ private function loadConsoleApplication(string $consoleApplicationLoader) */ public function findCommands(ClassReflection $classReflection): array { - if ($this->consoleApplication === null) { + $consoleApplication = $this->getConsoleApplication(); + if ($consoleApplication === null) { return []; } @@ -58,7 +66,7 @@ public function findCommands(ClassReflection $classReflection): array } $commands = []; - foreach ($this->consoleApplication->all() as $name => $command) { + foreach ($consoleApplication->all() as $name => $command) { $commandClass = new ObjectType(get_class($command)); $isLazyCommand = (new ObjectType('Symfony\Component\Console\Command\LazyCommand'))->isSuperTypeOf($commandClass)->yes(); From f7dd737329504115adaa987697a759a66dd2ee8a Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Wed, 5 Oct 2022 13:18:42 +0200 Subject: [PATCH 105/208] Native type in ConsoleApplicationResolver --- src/Symfony/ConsoleApplicationResolver.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Symfony/ConsoleApplicationResolver.php b/src/Symfony/ConsoleApplicationResolver.php index 941e8fef..51bd7960 100644 --- a/src/Symfony/ConsoleApplicationResolver.php +++ b/src/Symfony/ConsoleApplicationResolver.php @@ -27,11 +27,7 @@ public function __construct(Configuration $configuration) $this->consoleApplicationLoader = $configuration->getConsoleApplicationLoader(); } - /** - * @return Application|null - * @phpcsSuppress SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingNativeTypeHint - */ - private function getConsoleApplication() + private function getConsoleApplication(): ?Application { if ($this->consoleApplicationLoader === null) { return null; From e5e807b5ea8f656cd61fe9f29e79e23494d7a144 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 31 Oct 2022 00:38:35 +0000 Subject: [PATCH 106/208] Update metcalfc/changelog-generator action to v4 --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5fed0458..bac4a006 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,7 +18,7 @@ jobs: - name: Generate changelog id: changelog - uses: metcalfc/changelog-generator@v3.0.0 + uses: metcalfc/changelog-generator@v4.0.1 with: myToken: ${{ secrets.PHPSTAN_BOT_TOKEN }} From 7210072b7fd83bf62eb5f889d4da70b80c07f04c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A1chym=20Tou=C5=A1ek?= Date: Thu, 3 Nov 2022 12:32:52 +0100 Subject: [PATCH 107/208] Improve Envelope::all() return type --- stubs/Symfony/Component/Messenger/Envelope.stub | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/stubs/Symfony/Component/Messenger/Envelope.stub b/stubs/Symfony/Component/Messenger/Envelope.stub index c40a5ee6..af69bb66 100644 --- a/stubs/Symfony/Component/Messenger/Envelope.stub +++ b/stubs/Symfony/Component/Messenger/Envelope.stub @@ -14,4 +14,11 @@ final class Envelope public function last(string $stampFqcn): ?StampInterface { } + + /** + * @return ($stampFqcn is null ? array> : list) + */ + public function all(?string $stampFqcn = null): array + { + } } From d6ea16206b1b645ded5b43736d8ef5ae1168eb55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A1chym=20Tou=C5=A1ek?= Date: Fri, 4 Nov 2022 09:35:44 +0100 Subject: [PATCH 108/208] Fix Envelope::all() return type --- composer.json | 2 +- src/Type/Symfony/EnvelopeReturnTypeExtension.php | 13 +++++++++---- stubs/Symfony/Component/Messenger/Envelope.stub | 7 ------- tests/Type/Symfony/ExtensionTest.php | 1 + tests/Type/Symfony/data/envelope_all.php | 6 +++--- 5 files changed, 14 insertions(+), 15 deletions(-) diff --git a/composer.json b/composer.json index 65dfa3df..adc7050a 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ "require": { "php": "^7.2 || ^8.0", "ext-simplexml": "*", - "phpstan/phpstan": "^1.8.2" + "phpstan/phpstan": "^1.9.1" }, "conflict": { "symfony/framework-bundle": "<3.0" diff --git a/src/Type/Symfony/EnvelopeReturnTypeExtension.php b/src/Type/Symfony/EnvelopeReturnTypeExtension.php index 5ac61c2f..ec2531aa 100644 --- a/src/Type/Symfony/EnvelopeReturnTypeExtension.php +++ b/src/Type/Symfony/EnvelopeReturnTypeExtension.php @@ -5,10 +5,12 @@ use PhpParser\Node\Expr\MethodCall; use PHPStan\Analyser\Scope; use PHPStan\Reflection\MethodReflection; +use PHPStan\Type\Accessory\AccessoryArrayListType; use PHPStan\Type\ArrayType; use PHPStan\Type\Constant\ConstantStringType; use PHPStan\Type\DynamicMethodReturnTypeExtension; -use PHPStan\Type\MixedType; +use PHPStan\Type\Generic\GenericClassStringType; +use PHPStan\Type\IntegerType; use PHPStan\Type\ObjectType; use PHPStan\Type\Type; use function count; @@ -33,15 +35,18 @@ public function getTypeFromMethodCall( ): Type { if (count($methodCall->getArgs()) === 0) { - return new ArrayType(new MixedType(), new ArrayType(new MixedType(), new ObjectType('Symfony\Component\Messenger\Stamp\StampInterface'))); + return new ArrayType( + new GenericClassStringType(new ObjectType('Symfony\Component\Messenger\Stamp\StampInterface')), + AccessoryArrayListType::intersectWith(new ArrayType(new IntegerType(), new ObjectType('Symfony\Component\Messenger\Stamp\StampInterface'))) + ); } $argType = $scope->getType($methodCall->getArgs()[0]->value); if (!$argType instanceof ConstantStringType) { - return new ArrayType(new MixedType(), new ObjectType('Symfony\Component\Messenger\Stamp\StampInterface')); + return AccessoryArrayListType::intersectWith(new ArrayType(new IntegerType(), new ObjectType('Symfony\Component\Messenger\Stamp\StampInterface'))); } - return new ArrayType(new MixedType(), new ObjectType($argType->getValue())); + return AccessoryArrayListType::intersectWith(new ArrayType(new IntegerType(), new ObjectType($argType->getValue()))); } } diff --git a/stubs/Symfony/Component/Messenger/Envelope.stub b/stubs/Symfony/Component/Messenger/Envelope.stub index af69bb66..c40a5ee6 100644 --- a/stubs/Symfony/Component/Messenger/Envelope.stub +++ b/stubs/Symfony/Component/Messenger/Envelope.stub @@ -14,11 +14,4 @@ final class Envelope public function last(string $stampFqcn): ?StampInterface { } - - /** - * @return ($stampFqcn is null ? array> : list) - */ - public function all(?string $stampFqcn = null): array - { - } } diff --git a/tests/Type/Symfony/ExtensionTest.php b/tests/Type/Symfony/ExtensionTest.php index a0bb8115..8dff057e 100644 --- a/tests/Type/Symfony/ExtensionTest.php +++ b/tests/Type/Symfony/ExtensionTest.php @@ -76,6 +76,7 @@ public static function getAdditionalConfigFiles(): array return [ __DIR__ . '/../../../extension.neon', __DIR__ . '/extension-test.neon', + 'phar://' . __DIR__ . '/../../../vendor/phpstan/phpstan/phpstan.phar/conf/bleedingEdge.neon', ]; } diff --git a/tests/Type/Symfony/data/envelope_all.php b/tests/Type/Symfony/data/envelope_all.php index 95521530..aac9d583 100644 --- a/tests/Type/Symfony/data/envelope_all.php +++ b/tests/Type/Symfony/data/envelope_all.php @@ -4,6 +4,6 @@ $envelope = new \Symfony\Component\Messenger\Envelope(new stdClass()); -assertType('array', $envelope->all(\Symfony\Component\Messenger\Stamp\ReceivedStamp::class)); -assertType('array', $envelope->all(random_bytes(1))); -assertType('array>', $envelope->all()); +assertType('list', $envelope->all(\Symfony\Component\Messenger\Stamp\ReceivedStamp::class)); +assertType('list', $envelope->all(random_bytes(1))); +assertType('array, list>', $envelope->all()); From e08844440952110852cb223171f1907c15a344db Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Mon, 5 Dec 2022 09:14:55 +0100 Subject: [PATCH 109/208] Fix build --- .../Symfony/ServiceDynamicReturnTypeExtension.php | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php b/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php index a2168a91..b0101cf4 100644 --- a/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php @@ -18,6 +18,7 @@ use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; use function class_exists; use function in_array; +use function is_string; final class ServiceDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension { @@ -144,7 +145,17 @@ private function getHasTypeFromMethodCall( private function determineServiceClass(ParameterBag $parameterBag, ServiceDefinition $service): ?string { - return $parameterBag->resolveValue($service->getClass()); + $class = $service->getClass(); + if ($class === null) { + return null; + } + + $value = $parameterBag->resolveValue($class); + if (!is_string($value)) { + return null; + } + + return $value; } } From c55237a027f71f0cdb09c77fd27783dd806d228f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 5 Dec 2022 02:23:50 +0000 Subject: [PATCH 110/208] Update dessant/lock-threads action to v4 --- .github/workflows/lock-closed-issues.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lock-closed-issues.yml b/.github/workflows/lock-closed-issues.yml index a05d4173..4c7990df 100644 --- a/.github/workflows/lock-closed-issues.yml +++ b/.github/workflows/lock-closed-issues.yml @@ -8,7 +8,7 @@ jobs: lock: runs-on: ubuntu-latest steps: - - uses: dessant/lock-threads@v3 + - uses: dessant/lock-threads@v4 with: github-token: ${{ github.token }} issue-inactive-days: '31' From 738920755edd12c693ae83ab73b8f6bfa220a091 Mon Sep 17 00:00:00 2001 From: Fran Moreno Date: Thu, 15 Dec 2022 15:28:17 +0100 Subject: [PATCH 111/208] Remove nullable type after calling `HeaderBag::has` --- extension.neon | 11 ++++++++++- ...nsion.php => BagTypeSpecifyingExtension.php} | 17 +++++++++++++---- tests/Type/Symfony/ExtensionTest.php | 2 +- .../data/{header_bag_get.php => header_bag.php} | 6 ++++++ 4 files changed, 30 insertions(+), 6 deletions(-) rename src/Type/Symfony/{InputBagTypeSpecifyingExtension.php => BagTypeSpecifyingExtension.php} (79%) rename tests/Type/Symfony/data/{header_bag_get.php => header_bag.php} (82%) diff --git a/extension.neon b/extension.neon index 30f2139d..ab503e1e 100644 --- a/extension.neon +++ b/extension.neon @@ -298,5 +298,14 @@ services: # InputBag::get() type specification - - factory: PHPStan\Type\Symfony\InputBagTypeSpecifyingExtension + factory: PHPStan\Type\Symfony\BagTypeSpecifyingExtension tags: [phpstan.typeSpecifier.methodTypeSpecifyingExtension] + arguments: + className: Symfony\Component\HttpFoundation\InputBag + + # HeaderBag::get() type specification + - + factory: PHPStan\Type\Symfony\BagTypeSpecifyingExtension + tags: [phpstan.typeSpecifier.methodTypeSpecifyingExtension] + arguments: + className: Symfony\Component\HttpFoundation\HeaderBag diff --git a/src/Type/Symfony/InputBagTypeSpecifyingExtension.php b/src/Type/Symfony/BagTypeSpecifyingExtension.php similarity index 79% rename from src/Type/Symfony/InputBagTypeSpecifyingExtension.php rename to src/Type/Symfony/BagTypeSpecifyingExtension.php index d3ad578c..96aa91f1 100644 --- a/src/Type/Symfony/InputBagTypeSpecifyingExtension.php +++ b/src/Type/Symfony/BagTypeSpecifyingExtension.php @@ -11,21 +11,30 @@ use PHPStan\Reflection\MethodReflection; use PHPStan\Type\MethodTypeSpecifyingExtension; use PHPStan\Type\NullType; -use Symfony\Component\HttpFoundation\InputBag; -final class InputBagTypeSpecifyingExtension implements MethodTypeSpecifyingExtension, TypeSpecifierAwareExtension +final class BagTypeSpecifyingExtension implements MethodTypeSpecifyingExtension, TypeSpecifierAwareExtension { - private const INPUT_BAG_CLASS = InputBag::class; private const HAS_METHOD_NAME = 'has'; private const GET_METHOD_NAME = 'get'; /** @var TypeSpecifier */ private $typeSpecifier; + /** @var class-string */ + private $className; + + /** + * @param class-string $className + */ + public function __construct(string $className) + { + $this->className = $className; + } + public function getClass(): string { - return self::INPUT_BAG_CLASS; + return $this->className; } public function isMethodSupported(MethodReflection $methodReflection, MethodCall $node, TypeSpecifierContext $context): bool diff --git a/tests/Type/Symfony/ExtensionTest.php b/tests/Type/Symfony/ExtensionTest.php index 8dff057e..afe60ca4 100644 --- a/tests/Type/Symfony/ExtensionTest.php +++ b/tests/Type/Symfony/ExtensionTest.php @@ -15,7 +15,7 @@ class ExtensionTest extends TypeInferenceTestCase public function dataFileAsserts(): iterable { yield from $this->gatherAssertTypes(__DIR__ . '/data/envelope_all.php'); - yield from $this->gatherAssertTypes(__DIR__ . '/data/header_bag_get.php'); + yield from $this->gatherAssertTypes(__DIR__ . '/data/header_bag.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/response_header_bag_get_cookies.php'); if (class_exists('Symfony\Component\HttpFoundation\InputBag')) { diff --git a/tests/Type/Symfony/data/header_bag_get.php b/tests/Type/Symfony/data/header_bag.php similarity index 82% rename from tests/Type/Symfony/data/header_bag_get.php rename to tests/Type/Symfony/data/header_bag.php index e3f4c561..ce513bc1 100644 --- a/tests/Type/Symfony/data/header_bag_get.php +++ b/tests/Type/Symfony/data/header_bag.php @@ -4,6 +4,12 @@ $bag = new \Symfony\Component\HttpFoundation\HeaderBag(['foo' => ['bar']]); +if ($bag->has('bar')) { + assertType('string', $bag->get('bar')); +} else { + assertType('null', $bag->get('bar')); +} + assertType('string|null', $bag->get('foo')); assertType('string|null', $bag->get('foo', null)); assertType('string', $bag->get('foo', 'baz')); From 3178f15a60b62df21ddd202f6a668851eeb138c7 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Fri, 16 Dec 2022 10:43:26 +0100 Subject: [PATCH 112/208] Revert "Remove nullable type after calling `HeaderBag::has`" This reverts commit 738920755edd12c693ae83ab73b8f6bfa220a091. --- extension.neon | 11 +---------- ....php => InputBagTypeSpecifyingExtension.php} | 17 ++++------------- tests/Type/Symfony/ExtensionTest.php | 2 +- .../data/{header_bag.php => header_bag_get.php} | 6 ------ 4 files changed, 6 insertions(+), 30 deletions(-) rename src/Type/Symfony/{BagTypeSpecifyingExtension.php => InputBagTypeSpecifyingExtension.php} (79%) rename tests/Type/Symfony/data/{header_bag.php => header_bag_get.php} (82%) diff --git a/extension.neon b/extension.neon index ab503e1e..30f2139d 100644 --- a/extension.neon +++ b/extension.neon @@ -298,14 +298,5 @@ services: # InputBag::get() type specification - - factory: PHPStan\Type\Symfony\BagTypeSpecifyingExtension + factory: PHPStan\Type\Symfony\InputBagTypeSpecifyingExtension tags: [phpstan.typeSpecifier.methodTypeSpecifyingExtension] - arguments: - className: Symfony\Component\HttpFoundation\InputBag - - # HeaderBag::get() type specification - - - factory: PHPStan\Type\Symfony\BagTypeSpecifyingExtension - tags: [phpstan.typeSpecifier.methodTypeSpecifyingExtension] - arguments: - className: Symfony\Component\HttpFoundation\HeaderBag diff --git a/src/Type/Symfony/BagTypeSpecifyingExtension.php b/src/Type/Symfony/InputBagTypeSpecifyingExtension.php similarity index 79% rename from src/Type/Symfony/BagTypeSpecifyingExtension.php rename to src/Type/Symfony/InputBagTypeSpecifyingExtension.php index 96aa91f1..d3ad578c 100644 --- a/src/Type/Symfony/BagTypeSpecifyingExtension.php +++ b/src/Type/Symfony/InputBagTypeSpecifyingExtension.php @@ -11,30 +11,21 @@ use PHPStan\Reflection\MethodReflection; use PHPStan\Type\MethodTypeSpecifyingExtension; use PHPStan\Type\NullType; +use Symfony\Component\HttpFoundation\InputBag; -final class BagTypeSpecifyingExtension implements MethodTypeSpecifyingExtension, TypeSpecifierAwareExtension +final class InputBagTypeSpecifyingExtension implements MethodTypeSpecifyingExtension, TypeSpecifierAwareExtension { + private const INPUT_BAG_CLASS = InputBag::class; private const HAS_METHOD_NAME = 'has'; private const GET_METHOD_NAME = 'get'; /** @var TypeSpecifier */ private $typeSpecifier; - /** @var class-string */ - private $className; - - /** - * @param class-string $className - */ - public function __construct(string $className) - { - $this->className = $className; - } - public function getClass(): string { - return $this->className; + return self::INPUT_BAG_CLASS; } public function isMethodSupported(MethodReflection $methodReflection, MethodCall $node, TypeSpecifierContext $context): bool diff --git a/tests/Type/Symfony/ExtensionTest.php b/tests/Type/Symfony/ExtensionTest.php index afe60ca4..8dff057e 100644 --- a/tests/Type/Symfony/ExtensionTest.php +++ b/tests/Type/Symfony/ExtensionTest.php @@ -15,7 +15,7 @@ class ExtensionTest extends TypeInferenceTestCase public function dataFileAsserts(): iterable { yield from $this->gatherAssertTypes(__DIR__ . '/data/envelope_all.php'); - yield from $this->gatherAssertTypes(__DIR__ . '/data/header_bag.php'); + yield from $this->gatherAssertTypes(__DIR__ . '/data/header_bag_get.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/response_header_bag_get_cookies.php'); if (class_exists('Symfony\Component\HttpFoundation\InputBag')) { diff --git a/tests/Type/Symfony/data/header_bag.php b/tests/Type/Symfony/data/header_bag_get.php similarity index 82% rename from tests/Type/Symfony/data/header_bag.php rename to tests/Type/Symfony/data/header_bag_get.php index ce513bc1..e3f4c561 100644 --- a/tests/Type/Symfony/data/header_bag.php +++ b/tests/Type/Symfony/data/header_bag_get.php @@ -4,12 +4,6 @@ $bag = new \Symfony\Component\HttpFoundation\HeaderBag(['foo' => ['bar']]); -if ($bag->has('bar')) { - assertType('string', $bag->get('bar')); -} else { - assertType('null', $bag->get('bar')); -} - assertType('string|null', $bag->get('foo')); assertType('string|null', $bag->get('foo', null)); assertType('string', $bag->get('foo', 'baz')); From 4a920ef43ae4253d1831c33ebeaa9c01de1db49b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Mirtes?= Date: Mon, 19 Dec 2022 13:24:33 +0100 Subject: [PATCH 113/208] Create release-toot.yml --- .github/workflows/release-toot.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .github/workflows/release-toot.yml diff --git a/.github/workflows/release-toot.yml b/.github/workflows/release-toot.yml new file mode 100644 index 00000000..2af0f176 --- /dev/null +++ b/.github/workflows/release-toot.yml @@ -0,0 +1,21 @@ +name: Toot release + +# More triggers +# https://docs.github.com/en/actions/learn-github-actions/events-that-trigger-workflows#release +on: + release: + types: [published] + +jobs: + toot: + runs-on: ubuntu-latest + steps: + - uses: cbrgm/mastodon-github-action@v1 + if: ${{ !github.event.repository.private }} + with: + # GitHub event payload + # https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#release + message: "New release: ${{ github.event.repository.name }} ${{ github.event.release.tag_name }} ${{ github.event.release.html_url }} #phpstan" + env: + MASTODON_URL: phpc.social + MASTODON_ACCESS_TOKEN: ${{ secrets.MASTODON_ACCESS_TOKEN }} From 72cf8cfa4991747e9d858e602b26090682b77ba0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Mirtes?= Date: Tue, 20 Dec 2022 22:08:30 +0100 Subject: [PATCH 114/208] Update release-toot.yml --- .github/workflows/release-toot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-toot.yml b/.github/workflows/release-toot.yml index 2af0f176..6a1c8156 100644 --- a/.github/workflows/release-toot.yml +++ b/.github/workflows/release-toot.yml @@ -17,5 +17,5 @@ jobs: # https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#release message: "New release: ${{ github.event.repository.name }} ${{ github.event.release.tag_name }} ${{ github.event.release.html_url }} #phpstan" env: - MASTODON_URL: phpc.social + MASTODON_URL: https://phpc.social MASTODON_ACCESS_TOKEN: ${{ secrets.MASTODON_ACCESS_TOKEN }} From dac2474904b092267f0a19dfba8c46b6f21eab6a Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Thu, 22 Dec 2022 21:05:46 +0100 Subject: [PATCH 115/208] Add generics for PasswordUpgraderInterface --- extension.neon | 3 +++ .../User/PasswordAuthenticatedUserInterface.stub | 7 +++++++ .../Core/User/PasswordUpgraderInterface.stub | 14 ++++++++++++++ 3 files changed, 24 insertions(+) create mode 100644 stubs/Symfony/Component/Security/Core/User/PasswordAuthenticatedUserInterface.stub create mode 100644 stubs/Symfony/Component/Security/Core/User/PasswordUpgraderInterface.stub diff --git a/extension.neon b/extension.neon index 30f2139d..582126af 100644 --- a/extension.neon +++ b/extension.neon @@ -14,6 +14,7 @@ parameters: featureToggles: skipCheckGenericClasses: - Symfony\Component\OptionsResolver\Options + - Symfony\Component\Security\Core\User\PasswordUpgraderInterface stubFiles: - stubs/Psr/Cache/CacheItemInterface.stub - stubs/Symfony/Bundle/FrameworkBundle/KernelBrowser.stub @@ -50,6 +51,8 @@ parameters: - stubs/Symfony/Component/Security/Acl/Model/EntryInterface.stub - stubs/Symfony/Component/Security/Core/Authentication/Token/TokenInterface.stub - stubs/Symfony/Component/Security/Core/Authorization/Voter/VoterInterface.stub + - stubs/Symfony/Component/Security/Core/User/PasswordAuthenticatedUserInterface.stub + - stubs/Symfony/Component/Security/Core/User/PasswordUpgraderInterface.stub - stubs/Symfony/Component/Serializer/Encoder/ContextAwareDecoderInterface.stub - stubs/Symfony/Component/Serializer/Encoder/DecoderInterface.stub - stubs/Symfony/Component/Serializer/Encoder/EncoderInterface.stub diff --git a/stubs/Symfony/Component/Security/Core/User/PasswordAuthenticatedUserInterface.stub b/stubs/Symfony/Component/Security/Core/User/PasswordAuthenticatedUserInterface.stub new file mode 100644 index 00000000..19cc6040 --- /dev/null +++ b/stubs/Symfony/Component/Security/Core/User/PasswordAuthenticatedUserInterface.stub @@ -0,0 +1,7 @@ + Date: Thu, 29 Dec 2022 17:48:39 +0100 Subject: [PATCH 116/208] Utilize `null`-default return to simplify extensions --- ...CommandGetHelperDynamicReturnTypeExtension.php | 12 +++++------- ...rfaceGetArgumentDynamicReturnTypeExtension.php | 13 +++++-------- ...terfaceGetOptionDynamicReturnTypeExtension.php | 13 +++++-------- ...erfaceGetOptionsDynamicReturnTypeExtension.php | 8 +++----- ...rfaceHasArgumentDynamicReturnTypeExtension.php | 15 ++++++--------- ...terfaceHasOptionDynamicReturnTypeExtension.php | 15 ++++++--------- 6 files changed, 30 insertions(+), 46 deletions(-) diff --git a/src/Type/Symfony/CommandGetHelperDynamicReturnTypeExtension.php b/src/Type/Symfony/CommandGetHelperDynamicReturnTypeExtension.php index db2348e3..3b38258c 100644 --- a/src/Type/Symfony/CommandGetHelperDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/CommandGetHelperDynamicReturnTypeExtension.php @@ -36,22 +36,20 @@ public function isMethodSupported(MethodReflection $methodReflection): bool return $methodReflection->getName() === 'getHelper'; } - public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type + public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): ?Type { - $defaultReturnType = new ObjectType('Symfony\Component\Console\Helper\HelperInterface'); - if (!isset($methodCall->getArgs()[0])) { - return $defaultReturnType; + return null; } $classReflection = $scope->getClassReflection(); if ($classReflection === null) { - return $defaultReturnType; + return null; } $argStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->getArgs()[0]->value)); if (count($argStrings) !== 1) { - return $defaultReturnType; + return null; } $argName = $argStrings[0]->getValue(); @@ -65,7 +63,7 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method } } - return count($returnTypes) > 0 ? TypeCombinator::union(...$returnTypes) : $defaultReturnType; + return count($returnTypes) > 0 ? TypeCombinator::union(...$returnTypes) : null; } } diff --git a/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php index a8c35200..0af7c88d 100644 --- a/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php @@ -6,7 +6,6 @@ use PhpParser\Node\Expr\MethodCall; use PHPStan\Analyser\Scope; use PHPStan\Reflection\MethodReflection; -use PHPStan\Reflection\ParametersAcceptorSelector; use PHPStan\Symfony\ConsoleApplicationResolver; use PHPStan\Type\ArrayType; use PHPStan\Type\DynamicMethodReturnTypeExtension; @@ -38,22 +37,20 @@ public function isMethodSupported(MethodReflection $methodReflection): bool return $methodReflection->getName() === 'getArgument'; } - public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type + public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): ?Type { - $defaultReturnType = ParametersAcceptorSelector::selectFromArgs($scope, $methodCall->getArgs(), $methodReflection->getVariants())->getReturnType(); - if (!isset($methodCall->getArgs()[0])) { - return $defaultReturnType; + return null; } $classReflection = $scope->getClassReflection(); if ($classReflection === null) { - return $defaultReturnType; + return null; } $argStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->getArgs()[0]->value)); if (count($argStrings) !== 1) { - return $defaultReturnType; + return null; } $argName = $argStrings[0]->getValue(); @@ -79,7 +76,7 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method } } - return count($argTypes) > 0 ? TypeCombinator::union(...$argTypes) : $defaultReturnType; + return count($argTypes) > 0 ? TypeCombinator::union(...$argTypes) : null; } } diff --git a/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php index ad2bc6c6..d765f65b 100644 --- a/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php @@ -6,7 +6,6 @@ use PhpParser\Node\Expr\MethodCall; use PHPStan\Analyser\Scope; use PHPStan\Reflection\MethodReflection; -use PHPStan\Reflection\ParametersAcceptorSelector; use PHPStan\Symfony\ConsoleApplicationResolver; use PHPStan\Type\DynamicMethodReturnTypeExtension; use PHPStan\Type\Type; @@ -39,22 +38,20 @@ public function isMethodSupported(MethodReflection $methodReflection): bool return $methodReflection->getName() === 'getOption'; } - public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type + public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): ?Type { - $defaultReturnType = ParametersAcceptorSelector::selectFromArgs($scope, $methodCall->getArgs(), $methodReflection->getVariants())->getReturnType(); - if (!isset($methodCall->getArgs()[0])) { - return $defaultReturnType; + return null; } $classReflection = $scope->getClassReflection(); if ($classReflection === null) { - return $defaultReturnType; + return null; } $optStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->getArgs()[0]->value)); if (count($optStrings) !== 1) { - return $defaultReturnType; + return null; } $optName = $optStrings[0]->getValue(); @@ -69,7 +66,7 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method } } - return count($optTypes) > 0 ? TypeCombinator::union(...$optTypes) : $defaultReturnType; + return count($optTypes) > 0 ? TypeCombinator::union(...$optTypes) : null; } } diff --git a/src/Type/Symfony/InputInterfaceGetOptionsDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceGetOptionsDynamicReturnTypeExtension.php index 80c6d4e2..515c44d0 100644 --- a/src/Type/Symfony/InputInterfaceGetOptionsDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/InputInterfaceGetOptionsDynamicReturnTypeExtension.php @@ -6,7 +6,6 @@ use PhpParser\Node\Expr\MethodCall; use PHPStan\Analyser\Scope; use PHPStan\Reflection\MethodReflection; -use PHPStan\Reflection\ParametersAcceptorSelector; use PHPStan\Symfony\ConsoleApplicationResolver; use PHPStan\Type\Constant\ConstantArrayTypeBuilder; use PHPStan\Type\Constant\ConstantStringType; @@ -40,12 +39,11 @@ public function isMethodSupported(MethodReflection $methodReflection): bool return $methodReflection->getName() === 'getOptions'; } - public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type + public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): ?Type { - $defaultReturnType = ParametersAcceptorSelector::selectFromArgs($scope, $methodCall->getArgs(), $methodReflection->getVariants())->getReturnType(); $classReflection = $scope->getClassReflection(); if ($classReflection === null) { - return $defaultReturnType; + return null; } $optTypes = []; @@ -65,7 +63,7 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method } } - return count($optTypes) > 0 ? TypeCombinator::union(...$optTypes) : $defaultReturnType; + return count($optTypes) > 0 ? TypeCombinator::union(...$optTypes) : null; } } diff --git a/src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php index dc933e14..4ce49c4a 100644 --- a/src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php @@ -7,7 +7,6 @@ use PHPStan\Analyser\Scope; use PHPStan\Reflection\MethodReflection; use PHPStan\Symfony\ConsoleApplicationResolver; -use PHPStan\Type\BooleanType; use PHPStan\Type\Constant\ConstantBooleanType; use PHPStan\Type\DynamicMethodReturnTypeExtension; use PHPStan\Type\Type; @@ -36,22 +35,20 @@ public function isMethodSupported(MethodReflection $methodReflection): bool return $methodReflection->getName() === 'hasArgument'; } - public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type + public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): ?Type { - $defaultReturnType = new BooleanType(); - if (!isset($methodCall->getArgs()[0])) { - return $defaultReturnType; + return null; } $classReflection = $scope->getClassReflection(); if ($classReflection === null) { - return $defaultReturnType; + return null; } $argStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->getArgs()[0]->value)); if (count($argStrings) !== 1) { - return $defaultReturnType; + return null; } $argName = $argStrings[0]->getValue(); @@ -67,11 +64,11 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method } if (count($returnTypes) === 0) { - return $defaultReturnType; + return null; } $returnTypes = array_unique($returnTypes); - return count($returnTypes) === 1 ? new ConstantBooleanType($returnTypes[0]) : $defaultReturnType; + return count($returnTypes) === 1 ? new ConstantBooleanType($returnTypes[0]) : null; } } diff --git a/src/Type/Symfony/InputInterfaceHasOptionDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceHasOptionDynamicReturnTypeExtension.php index 3f239fe6..3be621f8 100644 --- a/src/Type/Symfony/InputInterfaceHasOptionDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/InputInterfaceHasOptionDynamicReturnTypeExtension.php @@ -7,7 +7,6 @@ use PHPStan\Analyser\Scope; use PHPStan\Reflection\MethodReflection; use PHPStan\Symfony\ConsoleApplicationResolver; -use PHPStan\Type\BooleanType; use PHPStan\Type\Constant\ConstantBooleanType; use PHPStan\Type\DynamicMethodReturnTypeExtension; use PHPStan\Type\Type; @@ -36,22 +35,20 @@ public function isMethodSupported(MethodReflection $methodReflection): bool return $methodReflection->getName() === 'hasOption'; } - public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type + public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): ?Type { - $defaultReturnType = new BooleanType(); - if (!isset($methodCall->getArgs()[0])) { - return $defaultReturnType; + return null; } $classReflection = $scope->getClassReflection(); if ($classReflection === null) { - return $defaultReturnType; + return null; } $optStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->getArgs()[0]->value)); if (count($optStrings) !== 1) { - return $defaultReturnType; + return null; } $optName = $optStrings[0]->getValue(); @@ -67,11 +64,11 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method } if (count($returnTypes) === 0) { - return $defaultReturnType; + return null; } $returnTypes = array_unique($returnTypes); - return count($returnTypes) === 1 ? new ConstantBooleanType($returnTypes[0]) : $defaultReturnType; + return count($returnTypes) === 1 ? new ConstantBooleanType($returnTypes[0]) : null; } } From d89a521e7e31822409bf37f70691f7a12a6e7d76 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Sat, 14 Jan 2023 23:21:38 +0100 Subject: [PATCH 117/208] Revert wrong typehint for getSession --- stubs/Symfony/Component/HttpFoundation/Request.stub | 5 ----- tests/Type/Symfony/data/request_get_session.php | 6 +++--- tests/Type/Symfony/data/request_get_session_null.php | 6 +++--- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/stubs/Symfony/Component/HttpFoundation/Request.stub b/stubs/Symfony/Component/HttpFoundation/Request.stub index 861769df..0c2140cc 100644 --- a/stubs/Symfony/Component/HttpFoundation/Request.stub +++ b/stubs/Symfony/Component/HttpFoundation/Request.stub @@ -26,11 +26,6 @@ class Request */ public $cookies; - /** - * @return \Symfony\Component\HttpFoundation\Session\Session - */ - public function getSession(); - /** * @return string[] */ diff --git a/tests/Type/Symfony/data/request_get_session.php b/tests/Type/Symfony/data/request_get_session.php index 17ef88a6..9a0335e5 100644 --- a/tests/Type/Symfony/data/request_get_session.php +++ b/tests/Type/Symfony/data/request_get_session.php @@ -1,14 +1,14 @@ getSession(); -assertType(Session::class, $request->getSession()); +assertType(SessionInterface::class, $request->getSession()); if ($request->hasSession()) { - assertType(Session::class, $request->getSession()); + assertType(SessionInterface::class, $request->getSession()); } diff --git a/tests/Type/Symfony/data/request_get_session_null.php b/tests/Type/Symfony/data/request_get_session_null.php index be0fd0d1..9c37979d 100644 --- a/tests/Type/Symfony/data/request_get_session_null.php +++ b/tests/Type/Symfony/data/request_get_session_null.php @@ -1,14 +1,14 @@ getSession(); -assertType(Session::class . '|null', $request->getSession()); +assertType(SessionInterface::class . '|null', $request->getSession()); if ($request->hasSession()) { - assertType(Session::class, $request->getSession()); + assertType(SessionInterface::class, $request->getSession()); } From 44aef787a1062d2eec7932e958f0fbe6b2a22a67 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Tue, 31 Jan 2023 16:20:29 +0100 Subject: [PATCH 118/208] Introduce Voter stub --- extension.neon | 2 ++ .../Core/Authorization/Voter/Voter.stub | 35 +++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 stubs/Symfony/Component/Security/Core/Authorization/Voter/Voter.stub diff --git a/extension.neon b/extension.neon index 582126af..e0c53ba0 100644 --- a/extension.neon +++ b/extension.neon @@ -14,6 +14,7 @@ parameters: featureToggles: skipCheckGenericClasses: - Symfony\Component\OptionsResolver\Options + - Symfony\Component\Security\Core\Authorization\Voter\Voter - Symfony\Component\Security\Core\User\PasswordUpgraderInterface stubFiles: - stubs/Psr/Cache/CacheItemInterface.stub @@ -50,6 +51,7 @@ parameters: - 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/Security/Core/User/PasswordAuthenticatedUserInterface.stub - stubs/Symfony/Component/Security/Core/User/PasswordUpgraderInterface.stub diff --git a/stubs/Symfony/Component/Security/Core/Authorization/Voter/Voter.stub b/stubs/Symfony/Component/Security/Core/Authorization/Voter/Voter.stub new file mode 100644 index 00000000..17485a28 --- /dev/null +++ b/stubs/Symfony/Component/Security/Core/Authorization/Voter/Voter.stub @@ -0,0 +1,35 @@ + Date: Tue, 31 Jan 2023 16:32:08 +0100 Subject: [PATCH 119/208] Fix build --- .../Symfony/InvalidArgumentDefaultValueRule.php | 2 +- src/Rules/Symfony/InvalidOptionDefaultValueRule.php | 2 +- src/Type/Symfony/EnvelopeReturnTypeExtension.php | 11 ++++++++--- .../Symfony/SerializerDynamicReturnTypeExtension.php | 12 ++++++++---- 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php b/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php index 51d7c3bc..e48f8718 100644 --- a/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php +++ b/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php @@ -46,7 +46,7 @@ public function processNode(Node $node, Scope $scope): array } $modeType = isset($node->getArgs()[1]) ? $scope->getType($node->getArgs()[1]->value) : new NullType(); - if ($modeType instanceof NullType) { + if ($modeType->isNull()->yes()) { $modeType = new ConstantIntegerType(2); // InputArgument::OPTIONAL } $modeTypes = TypeUtils::getConstantScalars($modeType); diff --git a/src/Rules/Symfony/InvalidOptionDefaultValueRule.php b/src/Rules/Symfony/InvalidOptionDefaultValueRule.php index ab52d626..171f94f1 100644 --- a/src/Rules/Symfony/InvalidOptionDefaultValueRule.php +++ b/src/Rules/Symfony/InvalidOptionDefaultValueRule.php @@ -48,7 +48,7 @@ public function processNode(Node $node, Scope $scope): array } $modeType = isset($node->getArgs()[2]) ? $scope->getType($node->getArgs()[2]->value) : new NullType(); - if ($modeType instanceof NullType) { + if ($modeType->isNull()->yes()) { $modeType = new ConstantIntegerType(1); // InputOption::VALUE_NONE } $modeTypes = TypeUtils::getConstantScalars($modeType); diff --git a/src/Type/Symfony/EnvelopeReturnTypeExtension.php b/src/Type/Symfony/EnvelopeReturnTypeExtension.php index ec2531aa..2e01dec7 100644 --- a/src/Type/Symfony/EnvelopeReturnTypeExtension.php +++ b/src/Type/Symfony/EnvelopeReturnTypeExtension.php @@ -7,12 +7,12 @@ use PHPStan\Reflection\MethodReflection; use PHPStan\Type\Accessory\AccessoryArrayListType; use PHPStan\Type\ArrayType; -use PHPStan\Type\Constant\ConstantStringType; use PHPStan\Type\DynamicMethodReturnTypeExtension; use PHPStan\Type\Generic\GenericClassStringType; use PHPStan\Type\IntegerType; use PHPStan\Type\ObjectType; use PHPStan\Type\Type; +use PHPStan\Type\TypeCombinator; use function count; final class EnvelopeReturnTypeExtension implements DynamicMethodReturnTypeExtension @@ -42,11 +42,16 @@ public function getTypeFromMethodCall( } $argType = $scope->getType($methodCall->getArgs()[0]->value); - if (!$argType instanceof ConstantStringType) { + if (count($argType->getConstantStrings()) === 0) { return AccessoryArrayListType::intersectWith(new ArrayType(new IntegerType(), new ObjectType('Symfony\Component\Messenger\Stamp\StampInterface'))); } - return AccessoryArrayListType::intersectWith(new ArrayType(new IntegerType(), new ObjectType($argType->getValue()))); + $objectTypes = []; + foreach ($argType->getConstantStrings() as $constantString) { + $objectTypes[] = new ObjectType($constantString->getValue()); + } + + return AccessoryArrayListType::intersectWith(new ArrayType(new IntegerType(), TypeCombinator::union(...$objectTypes))); } } diff --git a/src/Type/Symfony/SerializerDynamicReturnTypeExtension.php b/src/Type/Symfony/SerializerDynamicReturnTypeExtension.php index 5525a770..d3ea959d 100755 --- a/src/Type/Symfony/SerializerDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/SerializerDynamicReturnTypeExtension.php @@ -6,11 +6,12 @@ use PHPStan\Analyser\Scope; use PHPStan\Reflection\MethodReflection; use PHPStan\Type\ArrayType; -use PHPStan\Type\Constant\ConstantStringType; use PHPStan\Type\DynamicMethodReturnTypeExtension; use PHPStan\Type\MixedType; use PHPStan\Type\ObjectType; use PHPStan\Type\Type; +use PHPStan\Type\TypeCombinator; +use function count; use function substr; class SerializerDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension @@ -45,13 +46,16 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method } $argType = $scope->getType($methodCall->getArgs()[1]->value); - if (!$argType instanceof ConstantStringType) { + if (count($argType->getConstantStrings()) === 0) { return new MixedType(); } - $objectName = $argType->getValue(); + $types = []; + foreach ($argType->getConstantStrings() as $constantString) { + $types[] = $this->getType($constantString->getValue()); + } - return $this->getType($objectName); + return TypeCombinator::union(...$types); } private function getType(string $objectName): Type From d0151d06555e446634c7b8175d1feb292b102085 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Tue, 31 Jan 2023 16:41:53 +0100 Subject: [PATCH 120/208] Bump minimum PHPStan version --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index adc7050a..465f7050 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ "require": { "php": "^7.2 || ^8.0", "ext-simplexml": "*", - "phpstan/phpstan": "^1.9.1" + "phpstan/phpstan": "^1.9.4" }, "conflict": { "symfony/framework-bundle": "<3.0" From 2107deec9d95c0e7fbba35fe4d34287342878ef7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20W=C5=82odarczyk?= Date: Tue, 31 Jan 2023 19:10:01 +0100 Subject: [PATCH 121/208] Add ArrayObject to NormalizerInterface::normalize() return type. --- .../Component/Serializer/Normalizer/NormalizerInterface.stub | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/stubs/Symfony/Component/Serializer/Normalizer/NormalizerInterface.stub b/stubs/Symfony/Component/Serializer/Normalizer/NormalizerInterface.stub index a65b5bed..8fc966ad 100644 --- a/stubs/Symfony/Component/Serializer/Normalizer/NormalizerInterface.stub +++ b/stubs/Symfony/Component/Serializer/Normalizer/NormalizerInterface.stub @@ -2,13 +2,16 @@ namespace Symfony\Component\Serializer\Normalizer; +use ArrayObject; + interface NormalizerInterface { /** * @param mixed $object * @param string|null $format * @param array $context - * @return array|string|int|float|bool|null + * + * @return array|ArrayObject|string|int|float|bool|null */ public function normalize($object, $format = null, array $context = []); From cbf5b9ceadab8365ed46db42dcd23db1a4c26c93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20W=C5=82odarczyk?= Date: Tue, 31 Jan 2023 19:23:00 +0100 Subject: [PATCH 122/208] Add @throws annotations in DenormalizerInterface::denormalize() and NormalizerInterface::normalize(). --- extension.neon | 8 ++++++++ .../Exception/BadMethodCallException.stub | 7 +++++++ .../Exception/CircularReferenceException.stub | 7 +++++++ .../Exception/ExceptionInterface.stub | 7 +++++++ .../Exception/ExtraAttributesException.stub | 20 +++++++++++++++++++ .../Exception/InvalidArgumentException.stub | 7 +++++++ .../Serializer/Exception/LogicException.stub | 7 +++++++ .../Exception/RuntimeException.stub | 7 +++++++ .../Exception/UnexpectedValueException.stub | 7 +++++++ .../Normalizer/DenormalizerInterface.stub | 16 +++++++++++++++ .../Normalizer/NormalizerInterface.stub | 9 +++++++++ 11 files changed, 102 insertions(+) create mode 100644 stubs/Symfony/Component/Serializer/Exception/BadMethodCallException.stub create mode 100644 stubs/Symfony/Component/Serializer/Exception/CircularReferenceException.stub create mode 100644 stubs/Symfony/Component/Serializer/Exception/ExceptionInterface.stub create mode 100644 stubs/Symfony/Component/Serializer/Exception/ExtraAttributesException.stub create mode 100644 stubs/Symfony/Component/Serializer/Exception/InvalidArgumentException.stub create mode 100644 stubs/Symfony/Component/Serializer/Exception/LogicException.stub create mode 100644 stubs/Symfony/Component/Serializer/Exception/RuntimeException.stub create mode 100644 stubs/Symfony/Component/Serializer/Exception/UnexpectedValueException.stub diff --git a/extension.neon b/extension.neon index e0c53ba0..42ae87ba 100644 --- a/extension.neon +++ b/extension.neon @@ -58,6 +58,14 @@ parameters: - 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 diff --git a/stubs/Symfony/Component/Serializer/Exception/BadMethodCallException.stub b/stubs/Symfony/Component/Serializer/Exception/BadMethodCallException.stub new file mode 100644 index 00000000..3c569971 --- /dev/null +++ b/stubs/Symfony/Component/Serializer/Exception/BadMethodCallException.stub @@ -0,0 +1,7 @@ + $context * @return mixed + * + * @throws BadMethodCallException + * @throws InvalidArgumentException + * @throws UnexpectedValueException + * @throws ExtraAttributesException + * @throws LogicException + * @throws RuntimeException + * @throws ExceptionInterface */ public function denormalize($data, $type, $format = null, array $context = []); diff --git a/stubs/Symfony/Component/Serializer/Normalizer/NormalizerInterface.stub b/stubs/Symfony/Component/Serializer/Normalizer/NormalizerInterface.stub index 8fc966ad..fac60042 100644 --- a/stubs/Symfony/Component/Serializer/Normalizer/NormalizerInterface.stub +++ b/stubs/Symfony/Component/Serializer/Normalizer/NormalizerInterface.stub @@ -3,6 +3,10 @@ namespace Symfony\Component\Serializer\Normalizer; use ArrayObject; +use Symfony\Component\Serializer\Exception\CircularReferenceException; +use Symfony\Component\Serializer\Exception\ExceptionInterface; +use Symfony\Component\Serializer\Exception\InvalidArgumentException; +use Symfony\Component\Serializer\Exception\LogicException; interface NormalizerInterface { @@ -12,6 +16,11 @@ interface NormalizerInterface * @param array $context * * @return array|ArrayObject|string|int|float|bool|null + * + * @throws InvalidArgumentException + * @throws CircularReferenceException + * @throws LogicException + * @throws ExceptionInterface */ public function normalize($object, $format = null, array $context = []); From 8a8d0538ca943b20beda7e9799e14fb3683262d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20W=C5=82odarczyk?= Date: Sat, 4 Feb 2023 09:56:44 +0100 Subject: [PATCH 123/208] Add missing @throws in stubs. --- extension.neon | 5 +++++ stubs/Psr/Cache/CacheException.stub | 7 +++++++ stubs/Psr/Cache/InvalidArgumentException.stub | 7 +++++++ stubs/Symfony/Component/Console/Command.stub | 6 ++++++ .../Component/Console/Exception/ExceptionInterface.stub | 7 +++++++ .../Console/Exception/InvalidArgumentException.stub | 7 +++++++ .../Component/Console/Exception/LogicException.stub | 7 +++++++ .../DependencyInjection/Extension/ExtensionInterface.stub | 2 ++ .../Component/Serializer/Encoder/DecoderInterface.stub | 2 ++ .../Component/Serializer/Encoder/EncoderInterface.stub | 4 ++++ stubs/Symfony/Contracts/Cache/CacheInterface.stub | 4 ++++ 11 files changed, 58 insertions(+) create mode 100644 stubs/Psr/Cache/CacheException.stub create mode 100644 stubs/Psr/Cache/InvalidArgumentException.stub create mode 100644 stubs/Symfony/Component/Console/Exception/ExceptionInterface.stub create mode 100644 stubs/Symfony/Component/Console/Exception/InvalidArgumentException.stub create mode 100644 stubs/Symfony/Component/Console/Exception/LogicException.stub diff --git a/extension.neon b/extension.neon index 42ae87ba..e4880077 100644 --- a/extension.neon +++ b/extension.neon @@ -17,11 +17,16 @@ parameters: - Symfony\Component\Security\Core\Authorization\Voter\Voter - Symfony\Component\Security\Core\User\PasswordUpgraderInterface stubFiles: + - stubs/Psr/Cache/CacheException.stub - stubs/Psr/Cache/CacheItemInterface.stub + - stubs/Psr/Cache/InvalidArgumentException.stub - stubs/Symfony/Bundle/FrameworkBundle/KernelBrowser.stub - stubs/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.stub - stubs/Symfony/Bundle/FrameworkBundle/Test/TestContainer.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 diff --git a/stubs/Psr/Cache/CacheException.stub b/stubs/Psr/Cache/CacheException.stub new file mode 100644 index 00000000..1be3e49b --- /dev/null +++ b/stubs/Psr/Cache/CacheException.stub @@ -0,0 +1,7 @@ + $configs + * + * @throws \InvalidArgumentException */ public function load(array $configs, ContainerBuilder $container): void; } diff --git a/stubs/Symfony/Component/Serializer/Encoder/DecoderInterface.stub b/stubs/Symfony/Component/Serializer/Encoder/DecoderInterface.stub index 715dadc4..a9923edf 100644 --- a/stubs/Symfony/Component/Serializer/Encoder/DecoderInterface.stub +++ b/stubs/Symfony/Component/Serializer/Encoder/DecoderInterface.stub @@ -11,6 +11,8 @@ interface DecoderInterface * @param string $format * @param array $context * @return mixed + * + * @throws UnexpectedValueException */ public function decode($data, $format, array $context = []); diff --git a/stubs/Symfony/Component/Serializer/Encoder/EncoderInterface.stub b/stubs/Symfony/Component/Serializer/Encoder/EncoderInterface.stub index eb6fc97c..2d97bde3 100644 --- a/stubs/Symfony/Component/Serializer/Encoder/EncoderInterface.stub +++ b/stubs/Symfony/Component/Serializer/Encoder/EncoderInterface.stub @@ -2,6 +2,8 @@ namespace Symfony\Component\Serializer\Encoder; +use Symfony\Component\Serializer\Exception\UnexpectedValueException; + interface EncoderInterface { /** @@ -9,6 +11,8 @@ interface EncoderInterface * @param string $format * @param array $context * @return string + * + * @throws UnexpectedValueException */ public function encode($data, $format, array $context = []); diff --git a/stubs/Symfony/Contracts/Cache/CacheInterface.stub b/stubs/Symfony/Contracts/Cache/CacheInterface.stub index f4cc208d..ff3027b1 100644 --- a/stubs/Symfony/Contracts/Cache/CacheInterface.stub +++ b/stubs/Symfony/Contracts/Cache/CacheInterface.stub @@ -2,6 +2,8 @@ namespace Symfony\Contracts\Cache; +use Psr\Cache\InvalidArgumentException; + interface CacheInterface { /** @@ -10,6 +12,8 @@ interface CacheInterface * @param \Symfony\Contracts\Cache\CallbackInterface|callable(\Symfony\Contracts\Cache\ItemInterface, bool): T $callback * @param array $metadata * @return T + * + * @throws InvalidArgumentException */ public function get(string $key, callable $callback, float $beta = null, array &$metadata = null); } From 58e64ffc912c984f448e5ed800ca6c8ce343a0e9 Mon Sep 17 00:00:00 2001 From: jmleroux Date: Wed, 15 Feb 2023 05:55:06 +0100 Subject: [PATCH 124/208] Add PHPUnit compatibility for PHP 7.2 --- .github/workflows/build.yml | 12 ------------ composer.json | 2 +- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1045f11c..96765433 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -33,10 +33,6 @@ jobs: coverage: "none" php-version: "${{ matrix.php-version }}" - - name: "Downgrade PHPUnit" - if: matrix.php-version == '7.2' - run: "composer require --dev phpunit/phpunit:^8.5.29 --no-update --update-with-dependencies" - - name: "Validate Composer" run: "composer validate" @@ -101,10 +97,6 @@ jobs: coverage: "none" php-version: "${{ matrix.php-version }}" - - name: "Downgrade PHPUnit" - if: matrix.php-version == '7.2' - run: "composer require --dev phpunit/phpunit:^8.5.29 --no-update --update-with-dependencies" - - name: "Install lowest dependencies" if: ${{ matrix.dependencies == 'lowest' }} run: "composer update --prefer-lowest --no-interaction --no-progress" @@ -146,10 +138,6 @@ jobs: extensions: mbstring tools: composer:v2 - - name: "Downgrade PHPUnit" - if: matrix.php-version == '7.2' - run: "composer require --dev phpunit/phpunit:^8.5.29 --no-update --update-with-dependencies" - - name: "Install lowest dependencies" if: ${{ matrix.dependencies == 'lowest' }} run: "composer update --prefer-lowest --no-interaction --no-progress" diff --git a/composer.json b/composer.json index 465f7050..9f8c6569 100644 --- a/composer.json +++ b/composer.json @@ -25,7 +25,7 @@ "php-parallel-lint/php-parallel-lint": "^1.2", "phpstan/phpstan-phpunit": "^1.0", "phpstan/phpstan-strict-rules": "^1.0", - "phpunit/phpunit": "^9.5", + "phpunit/phpunit": "^8.5.29 || ^9.5", "psr/container": "1.0 || 1.1.1", "symfony/config": "^5.4 || ^6.1", "symfony/console": "^5.4 || ^6.1", From 2f363239357b849bc150295b554100c106b6c26b Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Wed, 8 Mar 2023 17:31:10 +0100 Subject: [PATCH 125/208] Fix build --- src/Type/Symfony/ParameterDynamicReturnTypeExtension.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php index b160ddc8..82d3599e 100644 --- a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php @@ -14,7 +14,6 @@ use PHPStan\Type\BooleanType; use PHPStan\Type\Constant\ConstantArrayType; use PHPStan\Type\Constant\ConstantBooleanType; -use PHPStan\Type\Constant\ConstantStringType; use PHPStan\Type\ConstantType; use PHPStan\Type\DynamicMethodReturnTypeExtension; use PHPStan\Type\FloatType; @@ -160,9 +159,12 @@ private function generalizeTypeFromValue(Scope $scope, $value): Type $keyTypes = []; $valueTypes = []; foreach ($value as $key => $element) { - /** @var ConstantStringType $keyType */ $keyType = $scope->getTypeFromValue($key); - $keyTypes[] = $keyType; + $keyStringTypes = $keyType->getConstantStrings(); + if (count($keyStringTypes) !== 1) { + throw new ShouldNotHappenException(); + } + $keyTypes[] = $keyStringTypes[0]; $valueTypes[] = $this->generalizeTypeFromValue($scope, $element); } From d7425ae62493f0462e6d3aa18cba8b90dea897f1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 20 Mar 2023 02:31:07 +0000 Subject: [PATCH 126/208] Update metcalfc/changelog-generator action to v4.1.0 --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bac4a006..92b72547 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,7 +18,7 @@ jobs: - name: Generate changelog id: changelog - uses: metcalfc/changelog-generator@v4.0.1 + uses: metcalfc/changelog-generator@v4.1.0 with: myToken: ${{ secrets.PHPSTAN_BOT_TOKEN }} From e0823d3daf5220ea2a07751ddffa3ad0882bdfe8 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Wed, 22 Mar 2023 09:34:23 +0100 Subject: [PATCH 127/208] Update PHPCS --- build-cs/composer.json | 7 +-- build-cs/composer.lock | 117 ++++++++++++++++++++++------------------- 2 files changed, 67 insertions(+), 57 deletions(-) diff --git a/build-cs/composer.json b/build-cs/composer.json index e3079710..16a240bc 100644 --- a/build-cs/composer.json +++ b/build-cs/composer.json @@ -1,8 +1,9 @@ { "require-dev": { - "consistence-community/coding-standard": "^3.10", - "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", - "slevomat/coding-standard": "^7.0" + "consistence-community/coding-standard": "^3.11.0", + "dealerdirect/phpcodesniffer-composer-installer": "^1.0.0", + "slevomat/coding-standard": "^8.8.0", + "squizlabs/php_codesniffer": "^3.5.3" }, "config": { "allow-plugins": { diff --git a/build-cs/composer.lock b/build-cs/composer.lock index 4bcc8de4..c25a151a 100644 --- a/build-cs/composer.lock +++ b/build-cs/composer.lock @@ -4,35 +4,35 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "4485bbedba7bcc71ace5f69dbb9b6c47", + "content-hash": "e69c1916405a7e3c8001c1b609a0ee61", "packages": [], "packages-dev": [ { "name": "consistence-community/coding-standard", - "version": "3.11.1", + "version": "3.11.2", "source": { "type": "git", "url": "https://github.com/consistence-community/coding-standard.git", - "reference": "4632fead8c9ee8f50044fcbce9f66c797b34c0df" + "reference": "adb4be482e76990552bf624309d2acc8754ba1bd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consistence-community/coding-standard/zipball/4632fead8c9ee8f50044fcbce9f66c797b34c0df", - "reference": "4632fead8c9ee8f50044fcbce9f66c797b34c0df", + "url": "https://api.github.com/repos/consistence-community/coding-standard/zipball/adb4be482e76990552bf624309d2acc8754ba1bd", + "reference": "adb4be482e76990552bf624309d2acc8754ba1bd", "shasum": "" }, "require": { - "php": ">=7.4", - "slevomat/coding-standard": "~7.0", - "squizlabs/php_codesniffer": "~3.6.0" + "php": "~8.0", + "slevomat/coding-standard": "~8.0", + "squizlabs/php_codesniffer": "~3.7.0" }, "replace": { "consistence/coding-standard": "3.10.*" }, "require-dev": { - "phing/phing": "2.16.4", - "php-parallel-lint/php-parallel-lint": "1.3.0", - "phpunit/phpunit": "9.5.4" + "phing/phing": "2.17.0", + "php-parallel-lint/php-parallel-lint": "1.3.1", + "phpunit/phpunit": "9.5.10" }, "type": "library", "autoload": { @@ -70,41 +70,44 @@ ], "support": { "issues": "https://github.com/consistence-community/coding-standard/issues", - "source": "https://github.com/consistence-community/coding-standard/tree/3.11.1" + "source": "https://github.com/consistence-community/coding-standard/tree/3.11.2" }, - "time": "2021-05-03T18:13:22+00:00" + "time": "2022-06-21T08:36:36+00:00" }, { "name": "dealerdirect/phpcodesniffer-composer-installer", - "version": "v0.7.2", + "version": "v1.0.0", "source": { "type": "git", - "url": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer.git", - "reference": "1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db" + "url": "https://github.com/PHPCSStandards/composer-installer.git", + "reference": "4be43904336affa5c2f70744a348312336afd0da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Dealerdirect/phpcodesniffer-composer-installer/zipball/1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db", - "reference": "1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db", + "url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/4be43904336affa5c2f70744a348312336afd0da", + "reference": "4be43904336affa5c2f70744a348312336afd0da", "shasum": "" }, "require": { "composer-plugin-api": "^1.0 || ^2.0", - "php": ">=5.3", + "php": ">=5.4", "squizlabs/php_codesniffer": "^2.0 || ^3.1.0 || ^4.0" }, "require-dev": { "composer/composer": "*", + "ext-json": "*", + "ext-zip": "*", "php-parallel-lint/php-parallel-lint": "^1.3.1", - "phpcompatibility/php-compatibility": "^9.0" + "phpcompatibility/php-compatibility": "^9.0", + "yoast/phpunit-polyfills": "^1.0" }, "type": "composer-plugin", "extra": { - "class": "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin" + "class": "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin" }, "autoload": { "psr-4": { - "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/" + "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -120,7 +123,7 @@ }, { "name": "Contributors", - "homepage": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer/graphs/contributors" + "homepage": "https://github.com/PHPCSStandards/composer-installer/graphs/contributors" } ], "description": "PHP_CodeSniffer Standards Composer Installer Plugin", @@ -144,23 +147,23 @@ "tests" ], "support": { - "issues": "https://github.com/dealerdirect/phpcodesniffer-composer-installer/issues", - "source": "https://github.com/dealerdirect/phpcodesniffer-composer-installer" + "issues": "https://github.com/PHPCSStandards/composer-installer/issues", + "source": "https://github.com/PHPCSStandards/composer-installer" }, - "time": "2022-02-04T12:51:07+00:00" + "time": "2023-01-05T11:28:13+00:00" }, { "name": "phpstan/phpdoc-parser", - "version": "1.5.1", + "version": "1.15.3", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "981cc368a216c988e862a75e526b6076987d1b50" + "reference": "61800f71a5526081d1b5633766aa88341f1ade76" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/981cc368a216c988e862a75e526b6076987d1b50", - "reference": "981cc368a216c988e862a75e526b6076987d1b50", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/61800f71a5526081d1b5633766aa88341f1ade76", + "reference": "61800f71a5526081d1b5633766aa88341f1ade76", "shasum": "" }, "require": { @@ -170,6 +173,7 @@ "php-parallel-lint/php-parallel-lint": "^1.2", "phpstan/extension-installer": "^1.0", "phpstan/phpstan": "^1.5", + "phpstan/phpstan-phpunit": "^1.1", "phpstan/phpstan-strict-rules": "^1.0", "phpunit/phpunit": "^9.5", "symfony/process": "^5.2" @@ -189,43 +193,43 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.5.1" + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.15.3" }, - "time": "2022-05-05T11:32:40+00:00" + "time": "2022-12-20T20:56:55+00:00" }, { "name": "slevomat/coding-standard", - "version": "7.2.1", + "version": "8.8.0", "source": { "type": "git", "url": "https://github.com/slevomat/coding-standard.git", - "reference": "aff06ae7a84e4534bf6f821dc982a93a5d477c90" + "reference": "59e25146a4ef0a7b194c5bc55b32dd414345db89" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/aff06ae7a84e4534bf6f821dc982a93a5d477c90", - "reference": "aff06ae7a84e4534bf6f821dc982a93a5d477c90", + "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/59e25146a4ef0a7b194c5bc55b32dd414345db89", + "reference": "59e25146a4ef0a7b194c5bc55b32dd414345db89", "shasum": "" }, "require": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7", + "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7 || ^1.0", "php": "^7.2 || ^8.0", - "phpstan/phpdoc-parser": "^1.5.1", - "squizlabs/php_codesniffer": "^3.6.2" + "phpstan/phpdoc-parser": ">=1.15.2 <1.16.0", + "squizlabs/php_codesniffer": "^3.7.1" }, "require-dev": { - "phing/phing": "2.17.3", + "phing/phing": "2.17.4", "php-parallel-lint/php-parallel-lint": "1.3.2", - "phpstan/phpstan": "1.4.10|1.7.1", - "phpstan/phpstan-deprecation-rules": "1.0.0", - "phpstan/phpstan-phpunit": "1.0.0|1.1.1", - "phpstan/phpstan-strict-rules": "1.2.3", - "phpunit/phpunit": "7.5.20|8.5.21|9.5.20" + "phpstan/phpstan": "1.4.10|1.9.6", + "phpstan/phpstan-deprecation-rules": "1.1.1", + "phpstan/phpstan-phpunit": "1.0.0|1.3.3", + "phpstan/phpstan-strict-rules": "1.4.4", + "phpunit/phpunit": "7.5.20|8.5.21|9.5.27" }, "type": "phpcodesniffer-standard", "extra": { "branch-alias": { - "dev-master": "7.x-dev" + "dev-master": "8.x-dev" } }, "autoload": { @@ -238,9 +242,13 @@ "MIT" ], "description": "Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.", + "keywords": [ + "dev", + "phpcs" + ], "support": { "issues": "https://github.com/slevomat/coding-standard/issues", - "source": "https://github.com/slevomat/coding-standard/tree/7.2.1" + "source": "https://github.com/slevomat/coding-standard/tree/8.8.0" }, "funding": [ { @@ -252,20 +260,20 @@ "type": "tidelift" } ], - "time": "2022-05-25T10:58:12+00:00" + "time": "2023-01-09T10:46:13+00:00" }, { "name": "squizlabs/php_codesniffer", - "version": "3.6.2", + "version": "3.7.2", "source": { "type": "git", "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "5e4e71592f69da17871dba6e80dd51bce74a351a" + "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/5e4e71592f69da17871dba6e80dd51bce74a351a", - "reference": "5e4e71592f69da17871dba6e80dd51bce74a351a", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/ed8e00df0a83aa96acf703f8c2979ff33341f879", + "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879", "shasum": "" }, "require": { @@ -301,14 +309,15 @@ "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", "keywords": [ "phpcs", - "standards" + "standards", + "static analysis" ], "support": { "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", "source": "https://github.com/squizlabs/PHP_CodeSniffer", "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" }, - "time": "2021-12-12T21:44:58+00:00" + "time": "2023-02-22T23:07:41+00:00" } ], "aliases": [], From db81b1861aac7cc2e66115cb33b4d1ea2a73d096 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Thu, 30 Mar 2023 10:38:10 +0200 Subject: [PATCH 128/208] Use stubfile extension for PasswordUpgraderInterface --- composer.json | 2 +- extension.neon | 6 ++-- src/Symfony/InputBagStubFilesExtension.php | 17 +++++++-- ...ordAuthenticatedUserStubFilesExtension.php | 36 +++++++++++++++++++ 4 files changed, 56 insertions(+), 5 deletions(-) create mode 100644 src/Symfony/PasswordAuthenticatedUserStubFilesExtension.php diff --git a/composer.json b/composer.json index 9f8c6569..22501953 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ "require": { "php": "^7.2 || ^8.0", "ext-simplexml": "*", - "phpstan/phpstan": "^1.9.4" + "phpstan/phpstan": "^1.9.18" }, "conflict": { "symfony/framework-bundle": "<3.0" diff --git a/extension.neon b/extension.neon index e4880077..1a8cf313 100644 --- a/extension.neon +++ b/extension.neon @@ -58,8 +58,6 @@ parameters: - 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/Security/Core/User/PasswordAuthenticatedUserInterface.stub - - stubs/Symfony/Component/Security/Core/User/PasswordUpgraderInterface.stub - stubs/Symfony/Component/Serializer/Encoder/ContextAwareDecoderInterface.stub - stubs/Symfony/Component/Serializer/Encoder/DecoderInterface.stub - stubs/Symfony/Component/Serializer/Encoder/EncoderInterface.stub @@ -298,6 +296,10 @@ services: class: PHPStan\Symfony\InputBagStubFilesExtension tags: - phpstan.stubFilesExtension + - + class: PHPStan\Symfony\PasswordAuthenticatedUserStubFilesExtension + tags: + - phpstan.stubFilesExtension # FormInterface::getErrors() return type - diff --git a/src/Symfony/InputBagStubFilesExtension.php b/src/Symfony/InputBagStubFilesExtension.php index 834ca228..140dae99 100644 --- a/src/Symfony/InputBagStubFilesExtension.php +++ b/src/Symfony/InputBagStubFilesExtension.php @@ -2,15 +2,28 @@ namespace PHPStan\Symfony; +use PHPStan\BetterReflection\Reflector\Exception\IdentifierNotFound; +use PHPStan\BetterReflection\Reflector\Reflector; use PHPStan\PhpDoc\StubFilesExtension; -use function class_exists; class InputBagStubFilesExtension implements StubFilesExtension { + /** @var Reflector */ + private $reflector; + + public function __construct( + Reflector $reflector + ) + { + $this->reflector = $reflector; + } + public function getFiles(): array { - if (!class_exists('Symfony\Component\HttpFoundation\InputBag')) { + try { + $this->reflector->reflectClass('Symfony\Component\HttpFoundation\InputBag'); + } catch (IdentifierNotFound $e) { return []; } diff --git a/src/Symfony/PasswordAuthenticatedUserStubFilesExtension.php b/src/Symfony/PasswordAuthenticatedUserStubFilesExtension.php new file mode 100644 index 00000000..8f8c4782 --- /dev/null +++ b/src/Symfony/PasswordAuthenticatedUserStubFilesExtension.php @@ -0,0 +1,36 @@ +reflector = $reflector; + } + + public function getFiles(): array + { + try { + $this->reflector->reflectClass('Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface'); + } catch (IdentifierNotFound $e) { + return []; + } + + return [ + __DIR__ . '/../../stubs/Symfony/Component/Security/Core/User/PasswordAuthenticatedUserInterface.stub', + __DIR__ . '/../../stubs/Symfony/Component/Security/Core/User/PasswordUpgraderInterface.stub', + ]; + } + +} From 1da7bf450c6b351fec08ca0aa97298473d4f6ab3 Mon Sep 17 00:00:00 2001 From: Yassine Guedidi Date: Thu, 23 Mar 2023 14:44:33 +0100 Subject: [PATCH 129/208] Add stubs for security factories --- extension.neon | 2 ++ .../Factory/AuthenticatorFactoryInterface.stub | 16 ++++++++++++++++ .../FirewallListenerFactoryInterface.stub | 15 +++++++++++++++ 3 files changed, 33 insertions(+) create mode 100644 stubs/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AuthenticatorFactoryInterface.stub create mode 100644 stubs/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/FirewallListenerFactoryInterface.stub diff --git a/extension.neon b/extension.neon index 1a8cf313..1dfcca46 100644 --- a/extension.neon +++ b/extension.neon @@ -23,6 +23,8 @@ parameters: - stubs/Symfony/Bundle/FrameworkBundle/KernelBrowser.stub - stubs/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.stub - stubs/Symfony/Bundle/FrameworkBundle/Test/TestContainer.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 diff --git a/stubs/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AuthenticatorFactoryInterface.stub b/stubs/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AuthenticatorFactoryInterface.stub new file mode 100644 index 00000000..8ca4b331 --- /dev/null +++ b/stubs/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AuthenticatorFactoryInterface.stub @@ -0,0 +1,16 @@ + $config + * + * @return string|string[] + */ + public function createAuthenticator(ContainerBuilder $container, string $firewallName, array $config, string $userProviderId); +} diff --git a/stubs/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/FirewallListenerFactoryInterface.stub b/stubs/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/FirewallListenerFactoryInterface.stub new file mode 100644 index 00000000..5c6b54fd --- /dev/null +++ b/stubs/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/FirewallListenerFactoryInterface.stub @@ -0,0 +1,15 @@ + $config + * + * @return string[] + */ + public function createListeners(ContainerBuilder $container, string $firewallName, array $config): array; +} From f45b9ceccfe81b6cd8229a8f9ad7060a8ee5cb30 Mon Sep 17 00:00:00 2001 From: Natsuki Ikeguchi Date: Fri, 14 Apr 2023 21:17:46 +0900 Subject: [PATCH 130/208] Support specifying type of data_class in forms --- extension.neon | 7 ++ .../Symfony/Component/Form/AbstractType.stub | 12 ++++ .../Component/Form/FormFactoryInterface.stub | 36 ++++++++++ .../Symfony/Component/Form/FormInterface.stub | 16 ++++- .../Form/FormTypeExtensionInterface.stub | 5 ++ .../Component/Form/FormTypeInterface.stub | 5 ++ .../Exception/InvalidOptionsException.stub | 7 ++ tests/Type/Symfony/ExtensionTest.php | 1 + tests/Type/Symfony/data/form_data_type.php | 72 +++++++++++++++++++ 9 files changed, 159 insertions(+), 2 deletions(-) create mode 100644 stubs/Symfony/Component/Form/AbstractType.stub create mode 100644 stubs/Symfony/Component/Form/FormFactoryInterface.stub create mode 100644 stubs/Symfony/Component/OptionsResolver/Exception/InvalidOptionsException.stub create mode 100644 tests/Type/Symfony/data/form_data_type.php diff --git a/extension.neon b/extension.neon index 1dfcca46..40165b39 100644 --- a/extension.neon +++ b/extension.neon @@ -13,6 +13,10 @@ parameters: consoleApplicationLoader: null featureToggles: skipCheckGenericClasses: + - Symfony\Component\Form\AbstractType + - Symfony\Component\Form\FormInterface + - Symfony\Component\Form\FormTypeExtensionInterface + - Symfony\Component\Form\FormTypeInterface - Symfony\Component\OptionsResolver\Options - Symfony\Component\Security\Core\Authorization\Voter\Voter - Symfony\Component\Security\Core\User\PasswordUpgraderInterface @@ -36,6 +40,7 @@ parameters: - 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 @@ -43,6 +48,7 @@ parameters: - stubs/Symfony/Component/Form/DataTransformerInterface.stub - stubs/Symfony/Component/Form/FormBuilderInterface.stub - stubs/Symfony/Component/Form/FormInterface.stub + - stubs/Symfony/Component/Form/FormFactoryInterface.stub - stubs/Symfony/Component/Form/FormTypeExtensionInterface.stub - stubs/Symfony/Component/Form/FormTypeInterface.stub - stubs/Symfony/Component/Form/FormView.stub @@ -52,6 +58,7 @@ parameters: - 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/Process.stub - stubs/Symfony/Component/PropertyAccess/PropertyPathInterface.stub diff --git a/stubs/Symfony/Component/Form/AbstractType.stub b/stubs/Symfony/Component/Form/AbstractType.stub new file mode 100644 index 00000000..76170a02 --- /dev/null +++ b/stubs/Symfony/Component/Form/AbstractType.stub @@ -0,0 +1,12 @@ + + */ +abstract class AbstractType implements FormTypeInterface +{ +} diff --git a/stubs/Symfony/Component/Form/FormFactoryInterface.stub b/stubs/Symfony/Component/Form/FormFactoryInterface.stub new file mode 100644 index 00000000..a2221ec0 --- /dev/null +++ b/stubs/Symfony/Component/Form/FormFactoryInterface.stub @@ -0,0 +1,36 @@ + + * @template TData + * + * @param class-string $type + * @param TData $data + * @param array $options + * + * @phpstan-return ($data is null ? FormInterface : FormInterface) + * + * @throws \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + */ + public function create(string $type = FormType::class, $data = null, array $options = []): FormInterface; + + /** + * @template TFormType of FormTypeInterface + * @template TData + * + * @param class-string $type + * @param TData $data + * @param array $options + * + * @phpstan-return ($data is null ? FormInterface : FormInterface) + * + * @throws \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + */ + public function createNamed(string $name, string $type = FormType::class, $data = null, array $options = []): FormInterface; +} diff --git a/stubs/Symfony/Component/Form/FormInterface.stub b/stubs/Symfony/Component/Form/FormInterface.stub index 6960cce5..4bd21229 100644 --- a/stubs/Symfony/Component/Form/FormInterface.stub +++ b/stubs/Symfony/Component/Form/FormInterface.stub @@ -3,10 +3,22 @@ namespace Symfony\Component\Form; /** - * @extends \ArrayAccess - * @extends \Traversable + * @template TData + * + * @extends \ArrayAccess> + * @extends \Traversable> */ interface FormInterface extends \ArrayAccess, \Traversable, \Countable { + /** + * @param TData $modelData + * + * @return $this + */ + public function setData($modelData): FormInterface; + /** + * @return TData + */ + public function getData(); } diff --git a/stubs/Symfony/Component/Form/FormTypeExtensionInterface.stub b/stubs/Symfony/Component/Form/FormTypeExtensionInterface.stub index 25b6f25d..234d1b2e 100644 --- a/stubs/Symfony/Component/Form/FormTypeExtensionInterface.stub +++ b/stubs/Symfony/Component/Form/FormTypeExtensionInterface.stub @@ -2,6 +2,9 @@ namespace Symfony\Component\Form; +/** + * @template TData + */ interface FormTypeExtensionInterface { /** @@ -10,11 +13,13 @@ interface FormTypeExtensionInterface public function buildForm(FormBuilderInterface $builder, array $options): void; /** + * @phpstan-param FormInterface $form * @param array $options */ public function buildView(FormView $view, FormInterface $form, array $options): void; /** + * @phpstan-param FormInterface $form * @param array $options */ public function finishView(FormView $view, FormInterface $form, array $options): void; diff --git a/stubs/Symfony/Component/Form/FormTypeInterface.stub b/stubs/Symfony/Component/Form/FormTypeInterface.stub index cebbc1c2..85bb539c 100644 --- a/stubs/Symfony/Component/Form/FormTypeInterface.stub +++ b/stubs/Symfony/Component/Form/FormTypeInterface.stub @@ -2,6 +2,9 @@ namespace Symfony\Component\Form; +/** + * @template TData + */ interface FormTypeInterface { /** @@ -10,11 +13,13 @@ interface FormTypeInterface public function buildForm(FormBuilderInterface $builder, array $options): void; /** + * @phpstan-param FormInterface $form * @param array $options */ public function buildView(FormView $view, FormInterface $form, array $options): void; /** + * @phpstan-param FormInterface $form * @param array $options */ public function finishView(FormView $view, FormInterface $form, array $options): void; diff --git a/stubs/Symfony/Component/OptionsResolver/Exception/InvalidOptionsException.stub b/stubs/Symfony/Component/OptionsResolver/Exception/InvalidOptionsException.stub new file mode 100644 index 00000000..2856e32b --- /dev/null +++ b/stubs/Symfony/Component/OptionsResolver/Exception/InvalidOptionsException.stub @@ -0,0 +1,7 @@ +gatherAssertTypes(__DIR__ . '/data/FormInterface_getErrors.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/cache.php'); + yield from $this->gatherAssertTypes(__DIR__ . '/data/form_data_type.php'); } /** diff --git a/tests/Type/Symfony/data/form_data_type.php b/tests/Type/Symfony/data/form_data_type.php new file mode 100644 index 00000000..524a5b7c --- /dev/null +++ b/tests/Type/Symfony/data/form_data_type.php @@ -0,0 +1,72 @@ + + */ +class DataClassType extends AbstractType +{ + + public function buildForm(FormBuilderInterface $builder, array $options): void + { + $builder + ->add('foo', NumberType::class) + ->add('bar', TextType::class) + ; + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver + ->setDefaults([ + 'data_class' => DataClass::class, + ]) + ; + } + +} + +class FormFactoryAwareClass +{ + + /** @var FormFactoryInterface */ + private $formFactory; + + public function __construct(FormFactoryInterface $formFactory) + { + $this->formFactory = $formFactory; + } + + public function doSomething(): void + { + $form = $this->formFactory->create(DataClassType::class, new DataClass()); + assertType('GenericFormDataType\DataClass', $form->getData()); + } + + public function doSomethingNullable(): void + { + $form = $this->formFactory->create(DataClassType::class); + assertType('GenericFormDataType\DataClass|null', $form->getData()); + } + +} From 7e78605a699d183f5a6936cf91904f4c16ca79b2 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Fri, 14 Apr 2023 18:58:33 +0200 Subject: [PATCH 131/208] Fix AbstractType stub --- stubs/Symfony/Component/Form/AbstractType.stub | 18 ++++++++++++++++++ .../Component/Form/FormTypeInterface.stub | 4 ++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/stubs/Symfony/Component/Form/AbstractType.stub b/stubs/Symfony/Component/Form/AbstractType.stub index 76170a02..da2b1439 100644 --- a/stubs/Symfony/Component/Form/AbstractType.stub +++ b/stubs/Symfony/Component/Form/AbstractType.stub @@ -9,4 +9,22 @@ namespace Symfony\Component\Form; */ abstract class AbstractType implements FormTypeInterface { + + /** + * @param array $options + */ + public function buildForm(FormBuilderInterface $builder, array $options): void; + + /** + * @param FormInterface $form + * @param array $options + */ + public function buildView(FormView $view, FormInterface $form, array $options): void; + + /** + * @param FormInterface $form + * @param array $options + */ + public function finishView(FormView $view, FormInterface $form, array $options): void; + } diff --git a/stubs/Symfony/Component/Form/FormTypeInterface.stub b/stubs/Symfony/Component/Form/FormTypeInterface.stub index 85bb539c..2f745283 100644 --- a/stubs/Symfony/Component/Form/FormTypeInterface.stub +++ b/stubs/Symfony/Component/Form/FormTypeInterface.stub @@ -13,13 +13,13 @@ interface FormTypeInterface public function buildForm(FormBuilderInterface $builder, array $options): void; /** - * @phpstan-param FormInterface $form + * @param FormInterface $form * @param array $options */ public function buildView(FormView $view, FormInterface $form, array $options): void; /** - * @phpstan-param FormInterface $form + * @param FormInterface $form * @param array $options */ public function finishView(FormView $view, FormInterface $form, array $options): void; From db75c81816167e797b8d74b545470d5244c8a7d4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 17 Apr 2023 01:41:01 +0000 Subject: [PATCH 132/208] Update build-cs --- build-cs/composer.lock | 50 +++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/build-cs/composer.lock b/build-cs/composer.lock index c25a151a..2eafc159 100644 --- a/build-cs/composer.lock +++ b/build-cs/composer.lock @@ -9,16 +9,16 @@ "packages-dev": [ { "name": "consistence-community/coding-standard", - "version": "3.11.2", + "version": "3.11.3", "source": { "type": "git", "url": "https://github.com/consistence-community/coding-standard.git", - "reference": "adb4be482e76990552bf624309d2acc8754ba1bd" + "reference": "f38e06327d5bf80ff5ff523a2c05e623b5e8d8b1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consistence-community/coding-standard/zipball/adb4be482e76990552bf624309d2acc8754ba1bd", - "reference": "adb4be482e76990552bf624309d2acc8754ba1bd", + "url": "https://api.github.com/repos/consistence-community/coding-standard/zipball/f38e06327d5bf80ff5ff523a2c05e623b5e8d8b1", + "reference": "f38e06327d5bf80ff5ff523a2c05e623b5e8d8b1", "shasum": "" }, "require": { @@ -70,9 +70,9 @@ ], "support": { "issues": "https://github.com/consistence-community/coding-standard/issues", - "source": "https://github.com/consistence-community/coding-standard/tree/3.11.2" + "source": "https://github.com/consistence-community/coding-standard/tree/3.11.3" }, - "time": "2022-06-21T08:36:36+00:00" + "time": "2023-03-27T14:55:41+00:00" }, { "name": "dealerdirect/phpcodesniffer-composer-installer", @@ -154,16 +154,16 @@ }, { "name": "phpstan/phpdoc-parser", - "version": "1.15.3", + "version": "1.18.1", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "61800f71a5526081d1b5633766aa88341f1ade76" + "reference": "22dcdfd725ddf99583bfe398fc624ad6c5004a0f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/61800f71a5526081d1b5633766aa88341f1ade76", - "reference": "61800f71a5526081d1b5633766aa88341f1ade76", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/22dcdfd725ddf99583bfe398fc624ad6c5004a0f", + "reference": "22dcdfd725ddf99583bfe398fc624ad6c5004a0f", "shasum": "" }, "require": { @@ -193,38 +193,38 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.15.3" + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.18.1" }, - "time": "2022-12-20T20:56:55+00:00" + "time": "2023-04-07T11:51:11+00:00" }, { "name": "slevomat/coding-standard", - "version": "8.8.0", + "version": "8.10.0", "source": { "type": "git", "url": "https://github.com/slevomat/coding-standard.git", - "reference": "59e25146a4ef0a7b194c5bc55b32dd414345db89" + "reference": "c4e213e6e57f741451a08e68ef838802eec92287" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/59e25146a4ef0a7b194c5bc55b32dd414345db89", - "reference": "59e25146a4ef0a7b194c5bc55b32dd414345db89", + "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/c4e213e6e57f741451a08e68ef838802eec92287", + "reference": "c4e213e6e57f741451a08e68ef838802eec92287", "shasum": "" }, "require": { "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7 || ^1.0", "php": "^7.2 || ^8.0", - "phpstan/phpdoc-parser": ">=1.15.2 <1.16.0", + "phpstan/phpdoc-parser": ">=1.18.0 <1.19.0", "squizlabs/php_codesniffer": "^3.7.1" }, "require-dev": { "phing/phing": "2.17.4", "php-parallel-lint/php-parallel-lint": "1.3.2", - "phpstan/phpstan": "1.4.10|1.9.6", - "phpstan/phpstan-deprecation-rules": "1.1.1", - "phpstan/phpstan-phpunit": "1.0.0|1.3.3", - "phpstan/phpstan-strict-rules": "1.4.4", - "phpunit/phpunit": "7.5.20|8.5.21|9.5.27" + "phpstan/phpstan": "1.4.10|1.10.11", + "phpstan/phpstan-deprecation-rules": "1.1.3", + "phpstan/phpstan-phpunit": "1.0.0|1.3.11", + "phpstan/phpstan-strict-rules": "1.5.1", + "phpunit/phpunit": "7.5.20|8.5.21|9.6.6|10.0.19" }, "type": "phpcodesniffer-standard", "extra": { @@ -234,7 +234,7 @@ }, "autoload": { "psr-4": { - "SlevomatCodingStandard\\": "SlevomatCodingStandard" + "SlevomatCodingStandard\\": "SlevomatCodingStandard/" } }, "notification-url": "https://packagist.org/downloads/", @@ -248,7 +248,7 @@ ], "support": { "issues": "https://github.com/slevomat/coding-standard/issues", - "source": "https://github.com/slevomat/coding-standard/tree/8.8.0" + "source": "https://github.com/slevomat/coding-standard/tree/8.10.0" }, "funding": [ { @@ -260,7 +260,7 @@ "type": "tidelift" } ], - "time": "2023-01-09T10:46:13+00:00" + "time": "2023-04-10T07:39:29+00:00" }, { "name": "squizlabs/php_codesniffer", From ddaa09397220a43bdfc9e6c0b30c6ece3e0d9a29 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 24 Apr 2023 01:28:58 +0000 Subject: [PATCH 133/208] Update dependency slevomat/coding-standard to v8.11.0 --- build-cs/composer.lock | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/build-cs/composer.lock b/build-cs/composer.lock index 2eafc159..0cea3f9e 100644 --- a/build-cs/composer.lock +++ b/build-cs/composer.lock @@ -154,16 +154,16 @@ }, { "name": "phpstan/phpdoc-parser", - "version": "1.18.1", + "version": "1.20.2", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "22dcdfd725ddf99583bfe398fc624ad6c5004a0f" + "reference": "90490bd8fd8530a272043c4950c180b6d0cf5f81" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/22dcdfd725ddf99583bfe398fc624ad6c5004a0f", - "reference": "22dcdfd725ddf99583bfe398fc624ad6c5004a0f", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/90490bd8fd8530a272043c4950c180b6d0cf5f81", + "reference": "90490bd8fd8530a272043c4950c180b6d0cf5f81", "shasum": "" }, "require": { @@ -193,38 +193,38 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.18.1" + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.20.2" }, - "time": "2023-04-07T11:51:11+00:00" + "time": "2023-04-22T12:59:35+00:00" }, { "name": "slevomat/coding-standard", - "version": "8.10.0", + "version": "8.11.0", "source": { "type": "git", "url": "https://github.com/slevomat/coding-standard.git", - "reference": "c4e213e6e57f741451a08e68ef838802eec92287" + "reference": "91428d5bcf7db93a842bcf97f465edf62527f3ea" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/c4e213e6e57f741451a08e68ef838802eec92287", - "reference": "c4e213e6e57f741451a08e68ef838802eec92287", + "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/91428d5bcf7db93a842bcf97f465edf62527f3ea", + "reference": "91428d5bcf7db93a842bcf97f465edf62527f3ea", "shasum": "" }, "require": { "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7 || ^1.0", "php": "^7.2 || ^8.0", - "phpstan/phpdoc-parser": ">=1.18.0 <1.19.0", + "phpstan/phpdoc-parser": ">=1.20.0 <1.21.0", "squizlabs/php_codesniffer": "^3.7.1" }, "require-dev": { "phing/phing": "2.17.4", "php-parallel-lint/php-parallel-lint": "1.3.2", - "phpstan/phpstan": "1.4.10|1.10.11", + "phpstan/phpstan": "1.10.14", "phpstan/phpstan-deprecation-rules": "1.1.3", - "phpstan/phpstan-phpunit": "1.0.0|1.3.11", + "phpstan/phpstan-phpunit": "1.3.11", "phpstan/phpstan-strict-rules": "1.5.1", - "phpunit/phpunit": "7.5.20|8.5.21|9.6.6|10.0.19" + "phpunit/phpunit": "7.5.20|8.5.21|9.6.6|10.1.1" }, "type": "phpcodesniffer-standard", "extra": { @@ -248,7 +248,7 @@ ], "support": { "issues": "https://github.com/slevomat/coding-standard/issues", - "source": "https://github.com/slevomat/coding-standard/tree/8.10.0" + "source": "https://github.com/slevomat/coding-standard/tree/8.11.0" }, "funding": [ { @@ -260,7 +260,7 @@ "type": "tidelift" } ], - "time": "2023-04-10T07:39:29+00:00" + "time": "2023-04-21T15:51:44+00:00" }, { "name": "squizlabs/php_codesniffer", From bc67cd94f6c39c07b3427fe06aa7c237d06a704e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 1 May 2023 02:05:48 +0000 Subject: [PATCH 134/208] Update dependency slevomat/coding-standard to v8.11.1 --- build-cs/composer.lock | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/build-cs/composer.lock b/build-cs/composer.lock index 0cea3f9e..c59ffc5f 100644 --- a/build-cs/composer.lock +++ b/build-cs/composer.lock @@ -154,16 +154,16 @@ }, { "name": "phpstan/phpdoc-parser", - "version": "1.20.2", + "version": "1.20.3", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "90490bd8fd8530a272043c4950c180b6d0cf5f81" + "reference": "6c04009f6cae6eda2f040745b6b846080ef069c2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/90490bd8fd8530a272043c4950c180b6d0cf5f81", - "reference": "90490bd8fd8530a272043c4950c180b6d0cf5f81", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/6c04009f6cae6eda2f040745b6b846080ef069c2", + "reference": "6c04009f6cae6eda2f040745b6b846080ef069c2", "shasum": "" }, "require": { @@ -193,22 +193,22 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.20.2" + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.20.3" }, - "time": "2023-04-22T12:59:35+00:00" + "time": "2023-04-25T09:01:03+00:00" }, { "name": "slevomat/coding-standard", - "version": "8.11.0", + "version": "8.11.1", "source": { "type": "git", "url": "https://github.com/slevomat/coding-standard.git", - "reference": "91428d5bcf7db93a842bcf97f465edf62527f3ea" + "reference": "af87461316b257e46e15bb041dca6fca3796d822" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/91428d5bcf7db93a842bcf97f465edf62527f3ea", - "reference": "91428d5bcf7db93a842bcf97f465edf62527f3ea", + "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/af87461316b257e46e15bb041dca6fca3796d822", + "reference": "af87461316b257e46e15bb041dca6fca3796d822", "shasum": "" }, "require": { @@ -248,7 +248,7 @@ ], "support": { "issues": "https://github.com/slevomat/coding-standard/issues", - "source": "https://github.com/slevomat/coding-standard/tree/8.11.0" + "source": "https://github.com/slevomat/coding-standard/tree/8.11.1" }, "funding": [ { @@ -260,7 +260,7 @@ "type": "tidelift" } ], - "time": "2023-04-21T15:51:44+00:00" + "time": "2023-04-24T08:19:01+00:00" }, { "name": "squizlabs/php_codesniffer", From c8493938b6ea628d9e68ec231c36ad1b13587649 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 15 May 2023 00:51:42 +0000 Subject: [PATCH 135/208] Update dependency slevomat/coding-standard to v8.12.0 --- build-cs/composer.lock | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/build-cs/composer.lock b/build-cs/composer.lock index c59ffc5f..db122416 100644 --- a/build-cs/composer.lock +++ b/build-cs/composer.lock @@ -154,16 +154,16 @@ }, { "name": "phpstan/phpdoc-parser", - "version": "1.20.3", + "version": "1.20.4", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "6c04009f6cae6eda2f040745b6b846080ef069c2" + "reference": "7d568c87a9df9c5f7e8b5f075fc469aa8cb0a4cd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/6c04009f6cae6eda2f040745b6b846080ef069c2", - "reference": "6c04009f6cae6eda2f040745b6b846080ef069c2", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/7d568c87a9df9c5f7e8b5f075fc469aa8cb0a4cd", + "reference": "7d568c87a9df9c5f7e8b5f075fc469aa8cb0a4cd", "shasum": "" }, "require": { @@ -193,22 +193,22 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.20.3" + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.20.4" }, - "time": "2023-04-25T09:01:03+00:00" + "time": "2023-05-02T09:19:37+00:00" }, { "name": "slevomat/coding-standard", - "version": "8.11.1", + "version": "8.12.0", "source": { "type": "git", "url": "https://github.com/slevomat/coding-standard.git", - "reference": "af87461316b257e46e15bb041dca6fca3796d822" + "reference": "cc04334ed0ce5a251389112fbd2dbe1dbc931ae8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/af87461316b257e46e15bb041dca6fca3796d822", - "reference": "af87461316b257e46e15bb041dca6fca3796d822", + "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/cc04334ed0ce5a251389112fbd2dbe1dbc931ae8", + "reference": "cc04334ed0ce5a251389112fbd2dbe1dbc931ae8", "shasum": "" }, "require": { @@ -220,11 +220,11 @@ "require-dev": { "phing/phing": "2.17.4", "php-parallel-lint/php-parallel-lint": "1.3.2", - "phpstan/phpstan": "1.10.14", + "phpstan/phpstan": "1.10.15", "phpstan/phpstan-deprecation-rules": "1.1.3", "phpstan/phpstan-phpunit": "1.3.11", "phpstan/phpstan-strict-rules": "1.5.1", - "phpunit/phpunit": "7.5.20|8.5.21|9.6.6|10.1.1" + "phpunit/phpunit": "7.5.20|8.5.21|9.6.8|10.1.3" }, "type": "phpcodesniffer-standard", "extra": { @@ -248,7 +248,7 @@ ], "support": { "issues": "https://github.com/slevomat/coding-standard/issues", - "source": "https://github.com/slevomat/coding-standard/tree/8.11.1" + "source": "https://github.com/slevomat/coding-standard/tree/8.12.0" }, "funding": [ { @@ -260,7 +260,7 @@ "type": "tidelift" } ], - "time": "2023-04-24T08:19:01+00:00" + "time": "2023-05-14T20:06:01+00:00" }, { "name": "squizlabs/php_codesniffer", From 7332b90dfc291ac5b4b83fbca2081936faa1e3f9 Mon Sep 17 00:00:00 2001 From: Nico Haase Date: Fri, 12 May 2023 16:51:50 +0200 Subject: [PATCH 136/208] Extend Serializer stubs --- .../Component/Serializer/Encoder/DecoderInterface.stub | 3 ++- .../Component/Serializer/Encoder/EncoderInterface.stub | 3 ++- .../Serializer/Normalizer/DenormalizerInterface.stub | 3 ++- .../Component/Serializer/Normalizer/NormalizerInterface.stub | 4 +++- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/stubs/Symfony/Component/Serializer/Encoder/DecoderInterface.stub b/stubs/Symfony/Component/Serializer/Encoder/DecoderInterface.stub index a9923edf..e19f772e 100644 --- a/stubs/Symfony/Component/Serializer/Encoder/DecoderInterface.stub +++ b/stubs/Symfony/Component/Serializer/Encoder/DecoderInterface.stub @@ -18,7 +18,8 @@ interface DecoderInterface /** * @param string $format Format name + * @param array $context * @return bool */ - public function supportsDecoding($format); + public function supportsDecoding($format, array $context = []); } diff --git a/stubs/Symfony/Component/Serializer/Encoder/EncoderInterface.stub b/stubs/Symfony/Component/Serializer/Encoder/EncoderInterface.stub index 2d97bde3..11e374eb 100644 --- a/stubs/Symfony/Component/Serializer/Encoder/EncoderInterface.stub +++ b/stubs/Symfony/Component/Serializer/Encoder/EncoderInterface.stub @@ -18,7 +18,8 @@ interface EncoderInterface /** * @param string $format Format name + * @param array $context * @return bool */ - public function supportsEncoding($format); + public function supportsEncoding($format, array $context = []); } diff --git a/stubs/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.stub b/stubs/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.stub index cb9af06d..b7e9968b 100644 --- a/stubs/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.stub +++ b/stubs/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.stub @@ -33,7 +33,8 @@ interface DenormalizerInterface * @param mixed $data * @param string $type * @param string|null $format + * @param array $context * @return bool */ - public function supportsDenormalization($data, $type, $format = null); + public function supportsDenormalization($data, $type, $format = null, array $context = []); } diff --git a/stubs/Symfony/Component/Serializer/Normalizer/NormalizerInterface.stub b/stubs/Symfony/Component/Serializer/Normalizer/NormalizerInterface.stub index fac60042..ba86b6b6 100644 --- a/stubs/Symfony/Component/Serializer/Normalizer/NormalizerInterface.stub +++ b/stubs/Symfony/Component/Serializer/Normalizer/NormalizerInterface.stub @@ -27,7 +27,9 @@ interface NormalizerInterface /** * @param mixed $data * @param string|null $format + * @param array $context + * * @return bool */ - public function supportsNormalization($data, $format = null); + public function supportsNormalization($data, $format = null, array $context = []); } From 8f1dab28569c92d8d3f73d5d5947d3851655759e Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Thu, 25 May 2023 15:27:48 +0200 Subject: [PATCH 137/208] Next-gen coding standard workflow --- .gitattributes | 1 - .github/renovate.json | 5 - .github/workflows/build.yml | 14 +- .gitignore | 1 + Makefile | 10 +- build-cs/.gitignore | 1 - build-cs/composer.json | 13 -- build-cs/composer.lock | 331 ------------------------------------ phpcs.xml | 112 ------------ 9 files changed, 21 insertions(+), 467 deletions(-) delete mode 100644 build-cs/.gitignore delete mode 100644 build-cs/composer.json delete mode 100644 build-cs/composer.lock delete mode 100644 phpcs.xml diff --git a/.gitattributes b/.gitattributes index 184bf6ae..1545ee73 100644 --- a/.gitattributes +++ b/.gitattributes @@ -7,7 +7,6 @@ tmp export-ignore .gitignore export-ignore .travis.yml export-ignore Makefile export-ignore -phpcs.xml export-ignore phpstan.neon export-ignore phpstan-baseline.neon export-ignore phpunit.xml export-ignore diff --git a/.github/renovate.json b/.github/renovate.json index b775cc18..d3f5961e 100644 --- a/.github/renovate.json +++ b/.github/renovate.json @@ -10,11 +10,6 @@ "enabled": true, "groupName": "root-composer" }, - { - "matchPaths": ["build-cs/**"], - "enabled": true, - "groupName": "build-cs" - }, { "matchPaths": [".github/**"], "enabled": true, diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 96765433..ba129bc9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -42,7 +42,7 @@ jobs: - name: "Lint" run: "make lint" - coding-standards: + coding-standard: name: "Coding Standard" runs-on: "ubuntu-latest" @@ -51,11 +51,17 @@ jobs: - name: "Checkout" uses: actions/checkout@v3 + - name: "Checkout build-cs" + uses: actions/checkout@v3 + with: + repository: "phpstan/build-cs" + path: "build-cs" + - name: "Install PHP" uses: "shivammathur/setup-php@v2" with: coverage: "none" - php-version: "8.0" + php-version: "8.2" - name: "Validate Composer" run: "composer validate" @@ -63,6 +69,10 @@ jobs: - name: "Install dependencies" run: "composer install --no-interaction --no-progress" + - name: "Install build-cs dependencies" + working-directory: "build-cs" + run: "composer install --no-interaction --no-progress" + - name: "Lint" run: "make lint" diff --git a/.gitignore b/.gitignore index 2db21315..7de9f3c5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /tests/tmp +/build-cs /vendor /composer.lock .phpunit.result.cache diff --git a/Makefile b/Makefile index b34d0fec..ecd8cfb2 100644 --- a/Makefile +++ b/Makefile @@ -10,13 +10,19 @@ lint: php vendor/bin/parallel-lint --colors \ src tests +.PHONY: cs-install +cs-install: + git clone https://github.com/phpstan/build-cs.git || true + git -C build-cs fetch origin && git -C build-cs reset --hard origin/main + composer install --working-dir build-cs + .PHONY: cs cs: - composer install --working-dir build-cs && php build-cs/vendor/bin/phpcs + php build-cs/vendor/bin/phpcs --standard=build-cs/phpcs.xml src tests .PHONY: cs-fix cs-fix: - php build-cs/vendor/bin/phpcbf + php build-cs/vendor/bin/phpcbf --standard=build-cs/phpcs.xml src tests .PHONY: phpstan phpstan: diff --git a/build-cs/.gitignore b/build-cs/.gitignore deleted file mode 100644 index 61ead866..00000000 --- a/build-cs/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/vendor diff --git a/build-cs/composer.json b/build-cs/composer.json deleted file mode 100644 index 16a240bc..00000000 --- a/build-cs/composer.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "require-dev": { - "consistence-community/coding-standard": "^3.11.0", - "dealerdirect/phpcodesniffer-composer-installer": "^1.0.0", - "slevomat/coding-standard": "^8.8.0", - "squizlabs/php_codesniffer": "^3.5.3" - }, - "config": { - "allow-plugins": { - "dealerdirect/phpcodesniffer-composer-installer": true - } - } -} diff --git a/build-cs/composer.lock b/build-cs/composer.lock deleted file mode 100644 index db122416..00000000 --- a/build-cs/composer.lock +++ /dev/null @@ -1,331 +0,0 @@ -{ - "_readme": [ - "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", - "This file is @generated automatically" - ], - "content-hash": "e69c1916405a7e3c8001c1b609a0ee61", - "packages": [], - "packages-dev": [ - { - "name": "consistence-community/coding-standard", - "version": "3.11.3", - "source": { - "type": "git", - "url": "https://github.com/consistence-community/coding-standard.git", - "reference": "f38e06327d5bf80ff5ff523a2c05e623b5e8d8b1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/consistence-community/coding-standard/zipball/f38e06327d5bf80ff5ff523a2c05e623b5e8d8b1", - "reference": "f38e06327d5bf80ff5ff523a2c05e623b5e8d8b1", - "shasum": "" - }, - "require": { - "php": "~8.0", - "slevomat/coding-standard": "~8.0", - "squizlabs/php_codesniffer": "~3.7.0" - }, - "replace": { - "consistence/coding-standard": "3.10.*" - }, - "require-dev": { - "phing/phing": "2.17.0", - "php-parallel-lint/php-parallel-lint": "1.3.1", - "phpunit/phpunit": "9.5.10" - }, - "type": "library", - "autoload": { - "psr-4": { - "Consistence\\": [ - "Consistence" - ] - }, - "classmap": [ - "Consistence" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Vašek Purchart", - "email": "me@vasekpurchart.cz", - "homepage": "http://vasekpurchart.cz" - } - ], - "description": "Consistence - Coding Standard - PHP Code Sniffer rules", - "keywords": [ - "Coding Standard", - "PHPCodeSniffer", - "codesniffer", - "coding", - "cs", - "phpcs", - "ruleset", - "sniffer", - "standard" - ], - "support": { - "issues": "https://github.com/consistence-community/coding-standard/issues", - "source": "https://github.com/consistence-community/coding-standard/tree/3.11.3" - }, - "time": "2023-03-27T14:55:41+00:00" - }, - { - "name": "dealerdirect/phpcodesniffer-composer-installer", - "version": "v1.0.0", - "source": { - "type": "git", - "url": "https://github.com/PHPCSStandards/composer-installer.git", - "reference": "4be43904336affa5c2f70744a348312336afd0da" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/4be43904336affa5c2f70744a348312336afd0da", - "reference": "4be43904336affa5c2f70744a348312336afd0da", - "shasum": "" - }, - "require": { - "composer-plugin-api": "^1.0 || ^2.0", - "php": ">=5.4", - "squizlabs/php_codesniffer": "^2.0 || ^3.1.0 || ^4.0" - }, - "require-dev": { - "composer/composer": "*", - "ext-json": "*", - "ext-zip": "*", - "php-parallel-lint/php-parallel-lint": "^1.3.1", - "phpcompatibility/php-compatibility": "^9.0", - "yoast/phpunit-polyfills": "^1.0" - }, - "type": "composer-plugin", - "extra": { - "class": "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin" - }, - "autoload": { - "psr-4": { - "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Franck Nijhof", - "email": "franck.nijhof@dealerdirect.com", - "homepage": "http://www.frenck.nl", - "role": "Developer / IT Manager" - }, - { - "name": "Contributors", - "homepage": "https://github.com/PHPCSStandards/composer-installer/graphs/contributors" - } - ], - "description": "PHP_CodeSniffer Standards Composer Installer Plugin", - "homepage": "http://www.dealerdirect.com", - "keywords": [ - "PHPCodeSniffer", - "PHP_CodeSniffer", - "code quality", - "codesniffer", - "composer", - "installer", - "phpcbf", - "phpcs", - "plugin", - "qa", - "quality", - "standard", - "standards", - "style guide", - "stylecheck", - "tests" - ], - "support": { - "issues": "https://github.com/PHPCSStandards/composer-installer/issues", - "source": "https://github.com/PHPCSStandards/composer-installer" - }, - "time": "2023-01-05T11:28:13+00:00" - }, - { - "name": "phpstan/phpdoc-parser", - "version": "1.20.4", - "source": { - "type": "git", - "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "7d568c87a9df9c5f7e8b5f075fc469aa8cb0a4cd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/7d568c87a9df9c5f7e8b5f075fc469aa8cb0a4cd", - "reference": "7d568c87a9df9c5f7e8b5f075fc469aa8cb0a4cd", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "require-dev": { - "php-parallel-lint/php-parallel-lint": "^1.2", - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^1.5", - "phpstan/phpstan-phpunit": "^1.1", - "phpstan/phpstan-strict-rules": "^1.0", - "phpunit/phpunit": "^9.5", - "symfony/process": "^5.2" - }, - "type": "library", - "autoload": { - "psr-4": { - "PHPStan\\PhpDocParser\\": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "PHPDoc parser with support for nullable, intersection and generic types", - "support": { - "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.20.4" - }, - "time": "2023-05-02T09:19:37+00:00" - }, - { - "name": "slevomat/coding-standard", - "version": "8.12.0", - "source": { - "type": "git", - "url": "https://github.com/slevomat/coding-standard.git", - "reference": "cc04334ed0ce5a251389112fbd2dbe1dbc931ae8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/cc04334ed0ce5a251389112fbd2dbe1dbc931ae8", - "reference": "cc04334ed0ce5a251389112fbd2dbe1dbc931ae8", - "shasum": "" - }, - "require": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7 || ^1.0", - "php": "^7.2 || ^8.0", - "phpstan/phpdoc-parser": ">=1.20.0 <1.21.0", - "squizlabs/php_codesniffer": "^3.7.1" - }, - "require-dev": { - "phing/phing": "2.17.4", - "php-parallel-lint/php-parallel-lint": "1.3.2", - "phpstan/phpstan": "1.10.15", - "phpstan/phpstan-deprecation-rules": "1.1.3", - "phpstan/phpstan-phpunit": "1.3.11", - "phpstan/phpstan-strict-rules": "1.5.1", - "phpunit/phpunit": "7.5.20|8.5.21|9.6.8|10.1.3" - }, - "type": "phpcodesniffer-standard", - "extra": { - "branch-alias": { - "dev-master": "8.x-dev" - } - }, - "autoload": { - "psr-4": { - "SlevomatCodingStandard\\": "SlevomatCodingStandard/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.", - "keywords": [ - "dev", - "phpcs" - ], - "support": { - "issues": "https://github.com/slevomat/coding-standard/issues", - "source": "https://github.com/slevomat/coding-standard/tree/8.12.0" - }, - "funding": [ - { - "url": "https://github.com/kukulich", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/slevomat/coding-standard", - "type": "tidelift" - } - ], - "time": "2023-05-14T20:06:01+00:00" - }, - { - "name": "squizlabs/php_codesniffer", - "version": "3.7.2", - "source": { - "type": "git", - "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/ed8e00df0a83aa96acf703f8c2979ff33341f879", - "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879", - "shasum": "" - }, - "require": { - "ext-simplexml": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": ">=5.4.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" - }, - "bin": [ - "bin/phpcs", - "bin/phpcbf" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Greg Sherwood", - "role": "lead" - } - ], - "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", - "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", - "keywords": [ - "phpcs", - "standards", - "static analysis" - ], - "support": { - "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", - "source": "https://github.com/squizlabs/PHP_CodeSniffer", - "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" - }, - "time": "2023-02-22T23:07:41+00:00" - } - ], - "aliases": [], - "minimum-stability": "stable", - "stability-flags": [], - "prefer-stable": false, - "prefer-lowest": false, - "platform": [], - "platform-dev": [], - "plugin-api-version": "2.3.0" -} diff --git a/phpcs.xml b/phpcs.xml deleted file mode 100644 index 3412c956..00000000 --- a/phpcs.xml +++ /dev/null @@ -1,112 +0,0 @@ - - - - - - - - - - src - tests - - - - - - - - - - - - - - - - - - - - - - - - - - - - 10 - - - - - - 10 - - - - - - - - 10 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - tests/*/data - tests/tmp - From 7e7b5bd2e8473d92af0ccd8532d22e83c155da2d Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Thu, 8 Jun 2023 10:18:51 +0200 Subject: [PATCH 138/208] Modernize rules with RuleErrorBuilder --- .../ContainerInterfacePrivateServiceRule.php | 10 +++++----- .../ContainerInterfaceUnknownServiceRule.php | 9 ++++----- .../InvalidArgumentDefaultValueRule.php | 19 ++++++++++++------ .../Symfony/InvalidOptionDefaultValueRule.php | 20 +++++++++++++------ src/Rules/Symfony/UndefinedArgumentRule.php | 7 ++----- src/Rules/Symfony/UndefinedOptionRule.php | 7 ++----- 6 files changed, 40 insertions(+), 32 deletions(-) diff --git a/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php b/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php index 0a3a3d3e..5e10c2b8 100644 --- a/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php +++ b/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php @@ -6,7 +6,7 @@ use PhpParser\Node\Expr\MethodCall; use PHPStan\Analyser\Scope; use PHPStan\Rules\Rule; -use PHPStan\Rules\RuleError; +use PHPStan\Rules\RuleErrorBuilder; use PHPStan\Symfony\ServiceMap; use PHPStan\TrinaryLogic; use PHPStan\Type\ObjectType; @@ -32,9 +32,6 @@ public function getNodeType(): string return MethodCall::class; } - /** - * @return (string|RuleError)[] errors - */ public function processNode(Node $node, Scope $scope): array { if (!$node->name instanceof Node\Identifier) { @@ -72,7 +69,10 @@ public function processNode(Node $node, Scope $scope): array if ($serviceId !== null) { $service = $this->serviceMap->getService($serviceId); if ($service !== null && !$service->isPublic()) { - return [sprintf('Service "%s" is private.', $serviceId)]; + return [ + RuleErrorBuilder::message(sprintf('Service "%s" is private.', $serviceId)) + ->build(), + ]; } } diff --git a/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php b/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php index 417f51f5..ccc1999e 100644 --- a/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php +++ b/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php @@ -7,7 +7,7 @@ use PhpParser\PrettyPrinter\Standard; use PHPStan\Analyser\Scope; use PHPStan\Rules\Rule; -use PHPStan\Rules\RuleError; +use PHPStan\Rules\RuleErrorBuilder; use PHPStan\Symfony\ServiceMap; use PHPStan\Type\ObjectType; use PHPStan\Type\Symfony\Helper; @@ -36,9 +36,6 @@ public function getNodeType(): string return MethodCall::class; } - /** - * @return (string|RuleError)[] errors - */ public function processNode(Node $node, Scope $scope): array { if (!$node->name instanceof Node\Identifier) { @@ -73,7 +70,9 @@ public function processNode(Node $node, Scope $scope): array $service = $this->serviceMap->getService($serviceId); $serviceIdType = $scope->getType($node->getArgs()[0]->value); if ($service === null && !$scope->getType(Helper::createMarkerNode($node->var, $serviceIdType, $this->printer))->equals($serviceIdType)) { - return [sprintf('Service "%s" is not registered in the container.', $serviceId)]; + return [ + RuleErrorBuilder::message(sprintf('Service "%s" is not registered in the container.', $serviceId))->build(), + ]; } } diff --git a/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php b/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php index e48f8718..a55881ce 100644 --- a/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php +++ b/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php @@ -6,7 +6,7 @@ use PhpParser\Node\Expr\MethodCall; use PHPStan\Analyser\Scope; use PHPStan\Rules\Rule; -use PHPStan\Rules\RuleError; +use PHPStan\Rules\RuleErrorBuilder; use PHPStan\Type\ArrayType; use PHPStan\Type\Constant\ConstantIntegerType; use PHPStan\Type\IntegerType; @@ -30,9 +30,6 @@ public function getNodeType(): string return MethodCall::class; } - /** - * @return (string|RuleError)[] errors - */ public function processNode(Node $node, Scope $scope): array { if (!(new ObjectType('Symfony\Component\Console\Command\Command'))->isSuperTypeOf($scope->getType($node->var))->yes()) { @@ -62,12 +59,22 @@ public function processNode(Node $node, Scope $scope): array // not an array if (($mode & 4) !== 4 && !(new UnionType([new StringType(), new NullType()]))->isSuperTypeOf($defaultType)->yes()) { - return [sprintf('Parameter #4 $default of method Symfony\Component\Console\Command\Command::addArgument() expects string|null, %s given.', $defaultType->describe(VerbosityLevel::typeOnly()))]; + return [ + RuleErrorBuilder::message(sprintf( + 'Parameter #4 $default of method Symfony\Component\Console\Command\Command::addArgument() expects string|null, %s given.', + $defaultType->describe(VerbosityLevel::typeOnly()) + ))->build(), + ]; } // is array if (($mode & 4) === 4 && !(new UnionType([new ArrayType(new IntegerType(), new StringType()), new NullType()]))->isSuperTypeOf($defaultType)->yes()) { - return [sprintf('Parameter #4 $default of method Symfony\Component\Console\Command\Command::addArgument() expects array|null, %s given.', $defaultType->describe(VerbosityLevel::typeOnly()))]; + return [ + RuleErrorBuilder::message(sprintf( + 'Parameter #4 $default of method Symfony\Component\Console\Command\Command::addArgument() expects array|null, %s given.', + $defaultType->describe(VerbosityLevel::typeOnly()) + ))->build(), + ]; } return []; diff --git a/src/Rules/Symfony/InvalidOptionDefaultValueRule.php b/src/Rules/Symfony/InvalidOptionDefaultValueRule.php index 171f94f1..6964d25e 100644 --- a/src/Rules/Symfony/InvalidOptionDefaultValueRule.php +++ b/src/Rules/Symfony/InvalidOptionDefaultValueRule.php @@ -6,7 +6,7 @@ use PhpParser\Node\Expr\MethodCall; use PHPStan\Analyser\Scope; use PHPStan\Rules\Rule; -use PHPStan\Rules\RuleError; +use PHPStan\Rules\RuleErrorBuilder; use PHPStan\Type\ArrayType; use PHPStan\Type\BooleanType; use PHPStan\Type\Constant\ConstantIntegerType; @@ -32,9 +32,6 @@ public function getNodeType(): string return MethodCall::class; } - /** - * @return (string|RuleError)[] errors - */ public function processNode(Node $node, Scope $scope): array { if (!(new ObjectType('Symfony\Component\Console\Command\Command'))->isSuperTypeOf($scope->getType($node->var))->yes()) { @@ -66,13 +63,24 @@ public function processNode(Node $node, Scope $scope): array if (($mode & 8) !== 8) { $checkType = new UnionType([new StringType(), new IntegerType(), new NullType(), new BooleanType()]); if (!$checkType->isSuperTypeOf($defaultType)->yes()) { - return [sprintf('Parameter #5 $default of method Symfony\Component\Console\Command\Command::addOption() expects %s, %s given.', $checkType->describe(VerbosityLevel::typeOnly()), $defaultType->describe(VerbosityLevel::typeOnly()))]; + return [ + RuleErrorBuilder::message(sprintf( + 'Parameter #5 $default of method Symfony\Component\Console\Command\Command::addOption() expects %s, %s given.', + $checkType->describe(VerbosityLevel::typeOnly()), + $defaultType->describe(VerbosityLevel::typeOnly()) + ))->build(), + ]; } } // is array if (($mode & 8) === 8 && !(new UnionType([new ArrayType(new MixedType(), new StringType()), new NullType()]))->isSuperTypeOf($defaultType)->yes()) { - return [sprintf('Parameter #5 $default of method Symfony\Component\Console\Command\Command::addOption() expects array|null, %s given.', $defaultType->describe(VerbosityLevel::typeOnly()))]; + return [ + RuleErrorBuilder::message(sprintf( + 'Parameter #5 $default of method Symfony\Component\Console\Command\Command::addOption() expects array|null, %s given.', + $defaultType->describe(VerbosityLevel::typeOnly()) + ))->build(), + ]; } return []; diff --git a/src/Rules/Symfony/UndefinedArgumentRule.php b/src/Rules/Symfony/UndefinedArgumentRule.php index 52f90241..07f35e2a 100644 --- a/src/Rules/Symfony/UndefinedArgumentRule.php +++ b/src/Rules/Symfony/UndefinedArgumentRule.php @@ -8,7 +8,7 @@ use PhpParser\PrettyPrinter\Standard; use PHPStan\Analyser\Scope; use PHPStan\Rules\Rule; -use PHPStan\Rules\RuleError; +use PHPStan\Rules\RuleErrorBuilder; use PHPStan\Symfony\ConsoleApplicationResolver; use PHPStan\Type\ObjectType; use PHPStan\Type\Symfony\Helper; @@ -39,9 +39,6 @@ public function getNodeType(): string return MethodCall::class; } - /** - * @return (string|RuleError)[] errors - */ public function processNode(Node $node, Scope $scope): array { $classReflection = $scope->getClassReflection(); @@ -78,7 +75,7 @@ public function processNode(Node $node, Scope $scope): array if ($scope->getType(Helper::createMarkerNode($node->var, $argType, $this->printer))->equals($argType)) { continue; } - $errors[] = sprintf('Command "%s" does not define argument "%s".', $name, $argName); + $errors[] = RuleErrorBuilder::message(sprintf('Command "%s" does not define argument "%s".', $name, $argName))->build(); } } diff --git a/src/Rules/Symfony/UndefinedOptionRule.php b/src/Rules/Symfony/UndefinedOptionRule.php index 00dd750e..5a8dc8e3 100644 --- a/src/Rules/Symfony/UndefinedOptionRule.php +++ b/src/Rules/Symfony/UndefinedOptionRule.php @@ -8,7 +8,7 @@ use PhpParser\PrettyPrinter\Standard; use PHPStan\Analyser\Scope; use PHPStan\Rules\Rule; -use PHPStan\Rules\RuleError; +use PHPStan\Rules\RuleErrorBuilder; use PHPStan\Symfony\ConsoleApplicationResolver; use PHPStan\Type\ObjectType; use PHPStan\Type\Symfony\Helper; @@ -39,9 +39,6 @@ public function getNodeType(): string return MethodCall::class; } - /** - * @return (string|RuleError)[] errors - */ public function processNode(Node $node, Scope $scope): array { $classReflection = $scope->getClassReflection(); @@ -78,7 +75,7 @@ public function processNode(Node $node, Scope $scope): array if ($scope->getType(Helper::createMarkerNode($node->var, $optType, $this->printer))->equals($optType)) { continue; } - $errors[] = sprintf('Command "%s" does not define option "%s".', $name, $optName); + $errors[] = RuleErrorBuilder::message(sprintf('Command "%s" does not define option "%s".', $name, $optName))->build(); } } From 14e46f20862e3772931aace725d06eb29612ea1a Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Thu, 8 Jun 2023 11:12:16 +0200 Subject: [PATCH 139/208] Bump PHPStan dependencies --- composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 22501953..ca18861b 100644 --- a/composer.json +++ b/composer.json @@ -23,8 +23,8 @@ "require-dev": { "nikic/php-parser": "^4.13.0", "php-parallel-lint/php-parallel-lint": "^1.2", - "phpstan/phpstan-phpunit": "^1.0", - "phpstan/phpstan-strict-rules": "^1.0", + "phpstan/phpstan-phpunit": "^1.3.11", + "phpstan/phpstan-strict-rules": "^1.5.1", "phpunit/phpunit": "^8.5.29 || ^9.5", "psr/container": "1.0 || 1.1.1", "symfony/config": "^5.4 || ^6.1", From 3bc0b957979e7447a907bd190236fcbd90fcb95d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Verlhac?= Date: Wed, 7 Jun 2023 12:47:04 +0200 Subject: [PATCH 140/208] feat: Sync Validor stubs with SF6.1 --- .../Component/Validator/ConstraintViolationInterface.stub | 3 +++ .../Validator/ConstraintViolationListInterface.stub | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/stubs/Symfony/Component/Validator/ConstraintViolationInterface.stub b/stubs/Symfony/Component/Validator/ConstraintViolationInterface.stub index ef3312bb..e7f2b8a2 100644 --- a/stubs/Symfony/Component/Validator/ConstraintViolationInterface.stub +++ b/stubs/Symfony/Component/Validator/ConstraintViolationInterface.stub @@ -2,6 +2,9 @@ namespace Symfony\Component\Validator; +/** + * @method string __toString() Converts the violation into a string for debugging purposes. Not implementing it is deprecated since Symfony 6.1. + */ interface ConstraintViolationInterface { } diff --git a/stubs/Symfony/Component/Validator/ConstraintViolationListInterface.stub b/stubs/Symfony/Component/Validator/ConstraintViolationListInterface.stub index 423bdfb0..00a2a9d5 100644 --- a/stubs/Symfony/Component/Validator/ConstraintViolationListInterface.stub +++ b/stubs/Symfony/Component/Validator/ConstraintViolationListInterface.stub @@ -3,8 +3,11 @@ namespace Symfony\Component\Validator; /** + * @extends \ArrayAccess * @extends \Traversable + * + * @method string __toString() Converts the violation into a string for debugging purposes. Not implementing it is deprecated since Symfony 6.1. */ -interface ConstraintViolationListInterface extends \Traversable +interface ConstraintViolationListInterface extends \Traversable, \Countable, \ArrayAccess { } From b055cab8b0dc01fb462220e3796ace29d9dab27d Mon Sep 17 00:00:00 2001 From: Christophe Coevoet Date: Mon, 28 Aug 2023 17:31:11 +0200 Subject: [PATCH 141/208] Add a more precise return type for ParameterBag::keys in stubs --- stubs/Symfony/Component/HttpFoundation/ParameterBag.stub | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/stubs/Symfony/Component/HttpFoundation/ParameterBag.stub b/stubs/Symfony/Component/HttpFoundation/ParameterBag.stub index 00a40741..0a6858e3 100644 --- a/stubs/Symfony/Component/HttpFoundation/ParameterBag.stub +++ b/stubs/Symfony/Component/HttpFoundation/ParameterBag.stub @@ -7,5 +7,10 @@ namespace Symfony\Component\HttpFoundation; */ class ParameterBag implements \IteratorAggregate { - + /** + * @return list + */ + public function keys(): array + { + } } From 7120655127f68bd78e0b012431707d1260a460f8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 11 Sep 2023 00:15:15 +0000 Subject: [PATCH 142/208] chore(deps): update actions/checkout action to v4 --- .github/workflows/build.yml | 10 +++++----- .github/workflows/create-tag.yml | 2 +- .github/workflows/release.yml | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ba129bc9..06457c85 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -25,7 +25,7 @@ jobs: steps: - name: "Checkout" - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: "Install PHP" uses: "shivammathur/setup-php@v2" @@ -49,10 +49,10 @@ jobs: steps: - name: "Checkout" - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: "Checkout build-cs" - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: "phpstan/build-cs" path: "build-cs" @@ -99,7 +99,7 @@ jobs: steps: - name: "Checkout" - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: "Install PHP" uses: "shivammathur/setup-php@v2" @@ -138,7 +138,7 @@ jobs: steps: - name: "Checkout" - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: "Install PHP" uses: "shivammathur/setup-php@v2" diff --git a/.github/workflows/create-tag.yml b/.github/workflows/create-tag.yml index 8452d986..a8535014 100644 --- a/.github/workflows/create-tag.yml +++ b/.github/workflows/create-tag.yml @@ -21,7 +21,7 @@ jobs: runs-on: "ubuntu-latest" steps: - name: "Checkout" - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 token: ${{ secrets.PHPSTAN_BOT_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 92b72547..e4a8ac62 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,7 +14,7 @@ jobs: steps: - name: "Checkout" - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Generate changelog id: changelog From abc7682290c4a1acf3425566616f2fe42c5fa8d0 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Thu, 28 Sep 2023 14:20:16 +0200 Subject: [PATCH 143/208] Open 1.3.x-dev --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 06457c85..58ae8be8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,7 +6,7 @@ on: pull_request: push: branches: - - "1.2.x" + - "1.3.x" jobs: lint: From 67cf72e7a1f4c7563ee5c17eb5b7fe1f29ac5088 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Thu, 28 Sep 2023 14:21:03 +0200 Subject: [PATCH 144/208] Open 1.4.x-dev --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 58ae8be8..b4453157 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,7 +6,7 @@ on: pull_request: push: branches: - - "1.3.x" + - "1.4.x" jobs: lint: From 6a0cefe070ce1dc70561a37d4181cf8f21aee94e Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Thu, 8 Jun 2023 10:18:44 +0200 Subject: [PATCH 145/208] Require PHPStan 1.11 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index ca18861b..fc3e9368 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ "require": { "php": "^7.2 || ^8.0", "ext-simplexml": "*", - "phpstan/phpstan": "^1.9.18" + "phpstan/phpstan": "^1.11" }, "conflict": { "symfony/framework-bundle": "<3.0" From ce3c25807d9713897605b420c6830d6a86cf56b5 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Thu, 8 Jun 2023 11:07:26 +0200 Subject: [PATCH 146/208] Error identifiers --- src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php | 1 + src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php | 4 +++- src/Rules/Symfony/InvalidArgumentDefaultValueRule.php | 4 ++-- src/Rules/Symfony/InvalidOptionDefaultValueRule.php | 4 ++-- src/Rules/Symfony/UndefinedArgumentRule.php | 4 +++- src/Rules/Symfony/UndefinedOptionRule.php | 4 +++- 6 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php b/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php index 5e10c2b8..996d3b77 100644 --- a/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php +++ b/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php @@ -71,6 +71,7 @@ public function processNode(Node $node, Scope $scope): array if ($service !== null && !$service->isPublic()) { return [ RuleErrorBuilder::message(sprintf('Service "%s" is private.', $serviceId)) + ->identifier('symfonyContainer.privateService') ->build(), ]; } diff --git a/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php b/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php index ccc1999e..fc7d9585 100644 --- a/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php +++ b/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php @@ -71,7 +71,9 @@ public function processNode(Node $node, Scope $scope): array $serviceIdType = $scope->getType($node->getArgs()[0]->value); if ($service === null && !$scope->getType(Helper::createMarkerNode($node->var, $serviceIdType, $this->printer))->equals($serviceIdType)) { return [ - RuleErrorBuilder::message(sprintf('Service "%s" is not registered in the container.', $serviceId))->build(), + RuleErrorBuilder::message(sprintf('Service "%s" is not registered in the container.', $serviceId)) + ->identifier('symfonyContainer.serviceNotFound') + ->build(), ]; } } diff --git a/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php b/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php index a55881ce..153d4af5 100644 --- a/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php +++ b/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php @@ -63,7 +63,7 @@ public function processNode(Node $node, Scope $scope): array RuleErrorBuilder::message(sprintf( 'Parameter #4 $default of method Symfony\Component\Console\Command\Command::addArgument() expects string|null, %s given.', $defaultType->describe(VerbosityLevel::typeOnly()) - ))->build(), + ))->identifier('argument.type')->build(), ]; } @@ -73,7 +73,7 @@ public function processNode(Node $node, Scope $scope): array RuleErrorBuilder::message(sprintf( 'Parameter #4 $default of method Symfony\Component\Console\Command\Command::addArgument() expects array|null, %s given.', $defaultType->describe(VerbosityLevel::typeOnly()) - ))->build(), + ))->identifier('argument.type')->build(), ]; } diff --git a/src/Rules/Symfony/InvalidOptionDefaultValueRule.php b/src/Rules/Symfony/InvalidOptionDefaultValueRule.php index 6964d25e..1595f4a8 100644 --- a/src/Rules/Symfony/InvalidOptionDefaultValueRule.php +++ b/src/Rules/Symfony/InvalidOptionDefaultValueRule.php @@ -68,7 +68,7 @@ public function processNode(Node $node, Scope $scope): array 'Parameter #5 $default of method Symfony\Component\Console\Command\Command::addOption() expects %s, %s given.', $checkType->describe(VerbosityLevel::typeOnly()), $defaultType->describe(VerbosityLevel::typeOnly()) - ))->build(), + ))->identifier('argument.type')->build(), ]; } } @@ -79,7 +79,7 @@ public function processNode(Node $node, Scope $scope): array RuleErrorBuilder::message(sprintf( 'Parameter #5 $default of method Symfony\Component\Console\Command\Command::addOption() expects array|null, %s given.', $defaultType->describe(VerbosityLevel::typeOnly()) - ))->build(), + ))->identifier('argument.type')->build(), ]; } diff --git a/src/Rules/Symfony/UndefinedArgumentRule.php b/src/Rules/Symfony/UndefinedArgumentRule.php index 07f35e2a..cd8be894 100644 --- a/src/Rules/Symfony/UndefinedArgumentRule.php +++ b/src/Rules/Symfony/UndefinedArgumentRule.php @@ -75,7 +75,9 @@ public function processNode(Node $node, Scope $scope): array if ($scope->getType(Helper::createMarkerNode($node->var, $argType, $this->printer))->equals($argType)) { continue; } - $errors[] = RuleErrorBuilder::message(sprintf('Command "%s" does not define argument "%s".', $name, $argName))->build(); + $errors[] = RuleErrorBuilder::message(sprintf('Command "%s" does not define argument "%s".', $name, $argName)) + ->identifier('symfonyConsole.argumentNotFound') + ->build(); } } diff --git a/src/Rules/Symfony/UndefinedOptionRule.php b/src/Rules/Symfony/UndefinedOptionRule.php index 5a8dc8e3..095bd44d 100644 --- a/src/Rules/Symfony/UndefinedOptionRule.php +++ b/src/Rules/Symfony/UndefinedOptionRule.php @@ -75,7 +75,9 @@ public function processNode(Node $node, Scope $scope): array if ($scope->getType(Helper::createMarkerNode($node->var, $optType, $this->printer))->equals($optType)) { continue; } - $errors[] = RuleErrorBuilder::message(sprintf('Command "%s" does not define option "%s".', $name, $optName))->build(); + $errors[] = RuleErrorBuilder::message(sprintf('Command "%s" does not define option "%s".', $name, $optName)) + ->identifier('symfonyConsole.optionNotFound') + ->build(); } } From 383855999db6a7d65d0bf580ce2762e17188c2a5 Mon Sep 17 00:00:00 2001 From: raalderink Date: Fri, 21 Apr 2023 13:37:56 +0200 Subject: [PATCH 147/208] Set properties autowired with @required as initialized --- composer.json | 5 +- extension.neon | 7 ++ phpstan-baseline.neon | 10 ++ src/Symfony/RequiredAutowiringExtension.php | 91 +++++++++++++++++++ .../RequiredAutowiringExtensionTest.php | 65 +++++++++++++ tests/Symfony/data/required-annotations.php | 38 ++++++++ tests/Symfony/data/required-attributes.php | 38 ++++++++ tests/Symfony/required-autowiring-config.neon | 6 ++ 8 files changed, 258 insertions(+), 2 deletions(-) create mode 100644 src/Symfony/RequiredAutowiringExtension.php create mode 100644 tests/Symfony/RequiredAutowiringExtensionTest.php create mode 100644 tests/Symfony/data/required-annotations.php create mode 100644 tests/Symfony/data/required-attributes.php create mode 100644 tests/Symfony/required-autowiring-config.neon diff --git a/composer.json b/composer.json index ca18861b..b8f33962 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ "require": { "php": "^7.2 || ^8.0", "ext-simplexml": "*", - "phpstan/phpstan": "^1.9.18" + "phpstan/phpstan": "^1.10.36" }, "conflict": { "symfony/framework-bundle": "<3.0" @@ -35,7 +35,8 @@ "symfony/http-foundation": "^5.4 || ^6.1", "symfony/messenger": "^5.4", "symfony/polyfill-php80": "^1.24", - "symfony/serializer": "^5.4" + "symfony/serializer": "^5.4", + "symfony/service-contracts": "^2.2.0" }, "config": { "sort-packages": true diff --git a/extension.neon b/extension.neon index 40165b39..fdfbfdcf 100644 --- a/extension.neon +++ b/extension.neon @@ -329,3 +329,10 @@ services: - factory: PHPStan\Type\Symfony\InputBagTypeSpecifyingExtension tags: [phpstan.typeSpecifier.methodTypeSpecifyingExtension] + + # Additional constructors and initialization checks for @required autowiring + - + class: PHPStan\Symfony\RequiredAutowiringExtension + tags: + - phpstan.properties.readWriteExtension + - phpstan.additionalConstructorsExtension diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 4abdaa0a..0f6edd5c 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,5 +1,10 @@ parameters: ignoreErrors: + - + message: "#^Although PHPStan\\\\Reflection\\\\Php\\\\PhpPropertyReflection is covered by backward compatibility promise, this instanceof assumption might break because it's not guaranteed to always stay the same\\.$#" + count: 1 + path: src/Symfony/RequiredAutowiringExtension.php + - message: "#^Call to function method_exists\\(\\) with Symfony\\\\Component\\\\Console\\\\Input\\\\InputOption and 'isNegatable' will always evaluate to true\\.$#" count: 1 @@ -10,6 +15,11 @@ parameters: count: 1 path: tests/Rules/NonexistentInputBagClassTest.php + - + message: "#^Accessing PHPStan\\\\Rules\\\\Properties\\\\UninitializedPropertyRule\\:\\:class is not covered by backward compatibility promise\\. The class might change in a minor PHPStan version\\.$#" + count: 1 + path: tests/Symfony/RequiredAutowiringExtensionTest.php + - message: "#^Accessing PHPStan\\\\Rules\\\\Comparison\\\\ImpossibleCheckTypeMethodCallRule\\:\\:class is not covered by backward compatibility promise\\. The class might change in a minor PHPStan version\\.$#" count: 1 diff --git a/src/Symfony/RequiredAutowiringExtension.php b/src/Symfony/RequiredAutowiringExtension.php new file mode 100644 index 00000000..a3cc6a31 --- /dev/null +++ b/src/Symfony/RequiredAutowiringExtension.php @@ -0,0 +1,91 @@ +fileTypeMapper = $fileTypeMapper; + } + + public function isAlwaysRead(PropertyReflection $property, string $propertyName): bool + { + return false; + } + + public function isAlwaysWritten(PropertyReflection $property, string $propertyName): bool + { + return false; + } + + public function isInitialized(PropertyReflection $property, string $propertyName): bool + { + // If the property is public, check for @required on the property itself + if (!$property->isPublic()) { + return false; + } + + if ($property->getDocComment() !== null && $this->isRequiredFromDocComment($property->getDocComment())) { + return true; + } + + // Check for the attribute version + if ($property instanceof PhpPropertyReflection && count($property->getNativeReflection()->getAttributes('Symfony\Contracts\Service\Attribute\Required')) > 0) { + return true; + } + + return false; + } + + public function getAdditionalConstructors(ClassReflection $classReflection): array + { + $additionalConstructors = []; + $nativeReflection = $classReflection->getNativeReflection(); + + foreach ($nativeReflection->getMethods() as $method) { + if (!$method->isPublic()) { + continue; + } + + if ($method->getDocComment() !== false && $this->isRequiredFromDocComment($method->getDocComment())) { + $additionalConstructors[] = $method->getName(); + } + + if (count($method->getAttributes('Symfony\Contracts\Service\Attribute\Required')) === 0) { + continue; + } + + $additionalConstructors[] = $method->getName(); + } + + return $additionalConstructors; + } + + private function isRequiredFromDocComment(string $docComment): bool + { + $phpDoc = $this->fileTypeMapper->getResolvedPhpDoc(null, null, null, null, $docComment); + + foreach ($phpDoc->getPhpDocNodes() as $node) { + // @required tag is available, meaning this property is always initialized + if (count($node->getTagsByName('@required')) > 0) { + return true; + } + } + + return false; + } + +} diff --git a/tests/Symfony/RequiredAutowiringExtensionTest.php b/tests/Symfony/RequiredAutowiringExtensionTest.php new file mode 100644 index 00000000..93fb3822 --- /dev/null +++ b/tests/Symfony/RequiredAutowiringExtensionTest.php @@ -0,0 +1,65 @@ + + */ +final class RequiredAutowiringExtensionTest extends RuleTestCase +{ + + protected function getRule(): Rule + { + $container = self::getContainer(); + $container->getServicesByTag(AdditionalConstructorsExtension::EXTENSION_TAG); + + return $container->getByType(UninitializedPropertyRule::class); + } + + public function testRequiredAnnotations(): void + { + $this->analyse([__DIR__ . '/data/required-annotations.php'], [ + [ + 'Class RequiredAnnotationTest\TestAnnotations has an uninitialized property $three. Give it default value or assign it in the constructor.', + 12, + ], + [ + 'Class RequiredAnnotationTest\TestAnnotations has an uninitialized property $four. Give it default value or assign it in the constructor.', + 14, + ], + ]); + } + + public function testRequiredAttributes(): void + { + if (!class_exists(Required::class)) { + self::markTestSkipped('Required symfony/service-contracts@3.2.1 or higher is not installed'); + } + + $this->analyse([__DIR__ . '/data/required-attributes.php'], [ + [ + 'Class RequiredAttributesTest\TestAttributes has an uninitialized property $three. Give it default value or assign it in the constructor.', + 14, + ], + [ + 'Class RequiredAttributesTest\TestAttributes has an uninitialized property $four. Give it default value or assign it in the constructor.', + 16, + ], + ]); + } + + public static function getAdditionalConfigFiles(): array + { + return [ + __DIR__ . '/required-autowiring-config.neon', + ]; + } + +} diff --git a/tests/Symfony/data/required-annotations.php b/tests/Symfony/data/required-annotations.php new file mode 100644 index 00000000..e2085ab3 --- /dev/null +++ b/tests/Symfony/data/required-annotations.php @@ -0,0 +1,38 @@ += 7.4 + +namespace RequiredAnnotationTest; + +class TestAnnotations +{ + /** @required */ + public string $one; + + private string $two; + + public string $three; + + private string $four; + + /** + * @required + */ + public function setTwo(int $two): void + { + $this->two = $two; + } + + public function getTwo(): int + { + return $this->two; + } + + public function setFour(int $four): void + { + $this->four = $four; + } + + public function getFour(): int + { + return $this->four; + } +} diff --git a/tests/Symfony/data/required-attributes.php b/tests/Symfony/data/required-attributes.php new file mode 100644 index 00000000..d847d276 --- /dev/null +++ b/tests/Symfony/data/required-attributes.php @@ -0,0 +1,38 @@ += 8.0 + +namespace RequiredAttributesTest; + +use Symfony\Contracts\Service\Attribute\Required; + +class TestAttributes +{ + #[Required] + public string $one; + + private string $two; + + public string $three; + + private string $four; + + #[Required] + public function setTwo(int $two): void + { + $this->two = $two; + } + + public function getTwo(): int + { + return $this->two; + } + + public function setFour(int $four): void + { + $this->four = $four; + } + + public function getFour(): int + { + return $this->four; + } +} diff --git a/tests/Symfony/required-autowiring-config.neon b/tests/Symfony/required-autowiring-config.neon new file mode 100644 index 00000000..3ff4183c --- /dev/null +++ b/tests/Symfony/required-autowiring-config.neon @@ -0,0 +1,6 @@ +services: + - + class: PHPStan\Symfony\RequiredAutowiringExtension + tags: + - phpstan.properties.readWriteExtension + - phpstan.additionalConstructorsExtension From 27ff6339f83796a7e0dd963cf445cd3c456fc620 Mon Sep 17 00:00:00 2001 From: Vladislav Nikolayev Date: Mon, 30 Oct 2023 16:52:15 +0200 Subject: [PATCH 148/208] Fix stub of `Voter::supports()` method --- .../Component/Security/Core/Authorization/Voter/Voter.stub | 1 + 1 file changed, 1 insertion(+) diff --git a/stubs/Symfony/Component/Security/Core/Authorization/Voter/Voter.stub b/stubs/Symfony/Component/Security/Core/Authorization/Voter/Voter.stub index 17485a28..c2daae3b 100644 --- a/stubs/Symfony/Component/Security/Core/Authorization/Voter/Voter.stub +++ b/stubs/Symfony/Component/Security/Core/Authorization/Voter/Voter.stub @@ -13,6 +13,7 @@ abstract class Voter implements VoterInterface /** * Determines if the attribute and subject are supported by this voter. * + * @param string $attribute * @param mixed $subject * * @phpstan-assert-if-true TSubject $subject From 44b68dda25f6a32f5628e7ae3c01a8ffe17171ec Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 20 Nov 2023 01:05:39 +0000 Subject: [PATCH 149/208] chore(deps): update dessant/lock-threads action to v5 --- .github/workflows/lock-closed-issues.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lock-closed-issues.yml b/.github/workflows/lock-closed-issues.yml index 4c7990df..c2b017b9 100644 --- a/.github/workflows/lock-closed-issues.yml +++ b/.github/workflows/lock-closed-issues.yml @@ -8,7 +8,7 @@ jobs: lock: runs-on: ubuntu-latest steps: - - uses: dessant/lock-threads@v4 + - uses: dessant/lock-threads@v5 with: github-token: ${{ github.token }} issue-inactive-days: '31' From 4cc4788b6692b34948385a7638b324b1bc35377a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 20 Nov 2023 01:05:35 +0000 Subject: [PATCH 150/208] chore(deps): update metcalfc/changelog-generator action to v4.2.0 --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e4a8ac62..2fb750a4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,7 +18,7 @@ jobs: - name: Generate changelog id: changelog - uses: metcalfc/changelog-generator@v4.1.0 + uses: metcalfc/changelog-generator@v4.2.0 with: myToken: ${{ secrets.PHPSTAN_BOT_TOKEN }} From 34b3c43684834f6a20aa51af8d455480d9de8b88 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Fri, 22 Dec 2023 12:22:34 +0100 Subject: [PATCH 151/208] Fix `CacheInterface::get()` return type --- extension.neon | 5 ++ ...InterfaceGetDynamicReturnTypeExtension.php | 48 +++++++++++++++++++ tests/Type/Symfony/data/cache.php | 20 ++++++++ 3 files changed, 73 insertions(+) create mode 100644 src/Type/Symfony/CacheInterfaceGetDynamicReturnTypeExtension.php diff --git a/extension.neon b/extension.neon index fdfbfdcf..af69d8bf 100644 --- a/extension.neon +++ b/extension.neon @@ -336,3 +336,8 @@ services: tags: - phpstan.properties.readWriteExtension - phpstan.additionalConstructorsExtension + + # CacheInterface::get() return type + - + factory: PHPStan\Type\Symfony\CacheInterfaceGetDynamicReturnTypeExtension + tags: [phpstan.broker.dynamicMethodReturnTypeExtension] diff --git a/src/Type/Symfony/CacheInterfaceGetDynamicReturnTypeExtension.php b/src/Type/Symfony/CacheInterfaceGetDynamicReturnTypeExtension.php new file mode 100644 index 00000000..ce736bfe --- /dev/null +++ b/src/Type/Symfony/CacheInterfaceGetDynamicReturnTypeExtension.php @@ -0,0 +1,48 @@ +getName() === 'get'; + } + + public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): ?Type + { + if (!isset($methodCall->getArgs()[1])) { + return null; + } + + $callbackReturnType = $scope->getType($methodCall->getArgs()[1]->value); + if ($callbackReturnType->isCallable()->yes()) { + $parametersAcceptor = ParametersAcceptorSelector::selectFromArgs( + $scope, + $methodCall->getArgs(), + $callbackReturnType->getCallableParametersAcceptors($scope) + ); + $returnType = $parametersAcceptor->getReturnType(); + + // generalize template parameters + return $returnType->generalize(GeneralizePrecision::templateArgument()); + } + + return null; + } + +} diff --git a/tests/Type/Symfony/data/cache.php b/tests/Type/Symfony/data/cache.php index 6b0728d4..a8862177 100644 --- a/tests/Type/Symfony/data/cache.php +++ b/tests/Type/Symfony/data/cache.php @@ -12,6 +12,26 @@ function testCacheCallable(\Symfony\Contracts\Cache\CacheInterface $cache): voi assertType('string', $result); }; +/** + * @param callable():string $fn + */ +function testNonScalarCacheCallable(\Symfony\Contracts\Cache\CacheInterface $cache, callable $fn): void { + $result = $cache->get('foo', $fn); + + assertType('string', $result); +}; + + +/** + * @param callable():non-empty-string $fn + */ +function testCacheCallableReturnTypeGeneralization(\Symfony\Contracts\Cache\CacheInterface $cache, callable $fn): void { + $result = $cache->get('foo', $fn); + + assertType('string', $result); +}; + + /** * @param \Symfony\Contracts\Cache\CallbackInterface<\stdClass> $cb */ From ef7db637be9b85fa00278fc3477ac66abe8eb7d1 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Wed, 10 Jan 2024 11:46:57 +0100 Subject: [PATCH 152/208] Infer type for builder getData --- extension.neon | 5 +++++ stubs/Symfony/Component/Form/AbstractType.stub | 1 + .../Component/Form/FormBuilderInterface.stub | 12 ++++++++++-- .../Form/FormConfigBuilderInterface.stub | 13 +++++++++++++ .../Component/Form/FormConfigInterface.stub | 16 ++++++++++++++++ .../Form/FormTypeExtensionInterface.stub | 1 + .../Component/Form/FormTypeInterface.stub | 1 + tests/Type/Symfony/data/form_data_type.php | 3 +++ 8 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 stubs/Symfony/Component/Form/FormConfigBuilderInterface.stub create mode 100644 stubs/Symfony/Component/Form/FormConfigInterface.stub diff --git a/extension.neon b/extension.neon index af69d8bf..dec41999 100644 --- a/extension.neon +++ b/extension.neon @@ -14,6 +14,9 @@ parameters: featureToggles: skipCheckGenericClasses: - Symfony\Component\Form\AbstractType + - Symfony\Component\Form\FormBuilderInterface + - Symfony\Component\Form\FormConfigBuilderInterface + - Symfony\Component\Form\FormConfigInterface - Symfony\Component\Form\FormInterface - Symfony\Component\Form\FormTypeExtensionInterface - Symfony\Component\Form\FormTypeInterface @@ -47,6 +50,8 @@ parameters: - 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 - stubs/Symfony/Component/Form/FormInterface.stub - stubs/Symfony/Component/Form/FormFactoryInterface.stub - stubs/Symfony/Component/Form/FormTypeExtensionInterface.stub diff --git a/stubs/Symfony/Component/Form/AbstractType.stub b/stubs/Symfony/Component/Form/AbstractType.stub index da2b1439..e99b746c 100644 --- a/stubs/Symfony/Component/Form/AbstractType.stub +++ b/stubs/Symfony/Component/Form/AbstractType.stub @@ -11,6 +11,7 @@ abstract class AbstractType implements FormTypeInterface { /** + * @param FormBuilderInterface $builder * @param array $options */ public function buildForm(FormBuilderInterface $builder, array $options): void; diff --git a/stubs/Symfony/Component/Form/FormBuilderInterface.stub b/stubs/Symfony/Component/Form/FormBuilderInterface.stub index 5173bb64..fe578c50 100644 --- a/stubs/Symfony/Component/Form/FormBuilderInterface.stub +++ b/stubs/Symfony/Component/Form/FormBuilderInterface.stub @@ -3,9 +3,17 @@ namespace Symfony\Component\Form; /** - * @extends \Traversable + * @template TData + * + * @extends \Traversable> + * @extends FormConfigBuilderInterface */ -interface FormBuilderInterface extends \Traversable +interface FormBuilderInterface extends \Traversable, \Countable, FormConfigBuilderInterface { + /** + * @return FormInterface + */ + public function getForm(): FormInterface; + } diff --git a/stubs/Symfony/Component/Form/FormConfigBuilderInterface.stub b/stubs/Symfony/Component/Form/FormConfigBuilderInterface.stub new file mode 100644 index 00000000..a167ce43 --- /dev/null +++ b/stubs/Symfony/Component/Form/FormConfigBuilderInterface.stub @@ -0,0 +1,13 @@ + + */ +interface FormConfigBuilderInterface extends FormConfigInterface +{ + +} diff --git a/stubs/Symfony/Component/Form/FormConfigInterface.stub b/stubs/Symfony/Component/Form/FormConfigInterface.stub new file mode 100644 index 00000000..942d467b --- /dev/null +++ b/stubs/Symfony/Component/Form/FormConfigInterface.stub @@ -0,0 +1,16 @@ + $builder * @param array $options */ public function buildForm(FormBuilderInterface $builder, array $options): void; diff --git a/stubs/Symfony/Component/Form/FormTypeInterface.stub b/stubs/Symfony/Component/Form/FormTypeInterface.stub index 2f745283..8536656a 100644 --- a/stubs/Symfony/Component/Form/FormTypeInterface.stub +++ b/stubs/Symfony/Component/Form/FormTypeInterface.stub @@ -8,6 +8,7 @@ namespace Symfony\Component\Form; interface FormTypeInterface { /** + * @param FormBuilderInterface $builder * @param array $options */ public function buildForm(FormBuilderInterface $builder, array $options): void; diff --git a/tests/Type/Symfony/data/form_data_type.php b/tests/Type/Symfony/data/form_data_type.php index 524a5b7c..e0cefb88 100644 --- a/tests/Type/Symfony/data/form_data_type.php +++ b/tests/Type/Symfony/data/form_data_type.php @@ -29,6 +29,9 @@ class DataClassType extends AbstractType public function buildForm(FormBuilderInterface $builder, array $options): void { + assertType('GenericFormDataType\DataClass|null', $builder->getData()); + assertType('GenericFormDataType\DataClass|null', $builder->getForm()->getData()); + $builder ->add('foo', NumberType::class) ->add('bar', TextType::class) From b2ddd0ffa7631afed5662f3e450320cd630257d6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 5 Feb 2024 00:26:02 +0000 Subject: [PATCH 153/208] chore(deps): update cbrgm/mastodon-github-action action to v2 --- .github/workflows/release-toot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-toot.yml b/.github/workflows/release-toot.yml index 6a1c8156..1ba4fd77 100644 --- a/.github/workflows/release-toot.yml +++ b/.github/workflows/release-toot.yml @@ -10,7 +10,7 @@ jobs: toot: runs-on: ubuntu-latest steps: - - uses: cbrgm/mastodon-github-action@v1 + - uses: cbrgm/mastodon-github-action@v2 if: ${{ !github.event.repository.private }} with: # GitHub event payload From a0e0f1f832d509f86eaf388a1881de664455b170 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 16 Feb 2024 22:38:05 +0000 Subject: [PATCH 154/208] chore(deps): update metcalfc/changelog-generator action to v4.3.1 --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2fb750a4..b1a669a9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,7 +18,7 @@ jobs: - name: Generate changelog id: changelog - uses: metcalfc/changelog-generator@v4.2.0 + uses: metcalfc/changelog-generator@v4.3.1 with: myToken: ${{ secrets.PHPSTAN_BOT_TOKEN }} From 67e208de049211ef1b0598be4576cb24729b20a9 Mon Sep 17 00:00:00 2001 From: Emanuele Panzeri Date: Fri, 23 Feb 2024 16:08:10 +0100 Subject: [PATCH 155/208] Add documentation to load DI helper --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 78f02320..25a155c1 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,10 @@ parameters: # If you're using PHP config files for Symfony 5.3+, you also need this for auto-loading of `Symfony\Config`: scanDirectories: - var/cache/dev/Symfony/Config + # If you're using PHP config files (including the ones under packages/*.php) for Symfony 5.3+, + # you need this to load the helper functions (i.e. service(), env()): + scanFiles: + - vendor/symfony/dependency-injection/Loader/Configurator/ContainerConfigurator.php ``` ## Constant hassers From d8a0bc03a68d95288b6471c37d435647fbdaff1a Mon Sep 17 00:00:00 2001 From: Robert Meijers Date: Thu, 29 Feb 2024 14:44:49 +0100 Subject: [PATCH 156/208] Narrow type for Extension::getConfiguration if class exists The base extension class automatically creates a Configuration instance when a Configuration class exists in the namespace of the extension. But PHPStan obviously doesn't understand this behaviour and always assumes that `getConfiguration()` returns `ConfigurationInterface|null` meaning that the default pattern to get and parse the configuration reports an error. I.e.: ```php namespace Foo; class SomeExtension extends Extension { public function load(array $configs, ContainerBuilder $container): void { $configuration = $this->getConfiguration($configs, $container); $config = $this->processConfiguration($configuration, $configs); } } ``` results in an error because `processConfiguration()` doesn't accept `ConfigurationInterface|null`. But when a `Configuration` class exists in the same namespace as the `Extension` class (so `Foo\Extension`) an instance of it is returned. This `DynamicReturnTypeExtension` overrides the return type of `Extension::getConfiguration()` so it automatically narrows the return type in case `getConfiguration()` is not overriden and a `Configuration` class exists. So that in the given example `getConfiguration()` doesn't return `ConfigurationInterface|null` anymore but `Foo\Configuration` and there is no error on calling `processConfiguration()`. --- extension.neon | 5 + ...ionGetConfigurationReturnTypeExtension.php | 105 ++++++++++++++++++ tests/Type/Symfony/ExtensionTest.php | 9 ++ .../anonymous/AnonymousExtension.php | 17 +++ .../IgnoreImplementedExtension.php | 23 ++++ .../multiple-types/MultipleTypes.php | 18 +++ .../Configuration.php | 16 +++ ...WithConstructorOptionalParamsExtension.php | 17 +++ .../Configuration.php | 16 +++ ...WithConstructorRequiredParamsExtension.php | 17 +++ .../Configuration.php | 16 +++ ...hConfigurationWithConstructorExtension.php | 17 +++ .../with-configuration/Configuration.php | 12 ++ .../WithConfigurationExtension.php | 17 +++ .../WithoutConfigurationExtension.php | 17 +++ 15 files changed, 322 insertions(+) create mode 100644 src/Type/Symfony/ExtensionGetConfigurationReturnTypeExtension.php create mode 100644 tests/Type/Symfony/data/extension/anonymous/AnonymousExtension.php create mode 100644 tests/Type/Symfony/data/extension/ignore-implemented/IgnoreImplementedExtension.php create mode 100644 tests/Type/Symfony/data/extension/multiple-types/MultipleTypes.php create mode 100644 tests/Type/Symfony/data/extension/with-configuration-with-constructor-optional-params/Configuration.php create mode 100644 tests/Type/Symfony/data/extension/with-configuration-with-constructor-optional-params/WithConfigurationWithConstructorOptionalParamsExtension.php create mode 100644 tests/Type/Symfony/data/extension/with-configuration-with-constructor-required-params/Configuration.php create mode 100644 tests/Type/Symfony/data/extension/with-configuration-with-constructor-required-params/WithConfigurationWithConstructorRequiredParamsExtension.php create mode 100644 tests/Type/Symfony/data/extension/with-configuration-with-constructor/Configuration.php create mode 100644 tests/Type/Symfony/data/extension/with-configuration-with-constructor/WithConfigurationWithConstructorExtension.php create mode 100644 tests/Type/Symfony/data/extension/with-configuration/Configuration.php create mode 100644 tests/Type/Symfony/data/extension/with-configuration/WithConfigurationExtension.php create mode 100644 tests/Type/Symfony/data/extension/without-configuration/WithoutConfigurationExtension.php diff --git a/extension.neon b/extension.neon index dec41999..531eb7df 100644 --- a/extension.neon +++ b/extension.neon @@ -346,3 +346,8 @@ services: - factory: PHPStan\Type\Symfony\CacheInterfaceGetDynamicReturnTypeExtension tags: [phpstan.broker.dynamicMethodReturnTypeExtension] + + # Extension::getConfiguration() return type + - + factory: PHPStan\Type\Symfony\ExtensionGetConfigurationReturnTypeExtension + tags: [phpstan.broker.dynamicMethodReturnTypeExtension] diff --git a/src/Type/Symfony/ExtensionGetConfigurationReturnTypeExtension.php b/src/Type/Symfony/ExtensionGetConfigurationReturnTypeExtension.php new file mode 100644 index 00000000..7af50773 --- /dev/null +++ b/src/Type/Symfony/ExtensionGetConfigurationReturnTypeExtension.php @@ -0,0 +1,105 @@ +reflectionProvider = $reflectionProvider; + } + + public function getClass(): string + { + return 'Symfony\Component\DependencyInjection\Extension\Extension'; + } + + public function isMethodSupported(MethodReflection $methodReflection): bool + { + return $methodReflection->getName() === 'getConfiguration' + && $methodReflection->getDeclaringClass()->getName() === 'Symfony\Component\DependencyInjection\Extension\Extension'; + } + + public function getTypeFromMethodCall( + MethodReflection $methodReflection, + MethodCall $methodCall, + Scope $scope + ): ?Type + { + $types = []; + $extensionType = $scope->getType($methodCall->var); + $classes = $extensionType->getObjectClassNames(); + + foreach ($classes as $extensionName) { + if (str_contains($extensionName, "\0")) { + $types[] = new NullType(); + continue; + } + + $lastBackslash = strrpos($extensionName, '\\'); + if ($lastBackslash === false) { + $types[] = new NullType(); + continue; + } + + $configurationName = substr_replace($extensionName, '\Configuration', $lastBackslash); + if (!$this->reflectionProvider->hasClass($configurationName)) { + $types[] = new NullType(); + continue; + } + + $reflection = $this->reflectionProvider->getClass($configurationName); + if ($this->hasRequiredConstructor($reflection)) { + $types[] = new NullType(); + continue; + } + + $types[] = new ObjectType($configurationName); + } + + return TypeCombinator::union(...$types); + } + + private function hasRequiredConstructor(ClassReflection $class): bool + { + if (!$class->hasConstructor()) { + return false; + } + + $constructor = $class->getConstructor(); + foreach ($constructor->getVariants() as $variant) { + $anyRequired = false; + foreach ($variant->getParameters() as $parameter) { + if (!$parameter->isOptional()) { + $anyRequired = true; + break; + } + } + + if (!$anyRequired) { + return false; + } + } + + return true; + } + +} diff --git a/tests/Type/Symfony/ExtensionTest.php b/tests/Type/Symfony/ExtensionTest.php index 879abb5d..73eb49b7 100644 --- a/tests/Type/Symfony/ExtensionTest.php +++ b/tests/Type/Symfony/ExtensionTest.php @@ -57,6 +57,15 @@ public function dataFileAsserts(): iterable yield from $this->gatherAssertTypes(__DIR__ . '/data/FormInterface_getErrors.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/cache.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/form_data_type.php'); + + yield from $this->gatherAssertTypes(__DIR__ . '/data/extension/with-configuration/WithConfigurationExtension.php'); + yield from $this->gatherAssertTypes(__DIR__ . '/data/extension/without-configuration/WithoutConfigurationExtension.php'); + yield from $this->gatherAssertTypes(__DIR__ . '/data/extension/anonymous/AnonymousExtension.php'); + yield from $this->gatherAssertTypes(__DIR__ . '/data/extension/ignore-implemented/IgnoreImplementedExtension.php'); + yield from $this->gatherAssertTypes(__DIR__ . '/data/extension/multiple-types/MultipleTypes.php'); + yield from $this->gatherAssertTypes(__DIR__ . '/data/extension/with-configuration-with-constructor/WithConfigurationWithConstructorExtension.php'); + yield from $this->gatherAssertTypes(__DIR__ . '/data/extension/with-configuration-with-constructor-optional-params/WithConfigurationWithConstructorOptionalParamsExtension.php'); + yield from $this->gatherAssertTypes(__DIR__ . '/data/extension/with-configuration-with-constructor-required-params/WithConfigurationWithConstructorRequiredParamsExtension.php'); } /** diff --git a/tests/Type/Symfony/data/extension/anonymous/AnonymousExtension.php b/tests/Type/Symfony/data/extension/anonymous/AnonymousExtension.php new file mode 100644 index 00000000..1e33cb37 --- /dev/null +++ b/tests/Type/Symfony/data/extension/anonymous/AnonymousExtension.php @@ -0,0 +1,17 @@ +getConfiguration($configs, $container) + ); + } +}; diff --git a/tests/Type/Symfony/data/extension/ignore-implemented/IgnoreImplementedExtension.php b/tests/Type/Symfony/data/extension/ignore-implemented/IgnoreImplementedExtension.php new file mode 100644 index 00000000..614431f2 --- /dev/null +++ b/tests/Type/Symfony/data/extension/ignore-implemented/IgnoreImplementedExtension.php @@ -0,0 +1,23 @@ +getConfiguration($configs, $container) + ); + } + + public function getConfiguration(array $config, ContainerBuilder $container): ?ConfigurationInterface + { + return null; + } +} diff --git a/tests/Type/Symfony/data/extension/multiple-types/MultipleTypes.php b/tests/Type/Symfony/data/extension/multiple-types/MultipleTypes.php new file mode 100644 index 00000000..77c44003 --- /dev/null +++ b/tests/Type/Symfony/data/extension/multiple-types/MultipleTypes.php @@ -0,0 +1,18 @@ +getConfiguration($configs, $container) + ); +} diff --git a/tests/Type/Symfony/data/extension/with-configuration-with-constructor-optional-params/Configuration.php b/tests/Type/Symfony/data/extension/with-configuration-with-constructor-optional-params/Configuration.php new file mode 100644 index 00000000..4ff16c39 --- /dev/null +++ b/tests/Type/Symfony/data/extension/with-configuration-with-constructor-optional-params/Configuration.php @@ -0,0 +1,16 @@ +getConfiguration($configs, $container) + ); + } +} diff --git a/tests/Type/Symfony/data/extension/with-configuration-with-constructor-required-params/Configuration.php b/tests/Type/Symfony/data/extension/with-configuration-with-constructor-required-params/Configuration.php new file mode 100644 index 00000000..b9d5bcc1 --- /dev/null +++ b/tests/Type/Symfony/data/extension/with-configuration-with-constructor-required-params/Configuration.php @@ -0,0 +1,16 @@ +getConfiguration($configs, $container) + ); + } +} diff --git a/tests/Type/Symfony/data/extension/with-configuration-with-constructor/Configuration.php b/tests/Type/Symfony/data/extension/with-configuration-with-constructor/Configuration.php new file mode 100644 index 00000000..8eea9eb9 --- /dev/null +++ b/tests/Type/Symfony/data/extension/with-configuration-with-constructor/Configuration.php @@ -0,0 +1,16 @@ +getConfiguration($configs, $container) + ); + } +} diff --git a/tests/Type/Symfony/data/extension/with-configuration/Configuration.php b/tests/Type/Symfony/data/extension/with-configuration/Configuration.php new file mode 100644 index 00000000..4e8c51b5 --- /dev/null +++ b/tests/Type/Symfony/data/extension/with-configuration/Configuration.php @@ -0,0 +1,12 @@ +getConfiguration($configs, $container) + ); + } +} diff --git a/tests/Type/Symfony/data/extension/without-configuration/WithoutConfigurationExtension.php b/tests/Type/Symfony/data/extension/without-configuration/WithoutConfigurationExtension.php new file mode 100644 index 00000000..dccec3e2 --- /dev/null +++ b/tests/Type/Symfony/data/extension/without-configuration/WithoutConfigurationExtension.php @@ -0,0 +1,17 @@ +getConfiguration($configs, $container) + ); + } +} From a32bc86da24495025d7aafd1ba62444d4a364a98 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Sat, 16 Mar 2024 17:50:20 +0100 Subject: [PATCH 157/208] Add `@param-out` for PropertyAccessor * Add param out for propertyAccessor * Fix stub --- composer.json | 2 +- extension.neon | 6 +++++ .../Exception/AccessException.stub | 7 ++++++ .../Exception/ExceptionInterface.stub | 7 ++++++ .../Exception/InvalidArgumentException.stub | 7 ++++++ .../Exception/RuntimeException.stub | 7 ++++++ .../Exception/UnexpectedTypeException.stub | 7 ++++++ .../PropertyAccessorInterface.stub | 23 +++++++++++++++++++ tests/Type/Symfony/ExtensionTest.php | 1 + tests/Type/Symfony/data/property_accessor.php | 13 +++++++++++ 10 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 stubs/Symfony/Component/PropertyAccess/Exception/AccessException.stub create mode 100644 stubs/Symfony/Component/PropertyAccess/Exception/ExceptionInterface.stub create mode 100644 stubs/Symfony/Component/PropertyAccess/Exception/InvalidArgumentException.stub create mode 100644 stubs/Symfony/Component/PropertyAccess/Exception/RuntimeException.stub create mode 100644 stubs/Symfony/Component/PropertyAccess/Exception/UnexpectedTypeException.stub create mode 100644 stubs/Symfony/Component/PropertyAccess/PropertyAccessorInterface.stub create mode 100644 tests/Type/Symfony/data/property_accessor.php diff --git a/composer.json b/composer.json index b8f33962..7da1f489 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ "require": { "php": "^7.2 || ^8.0", "ext-simplexml": "*", - "phpstan/phpstan": "^1.10.36" + "phpstan/phpstan": "^1.10.62" }, "conflict": { "symfony/framework-bundle": "<3.0" diff --git a/extension.neon b/extension.neon index 531eb7df..09837d28 100644 --- a/extension.neon +++ b/extension.neon @@ -66,6 +66,12 @@ parameters: - stubs/Symfony/Component/OptionsResolver/Exception/InvalidOptionsException.stub - stubs/Symfony/Component/OptionsResolver/Options.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/PropertyAccessorInterface.stub - stubs/Symfony/Component/PropertyAccess/PropertyPathInterface.stub - stubs/Symfony/Component/Security/Acl/Model/AclInterface.stub - stubs/Symfony/Component/Security/Acl/Model/EntryInterface.stub diff --git a/stubs/Symfony/Component/PropertyAccess/Exception/AccessException.stub b/stubs/Symfony/Component/PropertyAccess/Exception/AccessException.stub new file mode 100644 index 00000000..a763b784 --- /dev/null +++ b/stubs/Symfony/Component/PropertyAccess/Exception/AccessException.stub @@ -0,0 +1,7 @@ + + * @phpstan-param T &$objectOrArray + * @phpstan-param-out ($objectOrArray is object ? T : array) $objectOrArray + * @phpstan-param string|PropertyPathInterface $propertyPath + * @phpstan-param mixed $value + * + * @return void + * + * @throws Exception\InvalidArgumentException If the property path is invalid + * @throws Exception\AccessException If a property/index does not exist or is not public + * @throws Exception\UnexpectedTypeException If a value within the path is neither object nor array + */ + public function setValue(&$objectOrArray, $propertyPath, $value); + +} diff --git a/tests/Type/Symfony/ExtensionTest.php b/tests/Type/Symfony/ExtensionTest.php index 73eb49b7..a076caac 100644 --- a/tests/Type/Symfony/ExtensionTest.php +++ b/tests/Type/Symfony/ExtensionTest.php @@ -28,6 +28,7 @@ public function dataFileAsserts(): iterable yield from $this->gatherAssertTypes(__DIR__ . '/data/ExampleOptionCommand.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/ExampleOptionLazyCommand.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/kernel_interface.php'); + yield from $this->gatherAssertTypes(__DIR__ . '/data/property_accessor.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/request_get_content.php'); $ref = new ReflectionMethod(Request::class, 'getSession'); diff --git a/tests/Type/Symfony/data/property_accessor.php b/tests/Type/Symfony/data/property_accessor.php new file mode 100644 index 00000000..0e445684 --- /dev/null +++ b/tests/Type/Symfony/data/property_accessor.php @@ -0,0 +1,13 @@ + 'ea']; +$propertyAccessor->setValue($array, 'foo', 'bar'); +assertType('array', $array); + +$object = new \stdClass(); +$propertyAccessor->setValue($object, 'foo', 'bar'); +assertType('stdClass', $object); From 99848d82a72417cc70d3d57cbae0887040ca7fbe Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Thu, 11 Apr 2024 16:37:03 +0200 Subject: [PATCH 158/208] Add stub for Compound::getConstraints() --- extension.neon | 2 ++ .../Component/Validator/Constraints/Composite.stub | 9 +++++++++ .../Component/Validator/Constraints/Compound.stub | 14 ++++++++++++++ 3 files changed, 25 insertions(+) create mode 100644 stubs/Symfony/Component/Validator/Constraints/Composite.stub create mode 100644 stubs/Symfony/Component/Validator/Constraints/Compound.stub diff --git a/extension.neon b/extension.neon index 09837d28..1eb888ab 100644 --- a/extension.neon +++ b/extension.neon @@ -96,6 +96,8 @@ parameters: - 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 diff --git a/stubs/Symfony/Component/Validator/Constraints/Composite.stub b/stubs/Symfony/Component/Validator/Constraints/Composite.stub new file mode 100644 index 00000000..8344ea94 --- /dev/null +++ b/stubs/Symfony/Component/Validator/Constraints/Composite.stub @@ -0,0 +1,9 @@ + $options + * @return array + */ + abstract protected function getConstraints(array $options): array; +} From 3fbf634b2495adaa1dc4b7300293f8abf74128d8 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Wed, 6 Mar 2024 17:17:02 +0100 Subject: [PATCH 159/208] Synchronize stub with symfony --- .../Component/Validator/ConstraintViolationInterface.stub | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/stubs/Symfony/Component/Validator/ConstraintViolationInterface.stub b/stubs/Symfony/Component/Validator/ConstraintViolationInterface.stub index e7f2b8a2..fd1c7b9b 100644 --- a/stubs/Symfony/Component/Validator/ConstraintViolationInterface.stub +++ b/stubs/Symfony/Component/Validator/ConstraintViolationInterface.stub @@ -3,7 +3,9 @@ namespace Symfony\Component\Validator; /** - * @method string __toString() Converts the violation into a string for debugging purposes. Not implementing it is deprecated since Symfony 6.1. + * @method Constraint|null getConstraint() Returns the constraint whose validation caused the violation. Not implementing it is deprecated since Symfony 6.3. + * @method mixed getCause() Returns the cause of the violation. Not implementing it is deprecated since Symfony 6.2. + * @method string __toString() Converts the violation into a string for debugging purposes. Not implementing it is deprecated since Symfony 6.1. */ interface ConstraintViolationInterface { From f4b9407fa3203aebafd422ae8f0eb1ef94659a80 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Sat, 13 Apr 2024 10:28:03 +0200 Subject: [PATCH 160/208] Synchronize stub with symfony --- stubs/Symfony/Component/Validator/Constraints/Compound.stub | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stubs/Symfony/Component/Validator/Constraints/Compound.stub b/stubs/Symfony/Component/Validator/Constraints/Compound.stub index 3227009b..6a36c6bf 100644 --- a/stubs/Symfony/Component/Validator/Constraints/Compound.stub +++ b/stubs/Symfony/Component/Validator/Constraints/Compound.stub @@ -7,7 +7,7 @@ use Symfony\Component\Validator\Constraint; abstract class Compound extends Composite { /** - * @param array $options + * @param array $options * @return array */ abstract protected function getConstraints(array $options): array; From 51183fefbaf4713aa81eddbd273dc59dd5e5ff71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Mirtes?= Date: Sat, 20 Apr 2024 08:38:35 +0200 Subject: [PATCH 161/208] Update lock-closed-issues.yml --- .github/workflows/lock-closed-issues.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lock-closed-issues.yml b/.github/workflows/lock-closed-issues.yml index c2b017b9..047fe906 100644 --- a/.github/workflows/lock-closed-issues.yml +++ b/.github/workflows/lock-closed-issues.yml @@ -2,7 +2,7 @@ name: 'Lock Issues' on: schedule: - - cron: '0 0 * * *' + - cron: '5 0 * * *' jobs: lock: From 2c3d666889173e902714359d8216ec5627527d92 Mon Sep 17 00:00:00 2001 From: Benoit Viguier Date: Wed, 29 May 2024 10:32:08 +0200 Subject: [PATCH 162/208] Support for alias to inlined service --- src/Symfony/XmlServiceMapFactory.php | 9 +++++++-- tests/Symfony/DefaultServiceMapTest.php | 11 +++++++++++ tests/Symfony/container.xml | 2 ++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/Symfony/XmlServiceMapFactory.php b/src/Symfony/XmlServiceMapFactory.php index ad52c373..1cae5d97 100644 --- a/src/Symfony/XmlServiceMapFactory.php +++ b/src/Symfony/XmlServiceMapFactory.php @@ -48,11 +48,11 @@ public function create(): ServiceMap } $service = new Service( - strpos((string) $attrs->id, '.') === 0 ? substr((string) $attrs->id, 1) : (string) $attrs->id, + $this->cleanServiceId((string) $attrs->id), isset($attrs->class) ? (string) $attrs->class : null, isset($attrs->public) && (string) $attrs->public === 'true', isset($attrs->synthetic) && (string) $attrs->synthetic === 'true', - isset($attrs->alias) ? (string) $attrs->alias : null + isset($attrs->alias) ? $this->cleanServiceId((string) $attrs->alias) : null ); if ($service->getAlias() !== null) { @@ -79,4 +79,9 @@ public function create(): ServiceMap return new DefaultServiceMap($services); } + private function cleanServiceId(string $id): string + { + return strpos($id, '.') === 0 ? substr($id, 1) : $id; + } + } diff --git a/tests/Symfony/DefaultServiceMapTest.php b/tests/Symfony/DefaultServiceMapTest.php index dc8487e5..a0a27d98 100644 --- a/tests/Symfony/DefaultServiceMapTest.php +++ b/tests/Symfony/DefaultServiceMapTest.php @@ -113,6 +113,17 @@ static function (?Service $service): void { self::assertSame('withClass', $service->getAlias()); }, ]; + yield [ + 'aliasForInlined', + static function (?Service $service): void { + self::assertNotNull($service); + self::assertSame('aliasForInlined', $service->getId()); + self::assertNull($service->getClass()); + self::assertFalse($service->isPublic()); + self::assertFalse($service->isSynthetic()); + self::assertSame('inlined', $service->getAlias()); + }, + ]; } } diff --git a/tests/Symfony/container.xml b/tests/Symfony/container.xml index 4be3bd98..f456ab51 100644 --- a/tests/Symfony/container.xml +++ b/tests/Symfony/container.xml @@ -41,5 +41,7 @@ + + From af6ae0f4b91bc080265e80776af26da3e5befb28 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Thu, 30 May 2024 17:01:27 +0200 Subject: [PATCH 163/208] Update getArgument return type in interact method --- ...InterfaceGetArgumentDynamicReturnTypeExtension.php | 11 +++++++++++ tests/Type/Symfony/data/ExampleBaseCommand.php | 11 +++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php index 0af7c88d..a8d3eb37 100644 --- a/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php @@ -10,11 +10,13 @@ use PHPStan\Type\ArrayType; use PHPStan\Type\DynamicMethodReturnTypeExtension; use PHPStan\Type\IntegerType; +use PHPStan\Type\NullType; use PHPStan\Type\StringType; use PHPStan\Type\Type; use PHPStan\Type\TypeCombinator; use PHPStan\Type\TypeUtils; use function count; +use function in_array; final class InputInterfaceGetArgumentDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension { @@ -76,6 +78,15 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method } } + $method = $scope->getFunction(); + if ( + $method instanceof MethodReflection + && $method->getName() === 'interact' + && in_array('Symfony\Component\Console\Command\Command', $method->getDeclaringClass()->getParentClassesNames(), true) + ) { + $argTypes[] = new NullType(); + } + return count($argTypes) > 0 ? TypeCombinator::union(...$argTypes) : null; } diff --git a/tests/Type/Symfony/data/ExampleBaseCommand.php b/tests/Type/Symfony/data/ExampleBaseCommand.php index 0ab3a322..6c1f9422 100644 --- a/tests/Type/Symfony/data/ExampleBaseCommand.php +++ b/tests/Type/Symfony/data/ExampleBaseCommand.php @@ -17,6 +17,17 @@ protected function configure(): void $this->addArgument('base'); } + protected function interact(InputInterface $input, OutputInterface $output): int + { + assertType('string|null', $input->getArgument('base')); + assertType('string|null', $input->getArgument('aaa')); + assertType('string|null', $input->getArgument('bbb')); + assertType('array|string|null', $input->getArgument('diff')); + assertType('array|null', $input->getArgument('arr')); + assertType('string|null', $input->getArgument('both')); + assertType('Symfony\Component\Console\Helper\QuestionHelper', $this->getHelper('question')); + } + protected function execute(InputInterface $input, OutputInterface $output): int { assertType('string|null', $input->getArgument('base')); From bca27f1701fc1a297749e6c2a1e3da4462c1a6af Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Thu, 6 Jun 2024 15:55:20 +0200 Subject: [PATCH 164/208] Fix interact method inference --- ...nterfaceGetArgumentDynamicReturnTypeExtension.php | 12 ++++++++++-- tests/Type/Symfony/data/ExampleBaseCommand.php | 12 ++++++++---- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php index a8d3eb37..860317f6 100644 --- a/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php @@ -57,6 +57,7 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method $argName = $argStrings[0]->getValue(); $argTypes = []; + $canBeNullInInteract = false; foreach ($this->consoleApplicationResolver->findCommands($classReflection) as $command) { try { $command->mergeApplicationDefinition(); @@ -70,6 +71,8 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method $argType = new StringType(); if (!$argument->isRequired()) { $argType = TypeCombinator::union($argType, $scope->getTypeFromValue($argument->getDefault())); + } else { + $canBeNullInInteract = true; } } $argTypes[] = $argType; @@ -78,16 +81,21 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method } } + if (count($argTypes) === 0) { + return null; + } + $method = $scope->getFunction(); if ( - $method instanceof MethodReflection + $canBeNullInInteract + && $method instanceof MethodReflection && $method->getName() === 'interact' && in_array('Symfony\Component\Console\Command\Command', $method->getDeclaringClass()->getParentClassesNames(), true) ) { $argTypes[] = new NullType(); } - return count($argTypes) > 0 ? TypeCombinator::union(...$argTypes) : null; + return TypeCombinator::union(...$argTypes); } } diff --git a/tests/Type/Symfony/data/ExampleBaseCommand.php b/tests/Type/Symfony/data/ExampleBaseCommand.php index 6c1f9422..d2357089 100644 --- a/tests/Type/Symfony/data/ExampleBaseCommand.php +++ b/tests/Type/Symfony/data/ExampleBaseCommand.php @@ -3,6 +3,7 @@ namespace PHPStan\Type\Symfony; use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use function PHPStan\Testing\assertType; @@ -14,16 +15,18 @@ protected function configure(): void { parent::configure(); + $this->addArgument('required', InputArgument::REQUIRED); $this->addArgument('base'); } protected function interact(InputInterface $input, OutputInterface $output): int { assertType('string|null', $input->getArgument('base')); - assertType('string|null', $input->getArgument('aaa')); - assertType('string|null', $input->getArgument('bbb')); - assertType('array|string|null', $input->getArgument('diff')); - assertType('array|null', $input->getArgument('arr')); + assertType('string', $input->getArgument('aaa')); + assertType('string', $input->getArgument('bbb')); + assertType('string|null', $input->getArgument('required')); + assertType('array|string', $input->getArgument('diff')); + assertType('array', $input->getArgument('arr')); assertType('string|null', $input->getArgument('both')); assertType('Symfony\Component\Console\Helper\QuestionHelper', $this->getHelper('question')); } @@ -33,6 +36,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int assertType('string|null', $input->getArgument('base')); assertType('string', $input->getArgument('aaa')); assertType('string', $input->getArgument('bbb')); + assertType('string', $input->getArgument('required')); assertType('array|string', $input->getArgument('diff')); assertType('array', $input->getArgument('arr')); assertType('string|null', $input->getArgument('both')); From 1bd7c339f622dfb5a1a97dcaf1a862734eabfa1d Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Wed, 19 Jun 2024 21:29:16 +0200 Subject: [PATCH 165/208] Also support command argument and initialize method --- ...eGetArgumentDynamicReturnTypeExtension.php | 2 +- ...eHasArgumentDynamicReturnTypeExtension.php | 12 ++++++++++ .../Type/Symfony/data/ExampleBaseCommand.php | 23 ++++++++++++++++++- 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php index 860317f6..5eb4cfe9 100644 --- a/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php @@ -89,7 +89,7 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method if ( $canBeNullInInteract && $method instanceof MethodReflection - && $method->getName() === 'interact' + && ($method->getName() === 'interact' || $method->getName() === 'initialize') && in_array('Symfony\Component\Console\Command\Command', $method->getDeclaringClass()->getParentClassesNames(), true) ) { $argTypes[] = new NullType(); diff --git a/src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php index 4ce49c4a..2ca6b4a1 100644 --- a/src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php @@ -13,6 +13,7 @@ use PHPStan\Type\TypeUtils; use function array_unique; use function count; +use function in_array; final class InputInterfaceHasArgumentDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension { @@ -52,6 +53,17 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method } $argName = $argStrings[0]->getValue(); + if ($argName === 'command') { + $method = $scope->getFunction(); + if ( + $method instanceof MethodReflection + && ($method->getName() === 'interact' || $method->getName() === 'initialize') + && in_array('Symfony\Component\Console\Command\Command', $method->getDeclaringClass()->getParentClassesNames(), true) + ) { + return null; + } + } + $returnTypes = []; foreach ($this->consoleApplicationResolver->findCommands($classReflection) as $command) { try { diff --git a/tests/Type/Symfony/data/ExampleBaseCommand.php b/tests/Type/Symfony/data/ExampleBaseCommand.php index d2357089..0376429f 100644 --- a/tests/Type/Symfony/data/ExampleBaseCommand.php +++ b/tests/Type/Symfony/data/ExampleBaseCommand.php @@ -19,8 +19,26 @@ protected function configure(): void $this->addArgument('base'); } - protected function interact(InputInterface $input, OutputInterface $output): int + protected function initialize(InputInterface $input, OutputInterface $output): void { + assertType('bool', $input->hasArgument('command')); + assertType('string|null', $input->getArgument('command')); + + assertType('string|null', $input->getArgument('base')); + assertType('string', $input->getArgument('aaa')); + assertType('string', $input->getArgument('bbb')); + assertType('string|null', $input->getArgument('required')); + assertType('array|string', $input->getArgument('diff')); + assertType('array', $input->getArgument('arr')); + assertType('string|null', $input->getArgument('both')); + assertType('Symfony\Component\Console\Helper\QuestionHelper', $this->getHelper('question')); + } + + protected function interact(InputInterface $input, OutputInterface $output): void + { + assertType('bool', $input->hasArgument('command')); + assertType('string|null', $input->getArgument('command')); + assertType('string|null', $input->getArgument('base')); assertType('string', $input->getArgument('aaa')); assertType('string', $input->getArgument('bbb')); @@ -33,6 +51,9 @@ protected function interact(InputInterface $input, OutputInterface $output): int protected function execute(InputInterface $input, OutputInterface $output): int { + assertType('true', $input->hasArgument('command')); + assertType('string', $input->getArgument('command')); + assertType('string|null', $input->getArgument('base')); assertType('string', $input->getArgument('aaa')); assertType('string', $input->getArgument('bbb')); From e909a075d69e0d4db262ac3407350ae2c6b6ab5f Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Tue, 16 Jul 2024 13:33:56 +0200 Subject: [PATCH 166/208] DiagnoseExtension --- composer.json | 2 +- extension.neon | 4 +++ src/Symfony/ConsoleApplicationResolver.php | 5 ++++ src/Symfony/SymfonyDiagnoseExtension.php | 29 ++++++++++++++++++++++ 4 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 src/Symfony/SymfonyDiagnoseExtension.php diff --git a/composer.json b/composer.json index 2269d9a4..f132544d 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ "require": { "php": "^7.2 || ^8.0", "ext-simplexml": "*", - "phpstan/phpstan": "^1.11" + "phpstan/phpstan": "^1.11.7" }, "conflict": { "symfony/framework-bundle": "<3.0" diff --git a/extension.neon b/extension.neon index 1eb888ab..e58d668f 100644 --- a/extension.neon +++ b/extension.neon @@ -322,6 +322,10 @@ services: class: PHPStan\Symfony\PasswordAuthenticatedUserStubFilesExtension tags: - phpstan.stubFilesExtension + - + class: PHPStan\Symfony\SymfonyDiagnoseExtension + tags: + - phpstan.diagnoseExtension # FormInterface::getErrors() return type - diff --git a/src/Symfony/ConsoleApplicationResolver.php b/src/Symfony/ConsoleApplicationResolver.php index 51bd7960..52f5f4f0 100644 --- a/src/Symfony/ConsoleApplicationResolver.php +++ b/src/Symfony/ConsoleApplicationResolver.php @@ -27,6 +27,11 @@ public function __construct(Configuration $configuration) $this->consoleApplicationLoader = $configuration->getConsoleApplicationLoader(); } + public function hasConsoleApplicationLoader(): bool + { + return $this->consoleApplicationLoader !== null; + } + private function getConsoleApplication(): ?Application { if ($this->consoleApplicationLoader === null) { diff --git a/src/Symfony/SymfonyDiagnoseExtension.php b/src/Symfony/SymfonyDiagnoseExtension.php new file mode 100644 index 00000000..afd566dc --- /dev/null +++ b/src/Symfony/SymfonyDiagnoseExtension.php @@ -0,0 +1,29 @@ +consoleApplicationResolver = $consoleApplicationResolver; + } + + public function print(Output $output): void + { + $output->writeLineFormatted(sprintf( + 'Symfony\'s consoleApplicationLoader: %s', + $this->consoleApplicationResolver->hasConsoleApplicationLoader() ? 'In use' : 'No' + )); + $output->writeLineFormatted(''); + } + +} From ee88a01bc48f608143d3376802ec952270737cb8 Mon Sep 17 00:00:00 2001 From: Jan Nedbal Date: Thu, 18 Jul 2024 12:19:10 +0200 Subject: [PATCH 167/208] Fix internal error when DIC param map has > 256 items --- .../ParameterDynamicReturnTypeExtension.php | 5 +- tests/Type/Symfony/container.xml | 259 ++++++++++++++++++ .../data/ExampleAbstractController.php | 1 + 3 files changed, 264 insertions(+), 1 deletion(-) diff --git a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php index 82d3599e..82535133 100644 --- a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php @@ -13,6 +13,7 @@ use PHPStan\Type\ArrayType; use PHPStan\Type\BooleanType; use PHPStan\Type\Constant\ConstantArrayType; +use PHPStan\Type\Constant\ConstantArrayTypeBuilder; use PHPStan\Type\Constant\ConstantBooleanType; use PHPStan\Type\ConstantType; use PHPStan\Type\DynamicMethodReturnTypeExtension; @@ -168,7 +169,9 @@ private function generalizeTypeFromValue(Scope $scope, $value): Type $valueTypes[] = $this->generalizeTypeFromValue($scope, $element); } - return new ConstantArrayType($keyTypes, $valueTypes); + return ConstantArrayTypeBuilder::createFromConstantArray( + new ConstantArrayType($keyTypes, $valueTypes) + )->getArray(); } return new ArrayType( diff --git a/tests/Type/Symfony/container.xml b/tests/Type/Symfony/container.xml index e1ef6d89..224c72db 100644 --- a/tests/Type/Symfony/container.xml +++ b/tests/Type/Symfony/container.xml @@ -82,6 +82,265 @@ value of b value of c + + v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8 + v9 + v10 + v11 + v12 + v13 + v14 + v15 + v16 + v17 + v18 + v19 + v20 + v21 + v22 + v23 + v24 + v25 + v26 + v27 + v28 + v29 + v30 + v31 + v32 + v33 + v34 + v35 + v36 + v37 + v38 + v39 + v40 + v41 + v42 + v43 + v44 + v45 + v46 + v47 + v48 + v49 + v50 + v51 + v52 + v53 + v54 + v55 + v56 + v57 + v58 + v59 + v60 + v61 + v62 + v63 + v64 + v65 + v66 + v67 + v68 + v69 + v70 + v71 + v72 + v73 + v74 + v75 + v76 + v77 + v78 + v79 + v80 + v81 + v82 + v83 + v84 + v85 + v86 + v87 + v88 + v89 + v90 + v91 + v92 + v93 + v94 + v95 + v96 + v97 + v98 + v99 + v100 + v101 + v102 + v103 + v104 + v105 + v106 + v107 + v108 + v109 + v110 + v111 + v112 + v113 + v114 + v115 + v116 + v117 + v118 + v119 + v120 + v121 + v122 + v123 + v124 + v125 + v126 + v127 + v128 + v129 + v130 + v131 + v132 + v133 + v134 + v135 + v136 + v137 + v138 + v139 + v140 + v141 + v142 + v143 + v144 + v145 + v146 + v147 + v148 + v149 + v150 + v151 + v152 + v153 + v154 + v155 + v156 + v157 + v158 + v159 + v160 + v161 + v162 + v163 + v164 + v165 + v166 + v167 + v168 + v169 + v170 + v171 + v172 + v173 + v174 + v175 + v176 + v177 + v178 + v179 + v180 + v181 + v182 + v183 + v184 + v185 + v186 + v187 + v188 + v189 + v190 + v191 + v192 + v193 + v194 + v195 + v196 + v197 + v198 + v199 + v200 + v201 + v202 + v203 + v204 + v205 + v206 + v207 + v208 + v209 + v210 + v211 + v212 + v213 + v214 + v215 + v216 + v217 + v218 + v219 + v220 + v221 + v222 + v223 + v224 + v225 + v226 + v227 + v228 + v229 + v230 + v231 + v232 + v233 + v234 + v235 + v236 + v237 + v238 + v239 + v240 + v241 + v242 + v243 + v244 + v245 + v246 + v247 + v248 + v249 + v250 + v251 + v252 + v253 + v254 + v255 + v256 + v257 + VGhpcyBpcyBhIEJlbGwgY2hhciAH Y-m-d\TH:i:sP diff --git a/tests/Type/Symfony/data/ExampleAbstractController.php b/tests/Type/Symfony/data/ExampleAbstractController.php index d9857a53..53b38066 100644 --- a/tests/Type/Symfony/data/ExampleAbstractController.php +++ b/tests/Type/Symfony/data/ExampleAbstractController.php @@ -80,6 +80,7 @@ public function parameters(ContainerInterface $container, ParameterBagInterface assertType("array{a: string, b: string, c: string}", $container->getParameter('app.map')); assertType("array{a: string, b: string, c: string}", $parameterBag->get('app.map')); assertType("array{a: string, b: string, c: string}", $this->getParameter('app.map')); + assertType("non-falsy-string", implode(',', $this->getParameter('app.hugemap'))); assertType("string", $container->getParameter('app.binary')); assertType("string", $parameterBag->get('app.binary')); assertType("string", $this->getParameter('app.binary')); From 14eec8c011b856eee4d744a2a3f709db1e1858bd Mon Sep 17 00:00:00 2001 From: Zachary Lund Date: Mon, 12 Aug 2024 11:03:46 -0500 Subject: [PATCH 168/208] Add stub for AbstractController::createForm() --- extension.neon | 2 ++ .../Controller/AbstractController.stub | 24 +++++++++++++++++++ .../Service/ServiceSubscriberInterface.stub | 7 ++++++ tests/Type/Symfony/data/form_data_type.php | 18 ++++++++++++++ 4 files changed, 51 insertions(+) create mode 100644 stubs/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.stub create mode 100644 stubs/Symfony/Contracts/Service/ServiceSubscriberInterface.stub diff --git a/extension.neon b/extension.neon index e58d668f..512f9908 100644 --- a/extension.neon +++ b/extension.neon @@ -27,6 +27,7 @@ parameters: - 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/FrameworkBundle/KernelBrowser.stub - stubs/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.stub - stubs/Symfony/Bundle/FrameworkBundle/Test/TestContainer.stub @@ -103,6 +104,7 @@ parameters: - 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: diff --git a/stubs/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.stub b/stubs/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.stub new file mode 100644 index 00000000..075dce6d --- /dev/null +++ b/stubs/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.stub @@ -0,0 +1,24 @@ + + * @template TData + * + * @param class-string $type + * @param TData $data + * @param array $options + * + * @phpstan-return ($data is null ? FormInterface : FormInterface) + */ + protected function createForm(string $type, $data = null, array $options = []): FormInterface + { + } +} diff --git a/stubs/Symfony/Contracts/Service/ServiceSubscriberInterface.stub b/stubs/Symfony/Contracts/Service/ServiceSubscriberInterface.stub new file mode 100644 index 00000000..8860e239 --- /dev/null +++ b/stubs/Symfony/Contracts/Service/ServiceSubscriberInterface.stub @@ -0,0 +1,7 @@ +createForm(DataClassType::class, new DataClass()); + assertType('GenericFormDataType\DataClass', $form->getData()); + } + + public function doSomethingNullable(): void + { + $form = $this->createForm(DataClassType::class); + assertType('GenericFormDataType\DataClass|null', $form->getData()); + } + +} From 3cf113fa3e190ab9ab47b0aed46f32a36dfc2c7a Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Thu, 5 Sep 2024 13:39:28 +0200 Subject: [PATCH 169/208] Fix stubs --- .../Component/EventDispatcher/EventDispatcherInterface.stub | 2 +- stubs/Symfony/Component/HttpFoundation/Cookie.stub | 4 ++-- .../Serializer/Exception/ExtraAttributesException.stub | 2 +- stubs/Symfony/Contracts/Cache/CacheInterface.stub | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/stubs/Symfony/Component/EventDispatcher/EventDispatcherInterface.stub b/stubs/Symfony/Component/EventDispatcher/EventDispatcherInterface.stub index e4ad8fc4..a58e43ca 100644 --- a/stubs/Symfony/Component/EventDispatcher/EventDispatcherInterface.stub +++ b/stubs/Symfony/Component/EventDispatcher/EventDispatcherInterface.stub @@ -10,5 +10,5 @@ interface EventDispatcherInterface * * @return TEvent */ - public function dispatch(object $event, string $eventName = null): object; + public function dispatch(object $event, ?string $eventName = null): object; } diff --git a/stubs/Symfony/Component/HttpFoundation/Cookie.stub b/stubs/Symfony/Component/HttpFoundation/Cookie.stub index 40ee45ab..cfb45fa3 100644 --- a/stubs/Symfony/Component/HttpFoundation/Cookie.stub +++ b/stubs/Symfony/Component/HttpFoundation/Cookie.stub @@ -21,7 +21,7 @@ class Cookie * * @throws \InvalidArgumentException */ - public function __construct(string $name, string $value = null, $expire = 0, ?string $path = '/', string $domain = null, ?bool $secure = false, bool $httpOnly = true, bool $raw = false, string $sameSite = null); + public function __construct(string $name, ?string $value = null, $expire = 0, ?string $path = '/', ?string $domain = null, ?bool $secure = false, bool $httpOnly = true, bool $raw = false, ?string $sameSite = null); /** * @param string $name The name of the cookie @@ -36,7 +36,7 @@ class Cookie * * @throws \InvalidArgumentException */ - public function create(string $name, string $value = null, $expire = 0, ?string $path = '/', string $domain = null, ?bool $secure = false, bool $httpOnly = true, bool $raw = false, string $sameSite = null): self; + public function create(string $name, ?string $value = null, $expire = 0, ?string $path = '/', ?string $domain = null, ?bool $secure = false, bool $httpOnly = true, bool $raw = false, ?string $sameSite = null): self; /** * @return self::SAMESITE_*|null diff --git a/stubs/Symfony/Component/Serializer/Exception/ExtraAttributesException.stub b/stubs/Symfony/Component/Serializer/Exception/ExtraAttributesException.stub index 18621931..b23f0d19 100644 --- a/stubs/Symfony/Component/Serializer/Exception/ExtraAttributesException.stub +++ b/stubs/Symfony/Component/Serializer/Exception/ExtraAttributesException.stub @@ -7,7 +7,7 @@ class ExtraAttributesException extends RuntimeException /** * @param string[] $extraAttributes */ - public function __construct(array $extraAttributes, \Throwable $previous = null) + public function __construct(array $extraAttributes, ?\Throwable $previous = null) { } diff --git a/stubs/Symfony/Contracts/Cache/CacheInterface.stub b/stubs/Symfony/Contracts/Cache/CacheInterface.stub index ff3027b1..a361ead4 100644 --- a/stubs/Symfony/Contracts/Cache/CacheInterface.stub +++ b/stubs/Symfony/Contracts/Cache/CacheInterface.stub @@ -15,5 +15,5 @@ interface CacheInterface * * @throws InvalidArgumentException */ - public function get(string $key, callable $callback, float $beta = null, array &$metadata = null); + public function get(string $key, callable $callback, ?float $beta = null, ?array &$metadata = null); } From 42797536c3de1531769a725bfbb404f220ae0516 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Thu, 5 Sep 2024 18:13:37 +0200 Subject: [PATCH 170/208] Test newer PHP versions --- .github/workflows/build.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b4453157..c4f7c922 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,6 +22,8 @@ jobs: - "8.0" - "8.1" - "8.2" + - "8.3" + - "8.4" steps: - name: "Checkout" @@ -93,6 +95,8 @@ jobs: - "8.0" - "8.1" - "8.2" + - "8.3" + - "8.4" dependencies: - "lowest" - "highest" @@ -132,6 +136,8 @@ jobs: - "8.0" - "8.1" - "8.2" + - "8.3" + - "8.4" dependencies: - "lowest" - "highest" From 30d088616836b108fbfc634211c2feae130ae42e Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Thu, 5 Sep 2024 18:13:54 +0200 Subject: [PATCH 171/208] Pin build-cs --- .github/workflows/build.yml | 1 + Makefile | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c4f7c922..ad0554fb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -58,6 +58,7 @@ jobs: with: repository: "phpstan/build-cs" path: "build-cs" + ref: "1.x" - name: "Install PHP" uses: "shivammathur/setup-php@v2" diff --git a/Makefile b/Makefile index ecd8cfb2..b01b1537 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ lint: .PHONY: cs-install cs-install: git clone https://github.com/phpstan/build-cs.git || true - git -C build-cs fetch origin && git -C build-cs reset --hard origin/main + git -C build-cs fetch origin && git -C build-cs reset --hard origin/1.x composer install --working-dir build-cs .PHONY: cs From 51ab2438fb2695467cf96b58d2f8f28d4dd1e3e9 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Thu, 5 Sep 2024 18:15:09 +0200 Subject: [PATCH 172/208] Require PHPStan 1.12 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index f132544d..0d5cd41c 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ "require": { "php": "^7.2 || ^8.0", "ext-simplexml": "*", - "phpstan/phpstan": "^1.11.7" + "phpstan/phpstan": "^1.12" }, "conflict": { "symfony/framework-bundle": "<3.0" From 2c53b3802664ade95c0b12735b7b923ba52a545c Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Fri, 6 Sep 2024 14:20:56 +0200 Subject: [PATCH 173/208] Open 2.0.x --- .github/workflows/build.yml | 2 +- composer.json | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ad0554fb..df52d534 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,7 +6,7 @@ on: pull_request: push: branches: - - "1.4.x" + - "2.0.x" jobs: lint: diff --git a/composer.json b/composer.json index 0d5cd41c..ffb9f3b8 100644 --- a/composer.json +++ b/composer.json @@ -13,9 +13,9 @@ } ], "require": { - "php": "^7.2 || ^8.0", + "php": "^7.4 || ^8.0", "ext-simplexml": "*", - "phpstan/phpstan": "^1.12" + "phpstan/phpstan": "^2.0" }, "conflict": { "symfony/framework-bundle": "<3.0" @@ -23,9 +23,9 @@ "require-dev": { "nikic/php-parser": "^4.13.0", "php-parallel-lint/php-parallel-lint": "^1.2", - "phpstan/phpstan-phpunit": "^1.3.11", - "phpstan/phpstan-strict-rules": "^1.5.1", - "phpunit/phpunit": "^8.5.29 || ^9.5", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^9.6", "psr/container": "1.0 || 1.1.1", "symfony/config": "^5.4 || ^6.1", "symfony/console": "^5.4 || ^6.1", From 2f736a628af8d5059cf0fc831eb96771a9669d86 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Fri, 6 Sep 2024 14:21:26 +0200 Subject: [PATCH 174/208] Stop testing PHP 7.2 and 7.3 --- .github/workflows/build.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index df52d534..8d659b18 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,8 +16,6 @@ jobs: strategy: matrix: php-version: - - "7.2" - - "7.3" - "7.4" - "8.0" - "8.1" @@ -90,8 +88,6 @@ jobs: fail-fast: false matrix: php-version: - - "7.2" - - "7.3" - "7.4" - "8.0" - "8.1" @@ -131,8 +127,6 @@ jobs: fail-fast: false matrix: php-version: - - "7.2" - - "7.3" - "7.4" - "8.0" - "8.1" From 90e89765c80dd91483968dca9c3f6e124784e59f Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Fri, 6 Sep 2024 14:21:44 +0200 Subject: [PATCH 175/208] Update build-cs --- .github/workflows/build.yml | 2 +- Makefile | 2 +- .../ContainerInterfacePrivateServiceRule.php | 3 +- .../ContainerInterfaceUnknownServiceRule.php | 6 ++-- .../InvalidArgumentDefaultValueRule.php | 4 +-- .../Symfony/InvalidOptionDefaultValueRule.php | 4 +-- src/Rules/Symfony/UndefinedArgumentRule.php | 6 ++-- src/Rules/Symfony/UndefinedOptionRule.php | 6 ++-- src/Symfony/Configuration.php | 2 +- src/Symfony/ConsoleApplicationResolver.php | 6 ++-- src/Symfony/DefaultParameterMap.php | 6 ++-- src/Symfony/DefaultServiceMap.php | 2 +- src/Symfony/InputBagStubFilesExtension.php | 3 +- src/Symfony/Parameter.php | 3 +- ...ordAuthenticatedUserStubFilesExtension.php | 3 +- src/Symfony/RequiredAutowiringExtension.php | 3 +- src/Symfony/Service.php | 15 +++------ src/Symfony/SymfonyDiagnoseExtension.php | 5 ++- src/Symfony/XmlParameterMapFactory.php | 5 ++- src/Symfony/XmlServiceMapFactory.php | 7 ++-- .../ArgumentTypeSpecifyingExtension.php | 8 ++--- ...InterfaceGetDynamicReturnTypeExtension.php | 2 +- ...andGetHelperDynamicReturnTypeExtension.php | 3 +- ...ionPrototypeDynamicReturnTypeExtension.php | 2 +- ...ParentObjectDynamicReturnTypeExtension.php | 5 ++- ...ReturnParentDynamicReturnTypeExtension.php | 5 ++- ...rGetRootNodeDynamicReturnTypeExtension.php | 2 +- .../Config/ValueObject/ParentObjectType.php | 3 +- .../Config/ValueObject/TreeBuilderType.php | 3 +- .../Symfony/EnvelopeReturnTypeExtension.php | 2 +- ...ionGetConfigurationReturnTypeExtension.php | 3 +- src/Type/Symfony/Helper.php | 2 +- .../InputBagTypeSpecifyingExtension.php | 5 ++- ...eGetArgumentDynamicReturnTypeExtension.php | 3 +- ...aceGetOptionDynamicReturnTypeExtension.php | 6 ++-- ...ceGetOptionsDynamicReturnTypeExtension.php | 6 ++-- ...eHasArgumentDynamicReturnTypeExtension.php | 3 +- ...aceHasOptionDynamicReturnTypeExtension.php | 3 +- .../Symfony/OptionTypeSpecifyingExtension.php | 8 ++--- .../ParameterDynamicReturnTypeExtension.php | 32 ++++++------------- .../RequestTypeSpecifyingExtension.php | 5 ++- ...nseHeaderBagDynamicReturnTypeExtension.php | 6 ++-- .../SerializerDynamicReturnTypeExtension.php | 6 ++-- .../ServiceDynamicReturnTypeExtension.php | 15 +++------ .../ServiceTypeSpecifyingExtension.php | 11 +++---- ...nerInterfacePrivateServiceRuleFakeTest.php | 8 ++--- ...ntainerInterfacePrivateServiceRuleTest.php | 6 ++-- ...nerInterfaceUnknownServiceRuleFakeTest.php | 4 +-- ...ntainerInterfaceUnknownServiceRuleTest.php | 6 ++-- .../Symfony/ExampleServiceSubscriber.php | 3 +- .../InvalidArgumentDefaultValueRuleTest.php | 2 +- .../InvalidOptionDefaultValueRuleTest.php | 2 +- .../Symfony/UndefinedArgumentRuleTest.php | 2 +- .../Rules/Symfony/UndefinedOptionRuleTest.php | 2 +- .../Symfony/console_application_loader.php | 4 +-- 55 files changed, 107 insertions(+), 174 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8d659b18..88543fb5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -56,7 +56,7 @@ jobs: with: repository: "phpstan/build-cs" path: "build-cs" - ref: "1.x" + ref: "2.x" - name: "Install PHP" uses: "shivammathur/setup-php@v2" diff --git a/Makefile b/Makefile index b01b1537..1ee557df 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ lint: .PHONY: cs-install cs-install: git clone https://github.com/phpstan/build-cs.git || true - git -C build-cs fetch origin && git -C build-cs reset --hard origin/1.x + git -C build-cs fetch origin && git -C build-cs reset --hard origin/2.x composer install --working-dir build-cs .PHONY: cs diff --git a/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php b/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php index 996d3b77..5d935859 100644 --- a/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php +++ b/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php @@ -19,8 +19,7 @@ final class ContainerInterfacePrivateServiceRule implements Rule { - /** @var ServiceMap */ - private $serviceMap; + private ServiceMap $serviceMap; public function __construct(ServiceMap $symfonyServiceMap) { diff --git a/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php b/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php index fc7d9585..ec1a70e0 100644 --- a/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php +++ b/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php @@ -19,11 +19,9 @@ final class ContainerInterfaceUnknownServiceRule implements Rule { - /** @var ServiceMap */ - private $serviceMap; + private ServiceMap $serviceMap; - /** @var Standard */ - private $printer; + private Standard $printer; public function __construct(ServiceMap $symfonyServiceMap, Standard $printer) { diff --git a/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php b/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php index 153d4af5..27643a7d 100644 --- a/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php +++ b/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php @@ -62,7 +62,7 @@ public function processNode(Node $node, Scope $scope): array return [ RuleErrorBuilder::message(sprintf( 'Parameter #4 $default of method Symfony\Component\Console\Command\Command::addArgument() expects string|null, %s given.', - $defaultType->describe(VerbosityLevel::typeOnly()) + $defaultType->describe(VerbosityLevel::typeOnly()), ))->identifier('argument.type')->build(), ]; } @@ -72,7 +72,7 @@ public function processNode(Node $node, Scope $scope): array return [ RuleErrorBuilder::message(sprintf( 'Parameter #4 $default of method Symfony\Component\Console\Command\Command::addArgument() expects array|null, %s given.', - $defaultType->describe(VerbosityLevel::typeOnly()) + $defaultType->describe(VerbosityLevel::typeOnly()), ))->identifier('argument.type')->build(), ]; } diff --git a/src/Rules/Symfony/InvalidOptionDefaultValueRule.php b/src/Rules/Symfony/InvalidOptionDefaultValueRule.php index 1595f4a8..152514f1 100644 --- a/src/Rules/Symfony/InvalidOptionDefaultValueRule.php +++ b/src/Rules/Symfony/InvalidOptionDefaultValueRule.php @@ -67,7 +67,7 @@ public function processNode(Node $node, Scope $scope): array RuleErrorBuilder::message(sprintf( 'Parameter #5 $default of method Symfony\Component\Console\Command\Command::addOption() expects %s, %s given.', $checkType->describe(VerbosityLevel::typeOnly()), - $defaultType->describe(VerbosityLevel::typeOnly()) + $defaultType->describe(VerbosityLevel::typeOnly()), ))->identifier('argument.type')->build(), ]; } @@ -78,7 +78,7 @@ public function processNode(Node $node, Scope $scope): array return [ RuleErrorBuilder::message(sprintf( 'Parameter #5 $default of method Symfony\Component\Console\Command\Command::addOption() expects array|null, %s given.', - $defaultType->describe(VerbosityLevel::typeOnly()) + $defaultType->describe(VerbosityLevel::typeOnly()), ))->identifier('argument.type')->build(), ]; } diff --git a/src/Rules/Symfony/UndefinedArgumentRule.php b/src/Rules/Symfony/UndefinedArgumentRule.php index cd8be894..ff997be5 100644 --- a/src/Rules/Symfony/UndefinedArgumentRule.php +++ b/src/Rules/Symfony/UndefinedArgumentRule.php @@ -22,11 +22,9 @@ final class UndefinedArgumentRule implements Rule { - /** @var ConsoleApplicationResolver */ - private $consoleApplicationResolver; + private ConsoleApplicationResolver $consoleApplicationResolver; - /** @var Standard */ - private $printer; + private Standard $printer; public function __construct(ConsoleApplicationResolver $consoleApplicationResolver, Standard $printer) { diff --git a/src/Rules/Symfony/UndefinedOptionRule.php b/src/Rules/Symfony/UndefinedOptionRule.php index 095bd44d..ae96bfa9 100644 --- a/src/Rules/Symfony/UndefinedOptionRule.php +++ b/src/Rules/Symfony/UndefinedOptionRule.php @@ -22,11 +22,9 @@ final class UndefinedOptionRule implements Rule { - /** @var ConsoleApplicationResolver */ - private $consoleApplicationResolver; + private ConsoleApplicationResolver $consoleApplicationResolver; - /** @var Standard */ - private $printer; + private Standard $printer; public function __construct(ConsoleApplicationResolver $consoleApplicationResolver, Standard $printer) { diff --git a/src/Symfony/Configuration.php b/src/Symfony/Configuration.php index 4c1f1a31..e603b333 100644 --- a/src/Symfony/Configuration.php +++ b/src/Symfony/Configuration.php @@ -6,7 +6,7 @@ final class Configuration { /** @var array */ - private $parameters; + private array $parameters; /** * @param array $parameters diff --git a/src/Symfony/ConsoleApplicationResolver.php b/src/Symfony/ConsoleApplicationResolver.php index 52f5f4f0..a29740b7 100644 --- a/src/Symfony/ConsoleApplicationResolver.php +++ b/src/Symfony/ConsoleApplicationResolver.php @@ -16,11 +16,9 @@ final class ConsoleApplicationResolver { - /** @var string|null */ - private $consoleApplicationLoader; + private ?string $consoleApplicationLoader = null; - /** @var Application|null */ - private $consoleApplication; + private ?Application $consoleApplication = null; public function __construct(Configuration $configuration) { diff --git a/src/Symfony/DefaultParameterMap.php b/src/Symfony/DefaultParameterMap.php index 26317468..8d0af459 100644 --- a/src/Symfony/DefaultParameterMap.php +++ b/src/Symfony/DefaultParameterMap.php @@ -12,7 +12,7 @@ final class DefaultParameterMap implements ParameterMap { /** @var ParameterDefinition[] */ - private $parameters; + private array $parameters; /** * @param ParameterDefinition[] $parameters @@ -39,9 +39,7 @@ public static function getParameterKeysFromNode(Expr $node, Scope $scope): array { $strings = TypeUtils::getConstantStrings($scope->getType($node)); - return array_map(static function (Type $type) { - return $type->getValue(); - }, $strings); + return array_map(static fn (Type $type) => $type->getValue(), $strings); } } diff --git a/src/Symfony/DefaultServiceMap.php b/src/Symfony/DefaultServiceMap.php index cb8259d2..edf4dfd6 100644 --- a/src/Symfony/DefaultServiceMap.php +++ b/src/Symfony/DefaultServiceMap.php @@ -11,7 +11,7 @@ final class DefaultServiceMap implements ServiceMap { /** @var ServiceDefinition[] */ - private $services; + private array $services; /** * @param ServiceDefinition[] $services diff --git a/src/Symfony/InputBagStubFilesExtension.php b/src/Symfony/InputBagStubFilesExtension.php index 140dae99..6ce36f4b 100644 --- a/src/Symfony/InputBagStubFilesExtension.php +++ b/src/Symfony/InputBagStubFilesExtension.php @@ -9,8 +9,7 @@ class InputBagStubFilesExtension implements StubFilesExtension { - /** @var Reflector */ - private $reflector; + private Reflector $reflector; public function __construct( Reflector $reflector diff --git a/src/Symfony/Parameter.php b/src/Symfony/Parameter.php index 8ff3f7a8..53b53265 100644 --- a/src/Symfony/Parameter.php +++ b/src/Symfony/Parameter.php @@ -5,8 +5,7 @@ final class Parameter implements ParameterDefinition { - /** @var string */ - private $key; + private string $key; /** @var array|bool|float|int|string */ private $value; diff --git a/src/Symfony/PasswordAuthenticatedUserStubFilesExtension.php b/src/Symfony/PasswordAuthenticatedUserStubFilesExtension.php index 8f8c4782..42bbf9e7 100644 --- a/src/Symfony/PasswordAuthenticatedUserStubFilesExtension.php +++ b/src/Symfony/PasswordAuthenticatedUserStubFilesExtension.php @@ -9,8 +9,7 @@ class PasswordAuthenticatedUserStubFilesExtension implements StubFilesExtension { - /** @var Reflector */ - private $reflector; + private Reflector $reflector; public function __construct( Reflector $reflector diff --git a/src/Symfony/RequiredAutowiringExtension.php b/src/Symfony/RequiredAutowiringExtension.php index a3cc6a31..7d8d195d 100644 --- a/src/Symfony/RequiredAutowiringExtension.php +++ b/src/Symfony/RequiredAutowiringExtension.php @@ -13,8 +13,7 @@ class RequiredAutowiringExtension implements ReadWritePropertiesExtension, AdditionalConstructorsExtension { - /** @var FileTypeMapper */ - private $fileTypeMapper; + private FileTypeMapper $fileTypeMapper; public function __construct(FileTypeMapper $fileTypeMapper) { diff --git a/src/Symfony/Service.php b/src/Symfony/Service.php index c31324f5..881787f5 100644 --- a/src/Symfony/Service.php +++ b/src/Symfony/Service.php @@ -5,20 +5,15 @@ final class Service implements ServiceDefinition { - /** @var string */ - private $id; + private string $id; - /** @var string|null */ - private $class; + private ?string $class = null; - /** @var bool */ - private $public; + private bool $public; - /** @var bool */ - private $synthetic; + private bool $synthetic; - /** @var string|null */ - private $alias; + private ?string $alias = null; public function __construct( string $id, diff --git a/src/Symfony/SymfonyDiagnoseExtension.php b/src/Symfony/SymfonyDiagnoseExtension.php index afd566dc..38b19754 100644 --- a/src/Symfony/SymfonyDiagnoseExtension.php +++ b/src/Symfony/SymfonyDiagnoseExtension.php @@ -9,8 +9,7 @@ class SymfonyDiagnoseExtension implements DiagnoseExtension { - /** @var ConsoleApplicationResolver */ - private $consoleApplicationResolver; + private ConsoleApplicationResolver $consoleApplicationResolver; public function __construct(ConsoleApplicationResolver $consoleApplicationResolver) { @@ -21,7 +20,7 @@ public function print(Output $output): void { $output->writeLineFormatted(sprintf( 'Symfony\'s consoleApplicationLoader: %s', - $this->consoleApplicationResolver->hasConsoleApplicationLoader() ? 'In use' : 'No' + $this->consoleApplicationResolver->hasConsoleApplicationLoader() ? 'In use' : 'No', )); $output->writeLineFormatted(''); } diff --git a/src/Symfony/XmlParameterMapFactory.php b/src/Symfony/XmlParameterMapFactory.php index 1467d050..afb58ba8 100644 --- a/src/Symfony/XmlParameterMapFactory.php +++ b/src/Symfony/XmlParameterMapFactory.php @@ -15,8 +15,7 @@ final class XmlParameterMapFactory implements ParameterMapFactory { - /** @var string|null */ - private $containerXml; + private ?string $containerXml = null; public function __construct(Configuration $configuration) { @@ -47,7 +46,7 @@ public function create(): ParameterMap $parameter = new Parameter( (string) $attrs->key, - $this->getNodeValue($def) + $this->getNodeValue($def), ); $parameters[$parameter->getKey()] = $parameter; diff --git a/src/Symfony/XmlServiceMapFactory.php b/src/Symfony/XmlServiceMapFactory.php index 1cae5d97..8d2ba919 100644 --- a/src/Symfony/XmlServiceMapFactory.php +++ b/src/Symfony/XmlServiceMapFactory.php @@ -12,8 +12,7 @@ final class XmlServiceMapFactory implements ServiceMapFactory { - /** @var string|null */ - private $containerXml; + private ?string $containerXml = null; public function __construct(Configuration $configuration) { @@ -52,7 +51,7 @@ public function create(): ServiceMap isset($attrs->class) ? (string) $attrs->class : null, isset($attrs->public) && (string) $attrs->public === 'true', isset($attrs->synthetic) && (string) $attrs->synthetic === 'true', - isset($attrs->alias) ? $this->cleanServiceId((string) $attrs->alias) : null + isset($attrs->alias) ? $this->cleanServiceId((string) $attrs->alias) : null, ); if ($service->getAlias() !== null) { @@ -72,7 +71,7 @@ public function create(): ServiceMap $services[$alias]->getClass(), $service->isPublic(), $service->isSynthetic(), - $alias + $alias, ); } diff --git a/src/Type/Symfony/ArgumentTypeSpecifyingExtension.php b/src/Type/Symfony/ArgumentTypeSpecifyingExtension.php index b57f3c35..f9aafb23 100644 --- a/src/Type/Symfony/ArgumentTypeSpecifyingExtension.php +++ b/src/Type/Symfony/ArgumentTypeSpecifyingExtension.php @@ -15,11 +15,9 @@ final class ArgumentTypeSpecifyingExtension implements MethodTypeSpecifyingExtension, TypeSpecifierAwareExtension { - /** @var Standard */ - private $printer; + private Standard $printer; - /** @var TypeSpecifier */ - private $typeSpecifier; + private TypeSpecifier $typeSpecifier; public function __construct(Standard $printer) { @@ -45,7 +43,7 @@ public function specifyTypes(MethodReflection $methodReflection, MethodCall $nod return $this->typeSpecifier->create( Helper::createMarkerNode($node->var, $argType, $this->printer), $argType, - $context + $context, ); } diff --git a/src/Type/Symfony/CacheInterfaceGetDynamicReturnTypeExtension.php b/src/Type/Symfony/CacheInterfaceGetDynamicReturnTypeExtension.php index ce736bfe..0862ce61 100644 --- a/src/Type/Symfony/CacheInterfaceGetDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/CacheInterfaceGetDynamicReturnTypeExtension.php @@ -34,7 +34,7 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method $parametersAcceptor = ParametersAcceptorSelector::selectFromArgs( $scope, $methodCall->getArgs(), - $callbackReturnType->getCallableParametersAcceptors($scope) + $callbackReturnType->getCallableParametersAcceptors($scope), ); $returnType = $parametersAcceptor->getReturnType(); diff --git a/src/Type/Symfony/CommandGetHelperDynamicReturnTypeExtension.php b/src/Type/Symfony/CommandGetHelperDynamicReturnTypeExtension.php index 3b38258c..c59ba0dd 100644 --- a/src/Type/Symfony/CommandGetHelperDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/CommandGetHelperDynamicReturnTypeExtension.php @@ -18,8 +18,7 @@ final class CommandGetHelperDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension { - /** @var ConsoleApplicationResolver */ - private $consoleApplicationResolver; + private ConsoleApplicationResolver $consoleApplicationResolver; public function __construct(ConsoleApplicationResolver $consoleApplicationResolver) { diff --git a/src/Type/Symfony/Config/ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php index 828b000e..da0acd65 100644 --- a/src/Type/Symfony/Config/ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/Config/ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php @@ -72,7 +72,7 @@ public function getTypeFromMethodCall( return new ParentObjectType( $defaultType->describe(VerbosityLevel::typeOnly()), - $calledOnType + $calledOnType, ); } diff --git a/src/Type/Symfony/Config/PassParentObjectDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/PassParentObjectDynamicReturnTypeExtension.php index 6ef28bbd..c1750d00 100644 --- a/src/Type/Symfony/Config/PassParentObjectDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/Config/PassParentObjectDynamicReturnTypeExtension.php @@ -15,11 +15,10 @@ final class PassParentObjectDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension { - /** @var string */ - private $className; + private string $className; /** @var string[] */ - private $methods; + private array $methods; /** * @param string[] $methods diff --git a/src/Type/Symfony/Config/ReturnParentDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/ReturnParentDynamicReturnTypeExtension.php index 4babdcd7..32b9ecd8 100644 --- a/src/Type/Symfony/Config/ReturnParentDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/Config/ReturnParentDynamicReturnTypeExtension.php @@ -14,11 +14,10 @@ final class ReturnParentDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension { - /** @var string */ - private $className; + private string $className; /** @var string[] */ - private $methods; + private array $methods; /** * @param string[] $methods diff --git a/src/Type/Symfony/Config/TreeBuilderGetRootNodeDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/TreeBuilderGetRootNodeDynamicReturnTypeExtension.php index 4b7c1b3a..b25ceaf4 100644 --- a/src/Type/Symfony/Config/TreeBuilderGetRootNodeDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/Config/TreeBuilderGetRootNodeDynamicReturnTypeExtension.php @@ -37,7 +37,7 @@ public function getTypeFromMethodCall( if ($calledOnType instanceof TreeBuilderType) { return new ParentObjectType( $calledOnType->getRootNodeClassName(), - $calledOnType + $calledOnType, ); } diff --git a/src/Type/Symfony/Config/ValueObject/ParentObjectType.php b/src/Type/Symfony/Config/ValueObject/ParentObjectType.php index 56aec24f..19baf926 100644 --- a/src/Type/Symfony/Config/ValueObject/ParentObjectType.php +++ b/src/Type/Symfony/Config/ValueObject/ParentObjectType.php @@ -9,8 +9,7 @@ class ParentObjectType extends ObjectType { - /** @var Type */ - private $parent; + private Type $parent; public function __construct(string $className, Type $parent) { diff --git a/src/Type/Symfony/Config/ValueObject/TreeBuilderType.php b/src/Type/Symfony/Config/ValueObject/TreeBuilderType.php index 71bc4fc2..93c713f1 100644 --- a/src/Type/Symfony/Config/ValueObject/TreeBuilderType.php +++ b/src/Type/Symfony/Config/ValueObject/TreeBuilderType.php @@ -7,8 +7,7 @@ class TreeBuilderType extends ObjectType { - /** @var string */ - private $rootNodeClassName; + private string $rootNodeClassName; public function __construct(string $className, string $rootNodeClassName) { diff --git a/src/Type/Symfony/EnvelopeReturnTypeExtension.php b/src/Type/Symfony/EnvelopeReturnTypeExtension.php index 2e01dec7..23b077f2 100644 --- a/src/Type/Symfony/EnvelopeReturnTypeExtension.php +++ b/src/Type/Symfony/EnvelopeReturnTypeExtension.php @@ -37,7 +37,7 @@ public function getTypeFromMethodCall( if (count($methodCall->getArgs()) === 0) { return new ArrayType( new GenericClassStringType(new ObjectType('Symfony\Component\Messenger\Stamp\StampInterface')), - AccessoryArrayListType::intersectWith(new ArrayType(new IntegerType(), new ObjectType('Symfony\Component\Messenger\Stamp\StampInterface'))) + AccessoryArrayListType::intersectWith(new ArrayType(new IntegerType(), new ObjectType('Symfony\Component\Messenger\Stamp\StampInterface'))), ); } diff --git a/src/Type/Symfony/ExtensionGetConfigurationReturnTypeExtension.php b/src/Type/Symfony/ExtensionGetConfigurationReturnTypeExtension.php index 7af50773..d975d9d1 100644 --- a/src/Type/Symfony/ExtensionGetConfigurationReturnTypeExtension.php +++ b/src/Type/Symfony/ExtensionGetConfigurationReturnTypeExtension.php @@ -19,8 +19,7 @@ class ExtensionGetConfigurationReturnTypeExtension implements DynamicMethodReturnTypeExtension { - /** @var ReflectionProvider */ - private $reflectionProvider; + private ReflectionProvider $reflectionProvider; public function __construct(ReflectionProvider $reflectionProvider) { diff --git a/src/Type/Symfony/Helper.php b/src/Type/Symfony/Helper.php index 099b64c2..4aad820c 100644 --- a/src/Type/Symfony/Helper.php +++ b/src/Type/Symfony/Helper.php @@ -17,7 +17,7 @@ public static function createMarkerNode(Expr $expr, Type $type, PrettyPrinterAbs return new Expr\Variable(md5(sprintf( '%s::%s', $printer->prettyPrintExpr($expr), - $type->describe(VerbosityLevel::precise()) + $type->describe(VerbosityLevel::precise()), ))); } diff --git a/src/Type/Symfony/InputBagTypeSpecifyingExtension.php b/src/Type/Symfony/InputBagTypeSpecifyingExtension.php index d3ad578c..a11540bf 100644 --- a/src/Type/Symfony/InputBagTypeSpecifyingExtension.php +++ b/src/Type/Symfony/InputBagTypeSpecifyingExtension.php @@ -20,8 +20,7 @@ final class InputBagTypeSpecifyingExtension implements MethodTypeSpecifyingExten private const HAS_METHOD_NAME = 'has'; private const GET_METHOD_NAME = 'get'; - /** @var TypeSpecifier */ - private $typeSpecifier; + private TypeSpecifier $typeSpecifier; public function getClass(): string { @@ -38,7 +37,7 @@ public function specifyTypes(MethodReflection $methodReflection, MethodCall $nod return $this->typeSpecifier->create( new MethodCall($node->var, self::GET_METHOD_NAME, $node->getArgs()), new NullType(), - $context->negate() + $context->negate(), ); } diff --git a/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php index 5eb4cfe9..71e71748 100644 --- a/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php @@ -21,8 +21,7 @@ final class InputInterfaceGetArgumentDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension { - /** @var ConsoleApplicationResolver */ - private $consoleApplicationResolver; + private ConsoleApplicationResolver $consoleApplicationResolver; public function __construct(ConsoleApplicationResolver $consoleApplicationResolver) { diff --git a/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php index d765f65b..223b77cf 100644 --- a/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php @@ -16,11 +16,9 @@ final class InputInterfaceGetOptionDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension { - /** @var ConsoleApplicationResolver */ - private $consoleApplicationResolver; + private ConsoleApplicationResolver $consoleApplicationResolver; - /** @var GetOptionTypeHelper */ - private $getOptionTypeHelper; + private GetOptionTypeHelper $getOptionTypeHelper; public function __construct(ConsoleApplicationResolver $consoleApplicationResolver, GetOptionTypeHelper $getOptionTypeHelper) { diff --git a/src/Type/Symfony/InputInterfaceGetOptionsDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceGetOptionsDynamicReturnTypeExtension.php index 515c44d0..3105621d 100644 --- a/src/Type/Symfony/InputInterfaceGetOptionsDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/InputInterfaceGetOptionsDynamicReturnTypeExtension.php @@ -17,11 +17,9 @@ final class InputInterfaceGetOptionsDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension { - /** @var ConsoleApplicationResolver */ - private $consoleApplicationResolver; + private ConsoleApplicationResolver $consoleApplicationResolver; - /** @var GetOptionTypeHelper */ - private $getOptionTypeHelper; + private GetOptionTypeHelper $getOptionTypeHelper; public function __construct(ConsoleApplicationResolver $consoleApplicationResolver, GetOptionTypeHelper $getOptionTypeHelper) { diff --git a/src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php index 2ca6b4a1..91611838 100644 --- a/src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php @@ -18,8 +18,7 @@ final class InputInterfaceHasArgumentDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension { - /** @var ConsoleApplicationResolver */ - private $consoleApplicationResolver; + private ConsoleApplicationResolver $consoleApplicationResolver; public function __construct(ConsoleApplicationResolver $consoleApplicationResolver) { diff --git a/src/Type/Symfony/InputInterfaceHasOptionDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceHasOptionDynamicReturnTypeExtension.php index 3be621f8..c25e646a 100644 --- a/src/Type/Symfony/InputInterfaceHasOptionDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/InputInterfaceHasOptionDynamicReturnTypeExtension.php @@ -17,8 +17,7 @@ final class InputInterfaceHasOptionDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension { - /** @var ConsoleApplicationResolver */ - private $consoleApplicationResolver; + private ConsoleApplicationResolver $consoleApplicationResolver; public function __construct(ConsoleApplicationResolver $consoleApplicationResolver) { diff --git a/src/Type/Symfony/OptionTypeSpecifyingExtension.php b/src/Type/Symfony/OptionTypeSpecifyingExtension.php index 98e0e3fd..c8f49bb8 100644 --- a/src/Type/Symfony/OptionTypeSpecifyingExtension.php +++ b/src/Type/Symfony/OptionTypeSpecifyingExtension.php @@ -15,11 +15,9 @@ final class OptionTypeSpecifyingExtension implements MethodTypeSpecifyingExtension, TypeSpecifierAwareExtension { - /** @var Standard */ - private $printer; + private Standard $printer; - /** @var TypeSpecifier */ - private $typeSpecifier; + private TypeSpecifier $typeSpecifier; public function __construct(Standard $printer) { @@ -45,7 +43,7 @@ public function specifyTypes(MethodReflection $methodReflection, MethodCall $nod return $this->typeSpecifier->create( Helper::createMarkerNode($node->var, $argType, $this->printer), $argType, - $context + $context, ); } diff --git a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php index 82535133..7b70b443 100644 --- a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php @@ -44,23 +44,17 @@ final class ParameterDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension { - /** @var string */ - private $className; + private string $className; - /** @var string|null */ - private $methodGet; + private ?string $methodGet = null; - /** @var string|null */ - private $methodHas; + private ?string $methodHas = null; - /** @var bool */ - private $constantHassers; + private bool $constantHassers; - /** @var ParameterMap */ - private $parameterMap; + private ParameterMap $parameterMap; - /** @var TypeStringResolver */ - private $typeStringResolver; + private TypeStringResolver $typeStringResolver; public function __construct( string $className, @@ -86,9 +80,7 @@ public function getClass(): string public function isMethodSupported(MethodReflection $methodReflection): bool { - $methods = array_filter([$this->methodGet, $this->methodHas], static function (?string $method): bool { - return $method !== null; - }); + $methods = array_filter([$this->methodGet, $this->methodHas], static fn (?string $method): bool => $method !== null); return in_array($methodReflection->getName(), $methods, true); } @@ -170,17 +162,13 @@ private function generalizeTypeFromValue(Scope $scope, $value): Type } return ConstantArrayTypeBuilder::createFromConstantArray( - new ConstantArrayType($keyTypes, $valueTypes) + new ConstantArrayType($keyTypes, $valueTypes), )->getArray(); } return new ArrayType( - TypeCombinator::union(...array_map(function ($item) use ($scope): Type { - return $this->generalizeTypeFromValue($scope, $item); - }, array_keys($value))), - TypeCombinator::union(...array_map(function ($item) use ($scope): Type { - return $this->generalizeTypeFromValue($scope, $item); - }, array_values($value))) + TypeCombinator::union(...array_map(fn ($item): Type => $this->generalizeTypeFromValue($scope, $item), array_keys($value))), + TypeCombinator::union(...array_map(fn ($item): Type => $this->generalizeTypeFromValue($scope, $item), array_values($value))), ); } diff --git a/src/Type/Symfony/RequestTypeSpecifyingExtension.php b/src/Type/Symfony/RequestTypeSpecifyingExtension.php index ffb2e8f3..793e928f 100644 --- a/src/Type/Symfony/RequestTypeSpecifyingExtension.php +++ b/src/Type/Symfony/RequestTypeSpecifyingExtension.php @@ -20,8 +20,7 @@ final class RequestTypeSpecifyingExtension implements MethodTypeSpecifyingExtens private const HAS_METHOD_NAME = 'hasSession'; private const GET_METHOD_NAME = 'getSession'; - /** @var TypeSpecifier */ - private $typeSpecifier; + private TypeSpecifier $typeSpecifier; public function getClass(): string { @@ -45,7 +44,7 @@ public function specifyTypes(MethodReflection $methodReflection, MethodCall $nod return $this->typeSpecifier->create( new MethodCall($node->var, self::GET_METHOD_NAME), TypeCombinator::removeNull($returnType), - $context + $context, ); } diff --git a/src/Type/Symfony/ResponseHeaderBagDynamicReturnTypeExtension.php b/src/Type/Symfony/ResponseHeaderBagDynamicReturnTypeExtension.php index a659c684..5a95bf98 100644 --- a/src/Type/Symfony/ResponseHeaderBagDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/ResponseHeaderBagDynamicReturnTypeExtension.php @@ -52,9 +52,9 @@ public function getTypeFromMethodCall( new StringType(), new ArrayType( new StringType(), - new ObjectType(Cookie::class) - ) - ) + new ObjectType(Cookie::class), + ), + ), ); } } diff --git a/src/Type/Symfony/SerializerDynamicReturnTypeExtension.php b/src/Type/Symfony/SerializerDynamicReturnTypeExtension.php index d3ea959d..2b689fbb 100755 --- a/src/Type/Symfony/SerializerDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/SerializerDynamicReturnTypeExtension.php @@ -17,11 +17,9 @@ class SerializerDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension { - /** @var string */ - private $class; + private string $class; - /** @var string */ - private $method; + private string $method; public function __construct(string $class, string $method) { diff --git a/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php b/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php index b0101cf4..c8c49a89 100644 --- a/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php @@ -23,20 +23,15 @@ final class ServiceDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension { - /** @var string */ - private $className; + private string $className; - /** @var bool */ - private $constantHassers; + private bool $constantHassers; - /** @var ServiceMap */ - private $serviceMap; + private ServiceMap $serviceMap; - /** @var ParameterMap */ - private $parameterMap; + private ParameterMap $parameterMap; - /** @var ParameterBag|null */ - private $parameterBag; + private ?ParameterBag $parameterBag = null; public function __construct( string $className, diff --git a/src/Type/Symfony/ServiceTypeSpecifyingExtension.php b/src/Type/Symfony/ServiceTypeSpecifyingExtension.php index b52c1531..524e6132 100644 --- a/src/Type/Symfony/ServiceTypeSpecifyingExtension.php +++ b/src/Type/Symfony/ServiceTypeSpecifyingExtension.php @@ -15,14 +15,11 @@ final class ServiceTypeSpecifyingExtension implements MethodTypeSpecifyingExtension, TypeSpecifierAwareExtension { - /** @var string */ - private $className; + private string $className; - /** @var Standard */ - private $printer; + private Standard $printer; - /** @var TypeSpecifier */ - private $typeSpecifier; + private TypeSpecifier $typeSpecifier; public function __construct(string $className, Standard $printer) { @@ -49,7 +46,7 @@ public function specifyTypes(MethodReflection $methodReflection, MethodCall $nod return $this->typeSpecifier->create( Helper::createMarkerNode($node->var, $argType, $this->printer), $argType, - $context + $context, ); } diff --git a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleFakeTest.php b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleFakeTest.php index 4551accd..e4cb4bea 100644 --- a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleFakeTest.php +++ b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleFakeTest.php @@ -29,7 +29,7 @@ public function testGetPrivateService(): void [ __DIR__ . '/ExampleController.php', ], - [] + [], ); } @@ -42,7 +42,7 @@ public function testGetPrivateServiceInAbstractController(): void [ __DIR__ . '/ExampleAbstractController.php', ], - [] + [], ); } @@ -62,7 +62,7 @@ public function testGetPrivateServiceInLegacyServiceSubscriber(): void __DIR__ . '/ExampleLegacyServiceSubscriberFromAbstractController.php', __DIR__ . '/ExampleLegacyServiceSubscriberFromLegacyController.php', ], - [] + [], ); } @@ -82,7 +82,7 @@ public function testGetPrivateServiceInServiceSubscriber(): void __DIR__ . '/ExampleServiceSubscriberFromAbstractController.php', __DIR__ . '/ExampleServiceSubscriberFromLegacyController.php', ], - [] + [], ); } diff --git a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php index 51513b09..8c5efe8d 100644 --- a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php +++ b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php @@ -34,7 +34,7 @@ public function testGetPrivateService(): void 'Service "private" is private.', 13, ], - ] + ], ); } @@ -54,7 +54,7 @@ public function testGetPrivateServiceInLegacyServiceSubscriber(): void __DIR__ . '/ExampleLegacyServiceSubscriberFromAbstractController.php', __DIR__ . '/ExampleLegacyServiceSubscriberFromLegacyController.php', ], - [] + [], ); } @@ -74,7 +74,7 @@ public function testGetPrivateServiceInServiceSubscriber(): void __DIR__ . '/ExampleServiceSubscriberFromAbstractController.php', __DIR__ . '/ExampleServiceSubscriberFromLegacyController.php', ], - [] + [], ); } diff --git a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php index f975a179..f2f74e55 100644 --- a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php +++ b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php @@ -41,7 +41,7 @@ public function testGetPrivateService(): void [ __DIR__ . '/ExampleController.php', ], - [] + [], ); } @@ -55,7 +55,7 @@ public function testGetPrivateServiceInAbstractController(): void [ __DIR__ . '/ExampleAbstractController.php', ], - [] + [], ); } diff --git a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php index 4bd233e9..323065b0 100644 --- a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php +++ b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php @@ -35,7 +35,7 @@ public function testGetPrivateService(): void 'Service "unknown" is not registered in the container.', 25, ], - ] + ], ); } @@ -54,7 +54,7 @@ public function testGetPrivateServiceInAbstractController(): void 'Service "unknown" is not registered in the container.', 25, ], - ] + ], ); } @@ -68,7 +68,7 @@ public function testGetPrivateServiceInLegacyServiceSubscriber(): void [ __DIR__ . '/ExampleServiceSubscriber.php', ], - [] + [], ); } diff --git a/tests/Rules/Symfony/ExampleServiceSubscriber.php b/tests/Rules/Symfony/ExampleServiceSubscriber.php index 9005cf60..ec9c966d 100644 --- a/tests/Rules/Symfony/ExampleServiceSubscriber.php +++ b/tests/Rules/Symfony/ExampleServiceSubscriber.php @@ -9,8 +9,7 @@ final class ExampleServiceSubscriber implements ServiceSubscriberInterface { - /** @var ContainerInterface */ - private $locator; + private ContainerInterface $locator; public function __construct(ContainerInterface $locator) { diff --git a/tests/Rules/Symfony/InvalidArgumentDefaultValueRuleTest.php b/tests/Rules/Symfony/InvalidArgumentDefaultValueRuleTest.php index 5e4f2ee6..bc6a6563 100644 --- a/tests/Rules/Symfony/InvalidArgumentDefaultValueRuleTest.php +++ b/tests/Rules/Symfony/InvalidArgumentDefaultValueRuleTest.php @@ -35,7 +35,7 @@ public function testGetArgument(): void 'Parameter #4 $default of method Symfony\Component\Console\Command\Command::addArgument() expects array|null, array given.', 25, ], - ] + ], ); } diff --git a/tests/Rules/Symfony/InvalidOptionDefaultValueRuleTest.php b/tests/Rules/Symfony/InvalidOptionDefaultValueRuleTest.php index 3e5e03d4..2dcbbcd1 100644 --- a/tests/Rules/Symfony/InvalidOptionDefaultValueRuleTest.php +++ b/tests/Rules/Symfony/InvalidOptionDefaultValueRuleTest.php @@ -27,7 +27,7 @@ public function testGetArgument(): void 'Parameter #5 $default of method Symfony\Component\Console\Command\Command::addOption() expects array|null, array given.', 29, ], - ] + ], ); } diff --git a/tests/Rules/Symfony/UndefinedArgumentRuleTest.php b/tests/Rules/Symfony/UndefinedArgumentRuleTest.php index 95f4b733..6d25bf4a 100644 --- a/tests/Rules/Symfony/UndefinedArgumentRuleTest.php +++ b/tests/Rules/Symfony/UndefinedArgumentRuleTest.php @@ -30,7 +30,7 @@ public function testGetArgument(): void 'Command "example-rule" does not define argument "undefined".', 42, ], - ] + ], ); } diff --git a/tests/Rules/Symfony/UndefinedOptionRuleTest.php b/tests/Rules/Symfony/UndefinedOptionRuleTest.php index b3cbe0ef..50d38a83 100644 --- a/tests/Rules/Symfony/UndefinedOptionRuleTest.php +++ b/tests/Rules/Symfony/UndefinedOptionRuleTest.php @@ -30,7 +30,7 @@ public function testGetArgument(): void 'Command "example-rule" does not define option "bbb".', 49, ], - ] + ], ); } diff --git a/tests/Type/Symfony/console_application_loader.php b/tests/Type/Symfony/console_application_loader.php index b6755aea..fb5459b5 100644 --- a/tests/Type/Symfony/console_application_loader.php +++ b/tests/Type/Symfony/console_application_loader.php @@ -15,9 +15,7 @@ $application->add(new ExampleOptionCommand()); if (class_exists(LazyCommand::class)) { - $application->add(new LazyCommand('lazy-example-option', [], '', false, static function () { - return new ExampleOptionLazyCommand(); - })); + $application->add(new LazyCommand('lazy-example-option', [], '', false, static fn () => new ExampleOptionLazyCommand())); } else { $application->add(new ExampleOptionLazyCommand()); } From c88f96e66158021ee84b65f2d50a07002bd93645 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Fri, 6 Sep 2024 14:42:57 +0200 Subject: [PATCH 176/208] Drop dependency on nikic/php-parser --- composer.json | 1 - 1 file changed, 1 deletion(-) diff --git a/composer.json b/composer.json index ffb9f3b8..6cf39b10 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,6 @@ "symfony/framework-bundle": "<3.0" }, "require-dev": { - "nikic/php-parser": "^4.13.0", "php-parallel-lint/php-parallel-lint": "^1.2", "phpstan/phpstan-phpunit": "^2.0", "phpstan/phpstan-strict-rules": "^2.0", From 23a776d0326e4f2c0cf2bbd6f6b28bc32cc45b46 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Fri, 13 Sep 2024 14:54:12 +0200 Subject: [PATCH 177/208] Fixes after TypeSpecifier BC break --- src/Type/Symfony/ArgumentTypeSpecifyingExtension.php | 1 + src/Type/Symfony/InputBagTypeSpecifyingExtension.php | 1 + src/Type/Symfony/OptionTypeSpecifyingExtension.php | 1 + src/Type/Symfony/RequestTypeSpecifyingExtension.php | 1 + src/Type/Symfony/ServiceTypeSpecifyingExtension.php | 1 + 5 files changed, 5 insertions(+) diff --git a/src/Type/Symfony/ArgumentTypeSpecifyingExtension.php b/src/Type/Symfony/ArgumentTypeSpecifyingExtension.php index f9aafb23..019890c8 100644 --- a/src/Type/Symfony/ArgumentTypeSpecifyingExtension.php +++ b/src/Type/Symfony/ArgumentTypeSpecifyingExtension.php @@ -44,6 +44,7 @@ public function specifyTypes(MethodReflection $methodReflection, MethodCall $nod Helper::createMarkerNode($node->var, $argType, $this->printer), $argType, $context, + $scope, ); } diff --git a/src/Type/Symfony/InputBagTypeSpecifyingExtension.php b/src/Type/Symfony/InputBagTypeSpecifyingExtension.php index a11540bf..72e3bc3f 100644 --- a/src/Type/Symfony/InputBagTypeSpecifyingExtension.php +++ b/src/Type/Symfony/InputBagTypeSpecifyingExtension.php @@ -38,6 +38,7 @@ public function specifyTypes(MethodReflection $methodReflection, MethodCall $nod new MethodCall($node->var, self::GET_METHOD_NAME, $node->getArgs()), new NullType(), $context->negate(), + $scope, ); } diff --git a/src/Type/Symfony/OptionTypeSpecifyingExtension.php b/src/Type/Symfony/OptionTypeSpecifyingExtension.php index c8f49bb8..e8530417 100644 --- a/src/Type/Symfony/OptionTypeSpecifyingExtension.php +++ b/src/Type/Symfony/OptionTypeSpecifyingExtension.php @@ -44,6 +44,7 @@ public function specifyTypes(MethodReflection $methodReflection, MethodCall $nod Helper::createMarkerNode($node->var, $argType, $this->printer), $argType, $context, + $scope, ); } diff --git a/src/Type/Symfony/RequestTypeSpecifyingExtension.php b/src/Type/Symfony/RequestTypeSpecifyingExtension.php index 793e928f..40d38493 100644 --- a/src/Type/Symfony/RequestTypeSpecifyingExtension.php +++ b/src/Type/Symfony/RequestTypeSpecifyingExtension.php @@ -45,6 +45,7 @@ public function specifyTypes(MethodReflection $methodReflection, MethodCall $nod new MethodCall($node->var, self::GET_METHOD_NAME), TypeCombinator::removeNull($returnType), $context, + $scope, ); } diff --git a/src/Type/Symfony/ServiceTypeSpecifyingExtension.php b/src/Type/Symfony/ServiceTypeSpecifyingExtension.php index 524e6132..86dc4efc 100644 --- a/src/Type/Symfony/ServiceTypeSpecifyingExtension.php +++ b/src/Type/Symfony/ServiceTypeSpecifyingExtension.php @@ -47,6 +47,7 @@ public function specifyTypes(MethodReflection $methodReflection, MethodCall $nod Helper::createMarkerNode($node->var, $argType, $this->printer), $argType, $context, + $scope, ); } From bd9efb75ec99a20298ff04c167e9c2f599ae734d Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Tue, 24 Sep 2024 20:53:44 +0200 Subject: [PATCH 178/208] Fixes after PHPStan update --- src/Type/Symfony/EnvelopeReturnTypeExtension.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Type/Symfony/EnvelopeReturnTypeExtension.php b/src/Type/Symfony/EnvelopeReturnTypeExtension.php index 23b077f2..06e08772 100644 --- a/src/Type/Symfony/EnvelopeReturnTypeExtension.php +++ b/src/Type/Symfony/EnvelopeReturnTypeExtension.php @@ -37,13 +37,13 @@ public function getTypeFromMethodCall( if (count($methodCall->getArgs()) === 0) { return new ArrayType( new GenericClassStringType(new ObjectType('Symfony\Component\Messenger\Stamp\StampInterface')), - AccessoryArrayListType::intersectWith(new ArrayType(new IntegerType(), new ObjectType('Symfony\Component\Messenger\Stamp\StampInterface'))), + TypeCombinator::intersect(new ArrayType(new IntegerType(), new ObjectType('Symfony\Component\Messenger\Stamp\StampInterface')), new AccessoryArrayListType()), ); } $argType = $scope->getType($methodCall->getArgs()[0]->value); if (count($argType->getConstantStrings()) === 0) { - return AccessoryArrayListType::intersectWith(new ArrayType(new IntegerType(), new ObjectType('Symfony\Component\Messenger\Stamp\StampInterface'))); + return TypeCombinator::intersect(new ArrayType(new IntegerType(), new ObjectType('Symfony\Component\Messenger\Stamp\StampInterface')), new AccessoryArrayListType()); } $objectTypes = []; @@ -51,7 +51,7 @@ public function getTypeFromMethodCall( $objectTypes[] = new ObjectType($constantString->getValue()); } - return AccessoryArrayListType::intersectWith(new ArrayType(new IntegerType(), TypeCombinator::union(...$objectTypes))); + return TypeCombinator::intersect(new ArrayType(new IntegerType(), TypeCombinator::union(...$objectTypes)), new AccessoryArrayListType()); } } From 3eb61a05bf872bfb8a5663b75df5a1a22270086b Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Thu, 26 Sep 2024 15:01:53 +0200 Subject: [PATCH 179/208] Fixes after PHPStan update --- ...ionPrototypeDynamicReturnTypeExtension.php | 6 +++++- ...ParentObjectDynamicReturnTypeExtension.php | 6 +++++- ...ReturnParentDynamicReturnTypeExtension.php | 5 ++--- ...rGetRootNodeDynamicReturnTypeExtension.php | 8 ++------ .../HeaderBagDynamicReturnTypeExtension.php | 5 ++--- .../InputBagDynamicReturnTypeExtension.php | 12 ++++++++---- ...nelInterfaceDynamicReturnTypeExtension.php | 5 ++--- .../ParameterDynamicReturnTypeExtension.php | 12 +++++------- .../RequestDynamicReturnTypeExtension.php | 5 ++--- .../ServiceDynamicReturnTypeExtension.php | 19 ++++++++----------- 10 files changed, 41 insertions(+), 42 deletions(-) diff --git a/src/Type/Symfony/Config/ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php index da0acd65..73af2d77 100644 --- a/src/Type/Symfony/Config/ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/Config/ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php @@ -55,7 +55,11 @@ public function getTypeFromMethodCall( { $calledOnType = $scope->getType($methodCall->var); - $defaultType = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType(); + $defaultType = ParametersAcceptorSelector::selectFromArgs( + $scope, + $methodCall->getArgs(), + $methodReflection->getVariants(), + )->getReturnType(); if ($methodReflection->getName() === 'prototype') { if (!isset($methodCall->getArgs()[0])) { diff --git a/src/Type/Symfony/Config/PassParentObjectDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/PassParentObjectDynamicReturnTypeExtension.php index c1750d00..9264d17b 100644 --- a/src/Type/Symfony/Config/PassParentObjectDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/Config/PassParentObjectDynamicReturnTypeExtension.php @@ -47,7 +47,11 @@ public function getTypeFromMethodCall( { $calledOnType = $scope->getType($methodCall->var); - $defaultType = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType(); + $defaultType = ParametersAcceptorSelector::selectFromArgs( + $scope, + $methodCall->getArgs(), + $methodReflection->getVariants(), + )->getReturnType(); return new ParentObjectType($defaultType->describe(VerbosityLevel::typeOnly()), $calledOnType); } diff --git a/src/Type/Symfony/Config/ReturnParentDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/ReturnParentDynamicReturnTypeExtension.php index 32b9ecd8..d3351327 100644 --- a/src/Type/Symfony/Config/ReturnParentDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/Config/ReturnParentDynamicReturnTypeExtension.php @@ -5,7 +5,6 @@ use PhpParser\Node\Expr\MethodCall; use PHPStan\Analyser\Scope; use PHPStan\Reflection\MethodReflection; -use PHPStan\Reflection\ParametersAcceptorSelector; use PHPStan\Type\DynamicMethodReturnTypeExtension; use PHPStan\Type\Symfony\Config\ValueObject\ParentObjectType; use PHPStan\Type\Type; @@ -42,14 +41,14 @@ public function getTypeFromMethodCall( MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope - ): Type + ): ?Type { $calledOnType = $scope->getType($methodCall->var); if ($calledOnType instanceof ParentObjectType) { return $calledOnType->getParent(); } - return ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType(); + return null; } } diff --git a/src/Type/Symfony/Config/TreeBuilderGetRootNodeDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/TreeBuilderGetRootNodeDynamicReturnTypeExtension.php index b25ceaf4..2be2b574 100644 --- a/src/Type/Symfony/Config/TreeBuilderGetRootNodeDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/Config/TreeBuilderGetRootNodeDynamicReturnTypeExtension.php @@ -5,7 +5,6 @@ use PhpParser\Node\Expr\MethodCall; use PHPStan\Analyser\Scope; use PHPStan\Reflection\MethodReflection; -use PHPStan\Reflection\ParametersAcceptorSelector; use PHPStan\Type\DynamicMethodReturnTypeExtension; use PHPStan\Type\Symfony\Config\ValueObject\ParentObjectType; use PHPStan\Type\Symfony\Config\ValueObject\TreeBuilderType; @@ -28,12 +27,9 @@ public function getTypeFromMethodCall( MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope - ): Type + ): ?Type { $calledOnType = $scope->getType($methodCall->var); - - $defaultType = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType(); - if ($calledOnType instanceof TreeBuilderType) { return new ParentObjectType( $calledOnType->getRootNodeClassName(), @@ -41,7 +37,7 @@ public function getTypeFromMethodCall( ); } - return $defaultType; + return null; } } diff --git a/src/Type/Symfony/HeaderBagDynamicReturnTypeExtension.php b/src/Type/Symfony/HeaderBagDynamicReturnTypeExtension.php index 5b05a711..4bf5b8d0 100644 --- a/src/Type/Symfony/HeaderBagDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/HeaderBagDynamicReturnTypeExtension.php @@ -5,7 +5,6 @@ use PhpParser\Node\Expr\MethodCall; use PHPStan\Analyser\Scope; use PHPStan\Reflection\MethodReflection; -use PHPStan\Reflection\ParametersAcceptorSelector; use PHPStan\Type\ArrayType; use PHPStan\Type\Constant\ConstantBooleanType; use PHPStan\Type\DynamicMethodReturnTypeExtension; @@ -32,7 +31,7 @@ public function getTypeFromMethodCall( MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope - ): Type + ): ?Type { $firstArgType = isset($methodCall->getArgs()[2]) ? $scope->getType($methodCall->getArgs()[2]->value) : new ConstantBooleanType(true); $isTrueType = (new ConstantBooleanType(true))->isSuperTypeOf($firstArgType); @@ -48,7 +47,7 @@ public function getTypeFromMethodCall( return new ArrayType(new IntegerType(), new StringType()); } - return ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType(); + return null; } } diff --git a/src/Type/Symfony/InputBagDynamicReturnTypeExtension.php b/src/Type/Symfony/InputBagDynamicReturnTypeExtension.php index af35a548..75e6d0bc 100644 --- a/src/Type/Symfony/InputBagDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/InputBagDynamicReturnTypeExtension.php @@ -37,7 +37,7 @@ public function getTypeFromMethodCall( MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope - ): Type + ): ?Type { if ($methodReflection->getName() === 'get') { return $this->getGetTypeFromMethodCall($methodReflection, $methodCall, $scope); @@ -54,17 +54,21 @@ private function getGetTypeFromMethodCall( MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope - ): Type + ): ?Type { if (isset($methodCall->getArgs()[1])) { $argType = $scope->getType($methodCall->getArgs()[1]->value); $isNull = (new NullType())->isSuperTypeOf($argType); if ($isNull->no()) { - return TypeCombinator::removeNull(ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType()); + return TypeCombinator::removeNull(ParametersAcceptorSelector::selectFromArgs( + $scope, + $methodCall->getArgs(), + $methodReflection->getVariants(), + )->getReturnType()); } } - return ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType(); + return null; } private function getAllTypeFromMethodCall( diff --git a/src/Type/Symfony/KernelInterfaceDynamicReturnTypeExtension.php b/src/Type/Symfony/KernelInterfaceDynamicReturnTypeExtension.php index 5a2adae5..a0cb1c8a 100644 --- a/src/Type/Symfony/KernelInterfaceDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/KernelInterfaceDynamicReturnTypeExtension.php @@ -5,7 +5,6 @@ use PhpParser\Node\Expr\MethodCall; use PHPStan\Analyser\Scope; use PHPStan\Reflection\MethodReflection; -use PHPStan\Reflection\ParametersAcceptorSelector; use PHPStan\Type\ArrayType; use PHPStan\Type\Constant\ConstantBooleanType; use PHPStan\Type\DynamicMethodReturnTypeExtension; @@ -30,7 +29,7 @@ public function getTypeFromMethodCall( MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope - ): Type + ): ?Type { $firstArgType = isset($methodCall->getArgs()[2]) ? $scope->getType($methodCall->getArgs()[2]->value) : new ConstantBooleanType(true); $isTrueType = (new ConstantBooleanType(true))->isSuperTypeOf($firstArgType); @@ -44,7 +43,7 @@ public function getTypeFromMethodCall( return new ArrayType(new IntegerType(), new StringType()); } - return ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType(); + return null; } } diff --git a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php index 7b70b443..ad62f127 100644 --- a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php @@ -6,7 +6,6 @@ use PHPStan\Analyser\Scope; use PHPStan\PhpDoc\TypeStringResolver; use PHPStan\Reflection\MethodReflection; -use PHPStan\Reflection\ParametersAcceptorSelector; use PHPStan\ShouldNotHappenException; use PHPStan\Symfony\Configuration; use PHPStan\Symfony\ParameterMap; @@ -85,7 +84,7 @@ public function isMethodSupported(MethodReflection $methodReflection): bool return in_array($methodReflection->getName(), $methods, true); } - public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type + public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): ?Type { switch ($methodReflection->getName()) { case $this->methodGet: @@ -206,16 +205,15 @@ private function getHasTypeFromMethodCall( MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope - ): Type + ): ?Type { - $defaultReturnType = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType(); if (!isset($methodCall->getArgs()[0]) || !$this->constantHassers) { - return $defaultReturnType; + return null; } $parameterKeys = $this->parameterMap::getParameterKeysFromNode($methodCall->getArgs()[0]->value, $scope); if ($parameterKeys === []) { - return $defaultReturnType; + return null; } $has = null; @@ -228,7 +226,7 @@ private function getHasTypeFromMethodCall( ($has === true && $parameter === null) || ($has === false && $parameter !== null) ) { - return $defaultReturnType; + return null; } } diff --git a/src/Type/Symfony/RequestDynamicReturnTypeExtension.php b/src/Type/Symfony/RequestDynamicReturnTypeExtension.php index 284ddf01..6dcc1bcf 100644 --- a/src/Type/Symfony/RequestDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/RequestDynamicReturnTypeExtension.php @@ -5,7 +5,6 @@ use PhpParser\Node\Expr\MethodCall; use PHPStan\Analyser\Scope; use PHPStan\Reflection\MethodReflection; -use PHPStan\Reflection\ParametersAcceptorSelector; use PHPStan\Type\Constant\ConstantBooleanType; use PHPStan\Type\DynamicMethodReturnTypeExtension; use PHPStan\Type\ResourceType; @@ -29,7 +28,7 @@ public function getTypeFromMethodCall( MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope - ): Type + ): ?Type { if (!isset($methodCall->getArgs()[0])) { return new StringType(); @@ -46,7 +45,7 @@ public function getTypeFromMethodCall( return new StringType(); } - return ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType(); + return null; } } diff --git a/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php b/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php index c8c49a89..af35b368 100644 --- a/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php @@ -5,7 +5,6 @@ use PhpParser\Node\Expr\MethodCall; use PHPStan\Analyser\Scope; use PHPStan\Reflection\MethodReflection; -use PHPStan\Reflection\ParametersAcceptorSelector; use PHPStan\ShouldNotHappenException; use PHPStan\Symfony\Configuration; use PHPStan\Symfony\ParameterMap; @@ -56,7 +55,7 @@ public function isMethodSupported(MethodReflection $methodReflection): bool return in_array($methodReflection->getName(), ['get', 'has'], true); } - public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type + public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): ?Type { switch ($methodReflection->getName()) { case 'get': @@ -71,16 +70,15 @@ private function getGetTypeFromMethodCall( MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope - ): Type + ): ?Type { - $returnType = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType(); if (!isset($methodCall->getArgs()[0])) { - return $returnType; + return null; } $parameterBag = $this->tryGetParameterBag(); if ($parameterBag === null) { - return $returnType; + return null; } $serviceId = $this->serviceMap::getServiceIdFromNode($methodCall->getArgs()[0]->value, $scope); @@ -91,7 +89,7 @@ private function getGetTypeFromMethodCall( } } - return $returnType; + return null; } private function tryGetParameterBag(): ?ParameterBag @@ -122,11 +120,10 @@ private function getHasTypeFromMethodCall( MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope - ): Type + ): ?Type { - $returnType = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType(); if (!isset($methodCall->getArgs()[0]) || !$this->constantHassers) { - return $returnType; + return null; } $serviceId = $this->serviceMap::getServiceIdFromNode($methodCall->getArgs()[0]->value, $scope); @@ -135,7 +132,7 @@ private function getHasTypeFromMethodCall( return new ConstantBooleanType($service !== null && $service->isPublic()); } - return $returnType; + return null; } private function determineServiceClass(ParameterBag $parameterBag, ServiceDefinition $service): ?string From f7d5782044bedf93aeb3f38e09c91148ee90e5a1 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Thu, 26 Sep 2024 11:24:58 +0200 Subject: [PATCH 180/208] Fix InputBagTypeSpecifyingExtension --- src/Type/Symfony/InputBagTypeSpecifyingExtension.php | 2 +- tests/Type/Symfony/data/input_bag.php | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Type/Symfony/InputBagTypeSpecifyingExtension.php b/src/Type/Symfony/InputBagTypeSpecifyingExtension.php index d3ad578c..3a4e54fc 100644 --- a/src/Type/Symfony/InputBagTypeSpecifyingExtension.php +++ b/src/Type/Symfony/InputBagTypeSpecifyingExtension.php @@ -30,7 +30,7 @@ public function getClass(): string public function isMethodSupported(MethodReflection $methodReflection, MethodCall $node, TypeSpecifierContext $context): bool { - return $methodReflection->getName() === self::HAS_METHOD_NAME && !$context->null(); + return $methodReflection->getName() === self::HAS_METHOD_NAME && $context->false(); } public function specifyTypes(MethodReflection $methodReflection, MethodCall $node, Scope $scope, TypeSpecifierContext $context): SpecifiedTypes diff --git a/tests/Type/Symfony/data/input_bag.php b/tests/Type/Symfony/data/input_bag.php index efd08461..744a7765 100644 --- a/tests/Type/Symfony/data/input_bag.php +++ b/tests/Type/Symfony/data/input_bag.php @@ -7,7 +7,8 @@ assertType('bool|float|int|string|null', $bag->get('foo')); if ($bag->has('foo')) { - assertType('bool|float|int|string', $bag->get('foo')); + // Because `has` rely on `array_key_exists` we can still have set the NULL value. + assertType('bool|float|int|string|null', $bag->get('foo')); assertType('bool|float|int|string|null', $bag->get('bar')); } else { assertType('null', $bag->get('foo')); From 7e5633f28f2f62bde4d48c7739bc4f277ca90238 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Mon, 30 Sep 2024 17:09:04 +0200 Subject: [PATCH 181/208] [BCB] Remove legacy config options with `_` in their name --- extension.neon | 6 ------ src/Symfony/Configuration.php | 6 +++--- tests/Symfony/config.neon | 3 --- tests/Type/Symfony/extension-test.neon | 4 ++-- 4 files changed, 5 insertions(+), 14 deletions(-) delete mode 100644 tests/Symfony/config.neon diff --git a/extension.neon b/extension.neon index 512f9908..0d74d8b3 100644 --- a/extension.neon +++ b/extension.neon @@ -5,11 +5,8 @@ parameters: uncheckedExceptionClasses: - 'Symfony\Component\Console\Exception\InvalidArgumentException' symfony: - container_xml_path: null containerXmlPath: null - constant_hassers: true constantHassers: true - console_application_loader: null consoleApplicationLoader: null featureToggles: skipCheckGenericClasses: @@ -109,11 +106,8 @@ parameters: parametersSchema: symfony: structure([ - container_xml_path: schema(string(), nullable()) containerXmlPath: schema(string(), nullable()) - constant_hassers: bool() constantHassers: bool() - console_application_loader: schema(string(), nullable()) consoleApplicationLoader: schema(string(), nullable()) ]) diff --git a/src/Symfony/Configuration.php b/src/Symfony/Configuration.php index e603b333..acdd8508 100644 --- a/src/Symfony/Configuration.php +++ b/src/Symfony/Configuration.php @@ -18,17 +18,17 @@ public function __construct(array $parameters) public function getContainerXmlPath(): ?string { - return $this->parameters['containerXmlPath'] ?? $this->parameters['container_xml_path'] ?? null; + return $this->parameters['containerXmlPath']; } public function hasConstantHassers(): bool { - return $this->parameters['constantHassers'] ?? $this->parameters['constant_hassers'] ?? true; + return $this->parameters['constantHassers']; } public function getConsoleApplicationLoader(): ?string { - return $this->parameters['consoleApplicationLoader'] ?? $this->parameters['console_application_loader'] ?? null; + return $this->parameters['consoleApplicationLoader']; } } diff --git a/tests/Symfony/config.neon b/tests/Symfony/config.neon deleted file mode 100644 index e4f42c64..00000000 --- a/tests/Symfony/config.neon +++ /dev/null @@ -1,3 +0,0 @@ -parameters: - symfony: - container_xml_path: container.xml diff --git a/tests/Type/Symfony/extension-test.neon b/tests/Type/Symfony/extension-test.neon index f7dc1353..0f1d9522 100644 --- a/tests/Type/Symfony/extension-test.neon +++ b/tests/Type/Symfony/extension-test.neon @@ -1,4 +1,4 @@ parameters: symfony: - console_application_loader: console_application_loader.php - container_xml_path: container.xml + consoleApplicationLoader: console_application_loader.php + containerXmlPath: container.xml From a0572ad0ffd8bd4eff6cc5e41a3a3d951880ad80 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Mon, 30 Sep 2024 17:17:17 +0200 Subject: [PATCH 182/208] Fixes after PHPStan update --- src/Rules/Symfony/InvalidArgumentDefaultValueRule.php | 3 +-- src/Rules/Symfony/InvalidOptionDefaultValueRule.php | 3 +-- src/Rules/Symfony/UndefinedArgumentRule.php | 3 +-- src/Rules/Symfony/UndefinedOptionRule.php | 3 +-- src/Symfony/DefaultParameterMap.php | 3 +-- src/Symfony/DefaultServiceMap.php | 3 +-- .../Symfony/CommandGetHelperDynamicReturnTypeExtension.php | 3 +-- .../ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php | 3 +-- .../Symfony/Config/TreeBuilderDynamicReturnTypeExtension.php | 3 +-- .../InputInterfaceGetArgumentDynamicReturnTypeExtension.php | 3 +-- .../InputInterfaceGetOptionDynamicReturnTypeExtension.php | 3 +-- .../InputInterfaceHasArgumentDynamicReturnTypeExtension.php | 3 +-- .../InputInterfaceHasOptionDynamicReturnTypeExtension.php | 3 +-- src/Type/Symfony/ParameterDynamicReturnTypeExtension.php | 3 +-- 14 files changed, 14 insertions(+), 28 deletions(-) diff --git a/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php b/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php index 27643a7d..5435d4c9 100644 --- a/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php +++ b/src/Rules/Symfony/InvalidArgumentDefaultValueRule.php @@ -13,7 +13,6 @@ use PHPStan\Type\NullType; use PHPStan\Type\ObjectType; use PHPStan\Type\StringType; -use PHPStan\Type\TypeUtils; use PHPStan\Type\UnionType; use PHPStan\Type\VerbosityLevel; use function count; @@ -46,7 +45,7 @@ public function processNode(Node $node, Scope $scope): array if ($modeType->isNull()->yes()) { $modeType = new ConstantIntegerType(2); // InputArgument::OPTIONAL } - $modeTypes = TypeUtils::getConstantScalars($modeType); + $modeTypes = $modeType->getConstantScalarTypes(); if (count($modeTypes) !== 1) { return []; } diff --git a/src/Rules/Symfony/InvalidOptionDefaultValueRule.php b/src/Rules/Symfony/InvalidOptionDefaultValueRule.php index 152514f1..2e3dc0e9 100644 --- a/src/Rules/Symfony/InvalidOptionDefaultValueRule.php +++ b/src/Rules/Symfony/InvalidOptionDefaultValueRule.php @@ -15,7 +15,6 @@ use PHPStan\Type\NullType; use PHPStan\Type\ObjectType; use PHPStan\Type\StringType; -use PHPStan\Type\TypeUtils; use PHPStan\Type\UnionType; use PHPStan\Type\VerbosityLevel; use function count; @@ -48,7 +47,7 @@ public function processNode(Node $node, Scope $scope): array if ($modeType->isNull()->yes()) { $modeType = new ConstantIntegerType(1); // InputOption::VALUE_NONE } - $modeTypes = TypeUtils::getConstantScalars($modeType); + $modeTypes = $modeType->getConstantScalarTypes(); if (count($modeTypes) !== 1) { return []; } diff --git a/src/Rules/Symfony/UndefinedArgumentRule.php b/src/Rules/Symfony/UndefinedArgumentRule.php index ff997be5..bccbb515 100644 --- a/src/Rules/Symfony/UndefinedArgumentRule.php +++ b/src/Rules/Symfony/UndefinedArgumentRule.php @@ -12,7 +12,6 @@ use PHPStan\Symfony\ConsoleApplicationResolver; use PHPStan\Type\ObjectType; use PHPStan\Type\Symfony\Helper; -use PHPStan\Type\TypeUtils; use function count; use function sprintf; @@ -58,7 +57,7 @@ public function processNode(Node $node, Scope $scope): array } $argType = $scope->getType($node->getArgs()[0]->value); - $argStrings = TypeUtils::getConstantStrings($argType); + $argStrings = $argType->getConstantStrings(); if (count($argStrings) !== 1) { return []; } diff --git a/src/Rules/Symfony/UndefinedOptionRule.php b/src/Rules/Symfony/UndefinedOptionRule.php index ae96bfa9..f9f88a30 100644 --- a/src/Rules/Symfony/UndefinedOptionRule.php +++ b/src/Rules/Symfony/UndefinedOptionRule.php @@ -12,7 +12,6 @@ use PHPStan\Symfony\ConsoleApplicationResolver; use PHPStan\Type\ObjectType; use PHPStan\Type\Symfony\Helper; -use PHPStan\Type\TypeUtils; use function count; use function sprintf; @@ -58,7 +57,7 @@ public function processNode(Node $node, Scope $scope): array } $optType = $scope->getType($node->getArgs()[0]->value); - $optStrings = TypeUtils::getConstantStrings($optType); + $optStrings = $optType->getConstantStrings(); if (count($optStrings) !== 1) { return []; } diff --git a/src/Symfony/DefaultParameterMap.php b/src/Symfony/DefaultParameterMap.php index 8d0af459..3149fd7d 100644 --- a/src/Symfony/DefaultParameterMap.php +++ b/src/Symfony/DefaultParameterMap.php @@ -5,7 +5,6 @@ use PhpParser\Node\Expr; use PHPStan\Analyser\Scope; use PHPStan\Type\Type; -use PHPStan\Type\TypeUtils; use function array_map; final class DefaultParameterMap implements ParameterMap @@ -37,7 +36,7 @@ public function getParameter(string $key): ?ParameterDefinition public static function getParameterKeysFromNode(Expr $node, Scope $scope): array { - $strings = TypeUtils::getConstantStrings($scope->getType($node)); + $strings = $scope->getType($node)->getConstantStrings(); return array_map(static fn (Type $type) => $type->getValue(), $strings); } diff --git a/src/Symfony/DefaultServiceMap.php b/src/Symfony/DefaultServiceMap.php index edf4dfd6..5d3bccd0 100644 --- a/src/Symfony/DefaultServiceMap.php +++ b/src/Symfony/DefaultServiceMap.php @@ -4,7 +4,6 @@ use PhpParser\Node\Expr; use PHPStan\Analyser\Scope; -use PHPStan\Type\TypeUtils; use function count; final class DefaultServiceMap implements ServiceMap @@ -36,7 +35,7 @@ public function getService(string $id): ?ServiceDefinition public static function getServiceIdFromNode(Expr $node, Scope $scope): ?string { - $strings = TypeUtils::getConstantStrings($scope->getType($node)); + $strings = $scope->getType($node)->getConstantStrings(); return count($strings) === 1 ? $strings[0]->getValue() : null; } diff --git a/src/Type/Symfony/CommandGetHelperDynamicReturnTypeExtension.php b/src/Type/Symfony/CommandGetHelperDynamicReturnTypeExtension.php index c59ba0dd..fba70cfd 100644 --- a/src/Type/Symfony/CommandGetHelperDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/CommandGetHelperDynamicReturnTypeExtension.php @@ -10,7 +10,6 @@ use PHPStan\Type\ObjectType; use PHPStan\Type\Type; use PHPStan\Type\TypeCombinator; -use PHPStan\Type\TypeUtils; use Throwable; use function count; use function get_class; @@ -46,7 +45,7 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method return null; } - $argStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->getArgs()[0]->value)); + $argStrings = $scope->getType($methodCall->getArgs()[0]->value)->getConstantStrings(); if (count($argStrings) !== 1) { return null; } diff --git a/src/Type/Symfony/Config/ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php index 73af2d77..1dae22e2 100644 --- a/src/Type/Symfony/Config/ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/Config/ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php @@ -9,7 +9,6 @@ use PHPStan\Type\DynamicMethodReturnTypeExtension; use PHPStan\Type\Symfony\Config\ValueObject\ParentObjectType; use PHPStan\Type\Type; -use PHPStan\Type\TypeUtils; use PHPStan\Type\VerbosityLevel; use function count; use function in_array; @@ -66,7 +65,7 @@ public function getTypeFromMethodCall( return $defaultType; } - $argStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->getArgs()[0]->value)); + $argStrings = $scope->getType($methodCall->getArgs()[0]->value)->getConstantStrings(); if (count($argStrings) === 1 && isset(self::MAPPING[$argStrings[0]->getValue()])) { $type = $argStrings[0]->getValue(); diff --git a/src/Type/Symfony/Config/TreeBuilderDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/TreeBuilderDynamicReturnTypeExtension.php index 8cf3170a..4f266c50 100644 --- a/src/Type/Symfony/Config/TreeBuilderDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/Config/TreeBuilderDynamicReturnTypeExtension.php @@ -10,7 +10,6 @@ use PHPStan\Type\DynamicStaticMethodReturnTypeExtension; use PHPStan\Type\Symfony\Config\ValueObject\TreeBuilderType; use PHPStan\Type\Type; -use PHPStan\Type\TypeUtils; use function count; final class TreeBuilderDynamicReturnTypeExtension implements DynamicStaticMethodReturnTypeExtension @@ -47,7 +46,7 @@ public function getTypeFromStaticMethodCall(MethodReflection $methodReflection, $type = 'array'; if (isset($methodCall->getArgs()[1])) { - $argStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->getArgs()[1]->value)); + $argStrings = $scope->getType($methodCall->getArgs()[1]->value)->getConstantStrings(); if (count($argStrings) === 1 && isset(self::MAPPING[$argStrings[0]->getValue()])) { $type = $argStrings[0]->getValue(); } diff --git a/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php index 71e71748..88bd7b0e 100644 --- a/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php @@ -14,7 +14,6 @@ use PHPStan\Type\StringType; use PHPStan\Type\Type; use PHPStan\Type\TypeCombinator; -use PHPStan\Type\TypeUtils; use function count; use function in_array; @@ -49,7 +48,7 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method return null; } - $argStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->getArgs()[0]->value)); + $argStrings = $scope->getType($methodCall->getArgs()[0]->value)->getConstantStrings(); if (count($argStrings) !== 1) { return null; } diff --git a/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php index 223b77cf..6d0346cf 100644 --- a/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php @@ -10,7 +10,6 @@ use PHPStan\Type\DynamicMethodReturnTypeExtension; use PHPStan\Type\Type; use PHPStan\Type\TypeCombinator; -use PHPStan\Type\TypeUtils; use function count; final class InputInterfaceGetOptionDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension @@ -47,7 +46,7 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method return null; } - $optStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->getArgs()[0]->value)); + $optStrings = $scope->getType($methodCall->getArgs()[0]->value)->getConstantStrings(); if (count($optStrings) !== 1) { return null; } diff --git a/src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php index 91611838..34bffcea 100644 --- a/src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php @@ -10,7 +10,6 @@ use PHPStan\Type\Constant\ConstantBooleanType; use PHPStan\Type\DynamicMethodReturnTypeExtension; use PHPStan\Type\Type; -use PHPStan\Type\TypeUtils; use function array_unique; use function count; use function in_array; @@ -46,7 +45,7 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method return null; } - $argStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->getArgs()[0]->value)); + $argStrings = $scope->getType($methodCall->getArgs()[0]->value)->getConstantStrings(); if (count($argStrings) !== 1) { return null; } diff --git a/src/Type/Symfony/InputInterfaceHasOptionDynamicReturnTypeExtension.php b/src/Type/Symfony/InputInterfaceHasOptionDynamicReturnTypeExtension.php index c25e646a..e4f8b5b1 100644 --- a/src/Type/Symfony/InputInterfaceHasOptionDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/InputInterfaceHasOptionDynamicReturnTypeExtension.php @@ -10,7 +10,6 @@ use PHPStan\Type\Constant\ConstantBooleanType; use PHPStan\Type\DynamicMethodReturnTypeExtension; use PHPStan\Type\Type; -use PHPStan\Type\TypeUtils; use function array_unique; use function count; @@ -45,7 +44,7 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method return null; } - $optStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->getArgs()[0]->value)); + $optStrings = $scope->getType($methodCall->getArgs()[0]->value)->getConstantStrings(); if (count($optStrings) !== 1) { return null; } diff --git a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php index ad62f127..cd87ff6f 100644 --- a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php @@ -14,7 +14,6 @@ use PHPStan\Type\Constant\ConstantArrayType; use PHPStan\Type\Constant\ConstantArrayTypeBuilder; use PHPStan\Type\Constant\ConstantBooleanType; -use PHPStan\Type\ConstantType; use PHPStan\Type\DynamicMethodReturnTypeExtension; use PHPStan\Type\FloatType; use PHPStan\Type\GeneralizePrecision; @@ -194,7 +193,7 @@ private function generalizeType(Type $type): Type } return new ArrayType($this->generalizeType($type->getKeyType()), $this->generalizeType($type->getItemType())); } - if ($type instanceof ConstantType) { + if ($type->isConstantValue()->yes()) { return $type->generalize(GeneralizePrecision::lessSpecific()); } return $traverse($type); From d1e08acebbde9d8f1af925fd247742f40448e32b Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Fri, 4 Oct 2024 13:52:52 +0200 Subject: [PATCH 183/208] Fixes after PHPStan update --- .../Symfony/ContainerInterfaceUnknownServiceRule.php | 6 +++--- src/Rules/Symfony/UndefinedArgumentRule.php | 6 +++--- src/Rules/Symfony/UndefinedOptionRule.php | 6 +++--- src/Type/Symfony/ArgumentTypeSpecifyingExtension.php | 6 +++--- .../PassParentObjectDynamicReturnTypeExtension.php | 2 ++ .../Config/ReturnParentDynamicReturnTypeExtension.php | 2 ++ src/Type/Symfony/OptionTypeSpecifyingExtension.php | 6 +++--- .../Symfony/ParameterDynamicReturnTypeExtension.php | 4 ++++ .../Symfony/SerializerDynamicReturnTypeExtension.php | 4 ++++ src/Type/Symfony/ServiceDynamicReturnTypeExtension.php | 4 ++++ src/Type/Symfony/ServiceTypeSpecifyingExtension.php | 10 +++++++--- .../ContainerInterfaceUnknownServiceRuleFakeTest.php | 7 ++++--- .../ContainerInterfaceUnknownServiceRuleTest.php | 4 ++-- tests/Rules/Symfony/UndefinedArgumentRuleTest.php | 4 ++-- tests/Rules/Symfony/UndefinedOptionRuleTest.php | 4 ++-- 15 files changed, 48 insertions(+), 27 deletions(-) diff --git a/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php b/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php index ec1a70e0..23444b6b 100644 --- a/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php +++ b/src/Rules/Symfony/ContainerInterfaceUnknownServiceRule.php @@ -4,8 +4,8 @@ use PhpParser\Node; use PhpParser\Node\Expr\MethodCall; -use PhpParser\PrettyPrinter\Standard; use PHPStan\Analyser\Scope; +use PHPStan\Node\Printer\Printer; use PHPStan\Rules\Rule; use PHPStan\Rules\RuleErrorBuilder; use PHPStan\Symfony\ServiceMap; @@ -21,9 +21,9 @@ final class ContainerInterfaceUnknownServiceRule implements Rule private ServiceMap $serviceMap; - private Standard $printer; + private Printer $printer; - public function __construct(ServiceMap $symfonyServiceMap, Standard $printer) + public function __construct(ServiceMap $symfonyServiceMap, Printer $printer) { $this->serviceMap = $symfonyServiceMap; $this->printer = $printer; diff --git a/src/Rules/Symfony/UndefinedArgumentRule.php b/src/Rules/Symfony/UndefinedArgumentRule.php index bccbb515..ee36a23c 100644 --- a/src/Rules/Symfony/UndefinedArgumentRule.php +++ b/src/Rules/Symfony/UndefinedArgumentRule.php @@ -5,8 +5,8 @@ use InvalidArgumentException; use PhpParser\Node; use PhpParser\Node\Expr\MethodCall; -use PhpParser\PrettyPrinter\Standard; use PHPStan\Analyser\Scope; +use PHPStan\Node\Printer\Printer; use PHPStan\Rules\Rule; use PHPStan\Rules\RuleErrorBuilder; use PHPStan\Symfony\ConsoleApplicationResolver; @@ -23,9 +23,9 @@ final class UndefinedArgumentRule implements Rule private ConsoleApplicationResolver $consoleApplicationResolver; - private Standard $printer; + private Printer $printer; - public function __construct(ConsoleApplicationResolver $consoleApplicationResolver, Standard $printer) + public function __construct(ConsoleApplicationResolver $consoleApplicationResolver, Printer $printer) { $this->consoleApplicationResolver = $consoleApplicationResolver; $this->printer = $printer; diff --git a/src/Rules/Symfony/UndefinedOptionRule.php b/src/Rules/Symfony/UndefinedOptionRule.php index f9f88a30..39a6a4ac 100644 --- a/src/Rules/Symfony/UndefinedOptionRule.php +++ b/src/Rules/Symfony/UndefinedOptionRule.php @@ -5,8 +5,8 @@ use InvalidArgumentException; use PhpParser\Node; use PhpParser\Node\Expr\MethodCall; -use PhpParser\PrettyPrinter\Standard; use PHPStan\Analyser\Scope; +use PHPStan\Node\Printer\Printer; use PHPStan\Rules\Rule; use PHPStan\Rules\RuleErrorBuilder; use PHPStan\Symfony\ConsoleApplicationResolver; @@ -23,9 +23,9 @@ final class UndefinedOptionRule implements Rule private ConsoleApplicationResolver $consoleApplicationResolver; - private Standard $printer; + private Printer $printer; - public function __construct(ConsoleApplicationResolver $consoleApplicationResolver, Standard $printer) + public function __construct(ConsoleApplicationResolver $consoleApplicationResolver, Printer $printer) { $this->consoleApplicationResolver = $consoleApplicationResolver; $this->printer = $printer; diff --git a/src/Type/Symfony/ArgumentTypeSpecifyingExtension.php b/src/Type/Symfony/ArgumentTypeSpecifyingExtension.php index 019890c8..5c21f021 100644 --- a/src/Type/Symfony/ArgumentTypeSpecifyingExtension.php +++ b/src/Type/Symfony/ArgumentTypeSpecifyingExtension.php @@ -3,23 +3,23 @@ namespace PHPStan\Type\Symfony; use PhpParser\Node\Expr\MethodCall; -use PhpParser\PrettyPrinter\Standard; use PHPStan\Analyser\Scope; use PHPStan\Analyser\SpecifiedTypes; use PHPStan\Analyser\TypeSpecifier; use PHPStan\Analyser\TypeSpecifierAwareExtension; use PHPStan\Analyser\TypeSpecifierContext; +use PHPStan\Node\Printer\Printer; use PHPStan\Reflection\MethodReflection; use PHPStan\Type\MethodTypeSpecifyingExtension; final class ArgumentTypeSpecifyingExtension implements MethodTypeSpecifyingExtension, TypeSpecifierAwareExtension { - private Standard $printer; + private Printer $printer; private TypeSpecifier $typeSpecifier; - public function __construct(Standard $printer) + public function __construct(Printer $printer) { $this->printer = $printer; } diff --git a/src/Type/Symfony/Config/PassParentObjectDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/PassParentObjectDynamicReturnTypeExtension.php index 9264d17b..800d9dbc 100644 --- a/src/Type/Symfony/Config/PassParentObjectDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/Config/PassParentObjectDynamicReturnTypeExtension.php @@ -15,12 +15,14 @@ final class PassParentObjectDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension { + /** @var class-string */ private string $className; /** @var string[] */ private array $methods; /** + * @param class-string $className * @param string[] $methods */ public function __construct(string $className, array $methods) diff --git a/src/Type/Symfony/Config/ReturnParentDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/ReturnParentDynamicReturnTypeExtension.php index d3351327..034d5d80 100644 --- a/src/Type/Symfony/Config/ReturnParentDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/Config/ReturnParentDynamicReturnTypeExtension.php @@ -13,12 +13,14 @@ final class ReturnParentDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension { + /** @var class-string */ private string $className; /** @var string[] */ private array $methods; /** + * @param class-string $className * @param string[] $methods */ public function __construct(string $className, array $methods) diff --git a/src/Type/Symfony/OptionTypeSpecifyingExtension.php b/src/Type/Symfony/OptionTypeSpecifyingExtension.php index e8530417..8cd9dbd5 100644 --- a/src/Type/Symfony/OptionTypeSpecifyingExtension.php +++ b/src/Type/Symfony/OptionTypeSpecifyingExtension.php @@ -3,23 +3,23 @@ namespace PHPStan\Type\Symfony; use PhpParser\Node\Expr\MethodCall; -use PhpParser\PrettyPrinter\Standard; use PHPStan\Analyser\Scope; use PHPStan\Analyser\SpecifiedTypes; use PHPStan\Analyser\TypeSpecifier; use PHPStan\Analyser\TypeSpecifierAwareExtension; use PHPStan\Analyser\TypeSpecifierContext; +use PHPStan\Node\Printer\Printer; use PHPStan\Reflection\MethodReflection; use PHPStan\Type\MethodTypeSpecifyingExtension; final class OptionTypeSpecifyingExtension implements MethodTypeSpecifyingExtension, TypeSpecifierAwareExtension { - private Standard $printer; + private Printer $printer; private TypeSpecifier $typeSpecifier; - public function __construct(Standard $printer) + public function __construct(Printer $printer) { $this->printer = $printer; } diff --git a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php index cd87ff6f..5f0fb9bd 100644 --- a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php @@ -42,6 +42,7 @@ final class ParameterDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension { + /** @var class-string */ private string $className; private ?string $methodGet = null; @@ -54,6 +55,9 @@ final class ParameterDynamicReturnTypeExtension implements DynamicMethodReturnTy private TypeStringResolver $typeStringResolver; + /** + * @param class-string $className + */ public function __construct( string $className, ?string $methodGet, diff --git a/src/Type/Symfony/SerializerDynamicReturnTypeExtension.php b/src/Type/Symfony/SerializerDynamicReturnTypeExtension.php index 2b689fbb..84f256e4 100755 --- a/src/Type/Symfony/SerializerDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/SerializerDynamicReturnTypeExtension.php @@ -17,10 +17,14 @@ class SerializerDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension { + /** @var class-string */ private string $class; private string $method; + /** + * @param class-string $class + */ public function __construct(string $class, string $method) { $this->class = $class; diff --git a/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php b/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php index af35b368..13097469 100644 --- a/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php @@ -22,6 +22,7 @@ final class ServiceDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension { + /** @var class-string */ private string $className; private bool $constantHassers; @@ -32,6 +33,9 @@ final class ServiceDynamicReturnTypeExtension implements DynamicMethodReturnType private ?ParameterBag $parameterBag = null; + /** + * @param class-string $className + */ public function __construct( string $className, Configuration $configuration, diff --git a/src/Type/Symfony/ServiceTypeSpecifyingExtension.php b/src/Type/Symfony/ServiceTypeSpecifyingExtension.php index 86dc4efc..dd767ccb 100644 --- a/src/Type/Symfony/ServiceTypeSpecifyingExtension.php +++ b/src/Type/Symfony/ServiceTypeSpecifyingExtension.php @@ -3,25 +3,29 @@ namespace PHPStan\Type\Symfony; use PhpParser\Node\Expr\MethodCall; -use PhpParser\PrettyPrinter\Standard; use PHPStan\Analyser\Scope; use PHPStan\Analyser\SpecifiedTypes; use PHPStan\Analyser\TypeSpecifier; use PHPStan\Analyser\TypeSpecifierAwareExtension; use PHPStan\Analyser\TypeSpecifierContext; +use PHPStan\Node\Printer\Printer; use PHPStan\Reflection\MethodReflection; use PHPStan\Type\MethodTypeSpecifyingExtension; final class ServiceTypeSpecifyingExtension implements MethodTypeSpecifyingExtension, TypeSpecifierAwareExtension { + /** @var class-string */ private string $className; - private Standard $printer; + private Printer $printer; private TypeSpecifier $typeSpecifier; - public function __construct(string $className, Standard $printer) + /** + * @param class-string $className + */ + public function __construct(string $className, Printer $printer) { $this->className = $className; $this->printer = $printer; diff --git a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php index f2f74e55..065fa067 100644 --- a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php +++ b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php @@ -2,13 +2,14 @@ namespace PHPStan\Rules\Symfony; -use PhpParser\PrettyPrinter\Standard; +use PHPStan\Node\Printer\Printer; use PHPStan\Rules\Rule; use PHPStan\Symfony\Configuration; use PHPStan\Symfony\XmlServiceMapFactory; use PHPStan\Testing\RuleTestCase; use PHPStan\Type\MethodTypeSpecifyingExtension; use PHPStan\Type\Symfony\ServiceTypeSpecifyingExtension; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use function class_exists; /** @@ -19,7 +20,7 @@ final class ContainerInterfaceUnknownServiceRuleFakeTest extends RuleTestCase protected function getRule(): Rule { - return new ContainerInterfaceUnknownServiceRule((new XmlServiceMapFactory(new Configuration([])))->create(), new Standard()); + return new ContainerInterfaceUnknownServiceRule((new XmlServiceMapFactory(new Configuration([])))->create(), self::getContainer()->getByType(Printer::class)); } /** @@ -28,7 +29,7 @@ protected function getRule(): Rule protected function getMethodTypeSpecifyingExtensions(): array { return [ - new ServiceTypeSpecifyingExtension('Symfony\Bundle\FrameworkBundle\Controller\Controller', new Standard()), + new ServiceTypeSpecifyingExtension(AbstractController::class, self::getContainer()->getByType(Printer::class)), ]; } diff --git a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php index 323065b0..aa7683e2 100644 --- a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php +++ b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php @@ -2,7 +2,7 @@ namespace PHPStan\Rules\Symfony; -use PhpParser\PrettyPrinter\Standard; +use PHPStan\Node\Printer\Printer; use PHPStan\Rules\Rule; use PHPStan\Symfony\Configuration; use PHPStan\Symfony\XmlServiceMapFactory; @@ -18,7 +18,7 @@ final class ContainerInterfaceUnknownServiceRuleTest extends RuleTestCase protected function getRule(): Rule { - return new ContainerInterfaceUnknownServiceRule((new XmlServiceMapFactory(new Configuration(['containerXmlPath' => __DIR__ . '/container.xml'])))->create(), new Standard()); + return new ContainerInterfaceUnknownServiceRule((new XmlServiceMapFactory(new Configuration(['containerXmlPath' => __DIR__ . '/container.xml'])))->create(), self::getContainer()->getByType(Printer::class)); } public function testGetPrivateService(): void diff --git a/tests/Rules/Symfony/UndefinedArgumentRuleTest.php b/tests/Rules/Symfony/UndefinedArgumentRuleTest.php index 6d25bf4a..8b1b869c 100644 --- a/tests/Rules/Symfony/UndefinedArgumentRuleTest.php +++ b/tests/Rules/Symfony/UndefinedArgumentRuleTest.php @@ -2,7 +2,7 @@ namespace PHPStan\Rules\Symfony; -use PhpParser\PrettyPrinter\Standard; +use PHPStan\Node\Printer\Printer; use PHPStan\Rules\Rule; use PHPStan\Symfony\Configuration; use PHPStan\Symfony\ConsoleApplicationResolver; @@ -16,7 +16,7 @@ final class UndefinedArgumentRuleTest extends RuleTestCase protected function getRule(): Rule { - return new UndefinedArgumentRule(new ConsoleApplicationResolver(new Configuration(['consoleApplicationLoader' => __DIR__ . '/console_application_loader.php'])), new Standard()); + return new UndefinedArgumentRule(new ConsoleApplicationResolver(new Configuration(['consoleApplicationLoader' => __DIR__ . '/console_application_loader.php'])), self::getContainer()->getByType(Printer::class)); } public function testGetArgument(): void diff --git a/tests/Rules/Symfony/UndefinedOptionRuleTest.php b/tests/Rules/Symfony/UndefinedOptionRuleTest.php index 50d38a83..d376fa4b 100644 --- a/tests/Rules/Symfony/UndefinedOptionRuleTest.php +++ b/tests/Rules/Symfony/UndefinedOptionRuleTest.php @@ -2,7 +2,7 @@ namespace PHPStan\Rules\Symfony; -use PhpParser\PrettyPrinter\Standard; +use PHPStan\Node\Printer\Printer; use PHPStan\Rules\Rule; use PHPStan\Symfony\Configuration; use PHPStan\Symfony\ConsoleApplicationResolver; @@ -16,7 +16,7 @@ final class UndefinedOptionRuleTest extends RuleTestCase protected function getRule(): Rule { - return new UndefinedOptionRule(new ConsoleApplicationResolver(new Configuration(['consoleApplicationLoader' => __DIR__ . '/console_application_loader.php'])), new Standard()); + return new UndefinedOptionRule(new ConsoleApplicationResolver(new Configuration(['consoleApplicationLoader' => __DIR__ . '/console_application_loader.php'])), self::getContainer()->getByType(Printer::class)); } public function testGetArgument(): void From 237165eae2ddd114605f54a9f23d1171b4b3b55d Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Mon, 7 Oct 2024 11:17:28 +0200 Subject: [PATCH 184/208] Fixes after PHPStan update --- .../Symfony/ContainerInterfacePrivateServiceRule.php | 4 ++-- .../Form/FormInterfaceDynamicReturnTypeExtension.php | 8 ++++---- src/Type/Symfony/HeaderBagDynamicReturnTypeExtension.php | 4 ++-- .../Symfony/KernelInterfaceDynamicReturnTypeExtension.php | 4 ++-- src/Type/Symfony/RequestDynamicReturnTypeExtension.php | 4 ++-- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php b/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php index 5d935859..96f1efea 100644 --- a/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php +++ b/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php @@ -82,13 +82,13 @@ public function processNode(Node $node, Scope $scope): array private function isServiceSubscriber(Type $containerType, Scope $scope): TrinaryLogic { $serviceSubscriberInterfaceType = new ObjectType('Symfony\Contracts\Service\ServiceSubscriberInterface'); - $isContainerServiceSubscriber = $serviceSubscriberInterfaceType->isSuperTypeOf($containerType); + $isContainerServiceSubscriber = $serviceSubscriberInterfaceType->isSuperTypeOf($containerType)->result; $classReflection = $scope->getClassReflection(); if ($classReflection === null) { return $isContainerServiceSubscriber; } $containedClassType = new ObjectType($classReflection->getName()); - return $isContainerServiceSubscriber->or($serviceSubscriberInterfaceType->isSuperTypeOf($containedClassType)); + return $isContainerServiceSubscriber->or($serviceSubscriberInterfaceType->isSuperTypeOf($containedClassType)->result); } } diff --git a/src/Type/Symfony/Form/FormInterfaceDynamicReturnTypeExtension.php b/src/Type/Symfony/Form/FormInterfaceDynamicReturnTypeExtension.php index bdbc95ee..f80ddeb9 100644 --- a/src/Type/Symfony/Form/FormInterfaceDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/Form/FormInterfaceDynamicReturnTypeExtension.php @@ -41,10 +41,10 @@ public function getTypeFromMethodCall( $firstArgType = $scope->getType($methodCall->getArgs()[0]->value); $secondArgType = $scope->getType($methodCall->getArgs()[1]->value); - $firstIsTrueType = (new ConstantBooleanType(true))->isSuperTypeOf($firstArgType); - $firstIsFalseType = (new ConstantBooleanType(false))->isSuperTypeOf($firstArgType); - $secondIsTrueType = (new ConstantBooleanType(true))->isSuperTypeOf($secondArgType); - $secondIsFalseType = (new ConstantBooleanType(false))->isSuperTypeOf($secondArgType); + $firstIsTrueType = (new ConstantBooleanType(true))->isSuperTypeOf($firstArgType)->result; + $firstIsFalseType = (new ConstantBooleanType(false))->isSuperTypeOf($firstArgType)->result; + $secondIsTrueType = (new ConstantBooleanType(true))->isSuperTypeOf($secondArgType)->result; + $secondIsFalseType = (new ConstantBooleanType(false))->isSuperTypeOf($secondArgType)->result; $firstCompareType = $firstIsTrueType->compareTo($firstIsFalseType); $secondCompareType = $secondIsTrueType->compareTo($secondIsFalseType); diff --git a/src/Type/Symfony/HeaderBagDynamicReturnTypeExtension.php b/src/Type/Symfony/HeaderBagDynamicReturnTypeExtension.php index 4bf5b8d0..f50dc4e8 100644 --- a/src/Type/Symfony/HeaderBagDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/HeaderBagDynamicReturnTypeExtension.php @@ -34,8 +34,8 @@ public function getTypeFromMethodCall( ): ?Type { $firstArgType = isset($methodCall->getArgs()[2]) ? $scope->getType($methodCall->getArgs()[2]->value) : new ConstantBooleanType(true); - $isTrueType = (new ConstantBooleanType(true))->isSuperTypeOf($firstArgType); - $isFalseType = (new ConstantBooleanType(false))->isSuperTypeOf($firstArgType); + $isTrueType = (new ConstantBooleanType(true))->isSuperTypeOf($firstArgType)->result; + $isFalseType = (new ConstantBooleanType(false))->isSuperTypeOf($firstArgType)->result; $compareTypes = $isTrueType->compareTo($isFalseType); if ($compareTypes === $isTrueType) { diff --git a/src/Type/Symfony/KernelInterfaceDynamicReturnTypeExtension.php b/src/Type/Symfony/KernelInterfaceDynamicReturnTypeExtension.php index a0cb1c8a..810de2f6 100644 --- a/src/Type/Symfony/KernelInterfaceDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/KernelInterfaceDynamicReturnTypeExtension.php @@ -32,8 +32,8 @@ public function getTypeFromMethodCall( ): ?Type { $firstArgType = isset($methodCall->getArgs()[2]) ? $scope->getType($methodCall->getArgs()[2]->value) : new ConstantBooleanType(true); - $isTrueType = (new ConstantBooleanType(true))->isSuperTypeOf($firstArgType); - $isFalseType = (new ConstantBooleanType(false))->isSuperTypeOf($firstArgType); + $isTrueType = (new ConstantBooleanType(true))->isSuperTypeOf($firstArgType)->result; + $isFalseType = (new ConstantBooleanType(false))->isSuperTypeOf($firstArgType)->result; $compareTypes = $isTrueType->compareTo($isFalseType); if ($compareTypes === $isTrueType) { diff --git a/src/Type/Symfony/RequestDynamicReturnTypeExtension.php b/src/Type/Symfony/RequestDynamicReturnTypeExtension.php index 6dcc1bcf..fd0a3a00 100644 --- a/src/Type/Symfony/RequestDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/RequestDynamicReturnTypeExtension.php @@ -35,8 +35,8 @@ public function getTypeFromMethodCall( } $argType = $scope->getType($methodCall->getArgs()[0]->value); - $isTrueType = (new ConstantBooleanType(true))->isSuperTypeOf($argType); - $isFalseType = (new ConstantBooleanType(false))->isSuperTypeOf($argType); + $isTrueType = (new ConstantBooleanType(true))->isSuperTypeOf($argType)->result; + $isFalseType = (new ConstantBooleanType(false))->isSuperTypeOf($argType)->result; $compareTypes = $isTrueType->compareTo($isFalseType); if ($compareTypes === $isTrueType) { return new ResourceType(); From bb3e671a0cfdc3ae20eb6b74400ad8b9e1ee4815 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Wed, 9 Oct 2024 16:41:39 +0200 Subject: [PATCH 185/208] Cleanup `skipCheckGenericClasses` --- extension.neon | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/extension.neon b/extension.neon index 0d74d8b3..91e9c220 100644 --- a/extension.neon +++ b/extension.neon @@ -8,18 +8,6 @@ parameters: containerXmlPath: null constantHassers: true consoleApplicationLoader: null - featureToggles: - skipCheckGenericClasses: - - Symfony\Component\Form\AbstractType - - Symfony\Component\Form\FormBuilderInterface - - Symfony\Component\Form\FormConfigBuilderInterface - - Symfony\Component\Form\FormConfigInterface - - Symfony\Component\Form\FormInterface - - Symfony\Component\Form\FormTypeExtensionInterface - - Symfony\Component\Form\FormTypeInterface - - Symfony\Component\OptionsResolver\Options - - Symfony\Component\Security\Core\Authorization\Voter\Voter - - Symfony\Component\Security\Core\User\PasswordUpgraderInterface stubFiles: - stubs/Psr/Cache/CacheException.stub - stubs/Psr/Cache/CacheItemInterface.stub From 282a6982af299730790f0583f9662b8dede12ed7 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Mon, 14 Oct 2024 05:22:40 +0200 Subject: [PATCH 186/208] Fix after PHPStan update --- tests/Type/Symfony/data/input_bag.php | 4 ++-- tests/Type/Symfony/data/property_accessor.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/Type/Symfony/data/input_bag.php b/tests/Type/Symfony/data/input_bag.php index 744a7765..77b58821 100644 --- a/tests/Type/Symfony/data/input_bag.php +++ b/tests/Type/Symfony/data/input_bag.php @@ -18,5 +18,5 @@ assertType('bool|float|int|string|null', $bag->get('foo', null)); assertType('bool|float|int|string', $bag->get('foo', '')); assertType('bool|float|int|string', $bag->get('foo', 'baz')); -assertType('array', $bag->all()); -assertType('array', $bag->all('bar')); +assertType('array|bool|float|int|string>', $bag->all()); +assertType('array', $bag->all('bar')); diff --git a/tests/Type/Symfony/data/property_accessor.php b/tests/Type/Symfony/data/property_accessor.php index 0e445684..8d5e95f0 100644 --- a/tests/Type/Symfony/data/property_accessor.php +++ b/tests/Type/Symfony/data/property_accessor.php @@ -6,7 +6,7 @@ $array = [1 => 'ea']; $propertyAccessor->setValue($array, 'foo', 'bar'); -assertType('array', $array); +assertType('array', $array); $object = new \stdClass(); $propertyAccessor->setValue($object, 'foo', 'bar'); From 270c2ee1478d1f8dc5121f539e890017bd64b04c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Mirtes?= Date: Wed, 30 Oct 2024 13:01:49 +0100 Subject: [PATCH 187/208] Update Process.stub --- extension.neon | 1 + .../Process/Exception/LogicException.stub | 8 ++++++++ stubs/Symfony/Component/Process/Process.stub | 15 +++++++++++++++ 3 files changed, 24 insertions(+) create mode 100644 stubs/Symfony/Component/Process/Exception/LogicException.stub diff --git a/extension.neon b/extension.neon index 512f9908..4868bc2d 100644 --- a/extension.neon +++ b/extension.neon @@ -66,6 +66,7 @@ parameters: - 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 diff --git a/stubs/Symfony/Component/Process/Exception/LogicException.stub b/stubs/Symfony/Component/Process/Exception/LogicException.stub new file mode 100644 index 00000000..cb781d6a --- /dev/null +++ b/stubs/Symfony/Component/Process/Exception/LogicException.stub @@ -0,0 +1,8 @@ + */ class Process implements \IteratorAggregate { + /** + * @param int $flags + * + * @return \Generator + * + * @throws LogicException in case the output has been disabled + * @throws LogicException In case the process is not started + */ + public function getIterator(int $flags = 0): \Generator + { + + } + } From c7b7e7f520893621558bfbfdb2694d4364565c1d Mon Sep 17 00:00:00 2001 From: Michael Telgmann Date: Wed, 6 Nov 2024 11:08:10 +0100 Subject: [PATCH 188/208] feat: Add @api annotation to interfaces The following interfaces are now part of the public API and can be safely relied on. - \PHPStan\Symfony\ParameterDefinition - \PHPStan\Symfony\ParameterMap - \PHPStan\Symfony\ServiceDefinition - \PHPStan\Symfony\ServiceMap --- src/Symfony/ParameterDefinition.php | 3 +++ src/Symfony/ParameterMap.php | 3 +++ src/Symfony/ServiceDefinition.php | 3 +++ src/Symfony/ServiceMap.php | 3 +++ 4 files changed, 12 insertions(+) diff --git a/src/Symfony/ParameterDefinition.php b/src/Symfony/ParameterDefinition.php index e1aa2eaa..1da7723b 100644 --- a/src/Symfony/ParameterDefinition.php +++ b/src/Symfony/ParameterDefinition.php @@ -2,6 +2,9 @@ namespace PHPStan\Symfony; +/** + * @api + */ interface ParameterDefinition { diff --git a/src/Symfony/ParameterMap.php b/src/Symfony/ParameterMap.php index ff0f5224..0c551635 100644 --- a/src/Symfony/ParameterMap.php +++ b/src/Symfony/ParameterMap.php @@ -5,6 +5,9 @@ use PhpParser\Node\Expr; use PHPStan\Analyser\Scope; +/** + * @api + */ interface ParameterMap { diff --git a/src/Symfony/ServiceDefinition.php b/src/Symfony/ServiceDefinition.php index c7cdcd18..6df34cba 100644 --- a/src/Symfony/ServiceDefinition.php +++ b/src/Symfony/ServiceDefinition.php @@ -2,6 +2,9 @@ namespace PHPStan\Symfony; +/** + * @api + */ interface ServiceDefinition { diff --git a/src/Symfony/ServiceMap.php b/src/Symfony/ServiceMap.php index 6665ede0..bbd2d8a3 100644 --- a/src/Symfony/ServiceMap.php +++ b/src/Symfony/ServiceMap.php @@ -5,6 +5,9 @@ use PhpParser\Node\Expr; use PHPStan\Analyser\Scope; +/** + * @api + */ interface ServiceMap { From dd1aaa7f85f9916222a2ce7e4d21072fe03958f4 Mon Sep 17 00:00:00 2001 From: bnowak Date: Sat, 4 Jan 2025 14:55:31 +0100 Subject: [PATCH 189/208] Support for Messenger HandleTrait return types --- extension.neon | 12 ++ src/Symfony/MessageMap.php | 24 +++ src/Symfony/MessageMapFactory.php | 154 ++++++++++++++++++ src/Symfony/Service.php | 13 +- src/Symfony/ServiceDefinition.php | 3 + src/Symfony/ServiceTag.php | 31 ++++ src/Symfony/ServiceTagDefinition.php | 13 ++ src/Symfony/XmlServiceMapFactory.php | 12 +- ...essengerHandleTraitReturnTypeExtension.php | 91 +++++++++++ tests/Type/Symfony/ExtensionTest.php | 1 + tests/Type/Symfony/container.xml | 18 ++ .../Symfony/data/messenger_handle_trait.php | 113 +++++++++++++ 12 files changed, 483 insertions(+), 2 deletions(-) create mode 100644 src/Symfony/MessageMap.php create mode 100644 src/Symfony/MessageMapFactory.php create mode 100644 src/Symfony/ServiceTag.php create mode 100644 src/Symfony/ServiceTagDefinition.php create mode 100644 src/Type/Symfony/MessengerHandleTraitReturnTypeExtension.php create mode 100644 tests/Type/Symfony/data/messenger_handle_trait.php diff --git a/extension.neon b/extension.neon index 4868bc2d..cbdfd73d 100644 --- a/extension.neon +++ b/extension.neon @@ -140,6 +140,13 @@ services: - factory: @symfony.parameterMapFactory::create() + # message map + symfony.messageMapFactory: + class: PHPStan\Symfony\MessageMapFactory + factory: PHPStan\Symfony\MessageMapFactory + - + factory: @symfony.messageMapFactory::create() + # ControllerTrait::get()/has() return type - factory: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Symfony\Component\DependencyInjection\ContainerInterface) @@ -203,6 +210,11 @@ services: factory: PHPStan\Type\Symfony\EnvelopeReturnTypeExtension tags: [phpstan.broker.dynamicMethodReturnTypeExtension] + # Messenger HandleTrait::handle() return type + - + class: PHPStan\Type\Symfony\MessengerHandleTraitReturnTypeExtension + tags: [phpstan.broker.expressionTypeResolverExtension] + # InputInterface::getArgument() return type - factory: PHPStan\Type\Symfony\InputInterfaceGetArgumentDynamicReturnTypeExtension diff --git a/src/Symfony/MessageMap.php b/src/Symfony/MessageMap.php new file mode 100644 index 00000000..7523742c --- /dev/null +++ b/src/Symfony/MessageMap.php @@ -0,0 +1,24 @@ + */ + private $messageMap; + + /** @param array $messageMap */ + public function __construct(array $messageMap) + { + $this->messageMap = $messageMap; + } + + public function getTypeForClass(string $class): ?Type + { + return $this->messageMap[$class] ?? null; + } + +} diff --git a/src/Symfony/MessageMapFactory.php b/src/Symfony/MessageMapFactory.php new file mode 100644 index 00000000..5c9ef152 --- /dev/null +++ b/src/Symfony/MessageMapFactory.php @@ -0,0 +1,154 @@ +serviceMap = $symfonyServiceMap; + $this->reflectionProvider = $reflectionProvider; + } + + public function create(): MessageMap + { + $returnTypesMap = []; + + foreach ($this->serviceMap->getServices() as $service) { + $serviceClass = $service->getClass(); + + if ($serviceClass === null) { + continue; + } + + foreach ($service->getTags() as $tag) { + if ($tag->getName() !== self::MESSENGER_HANDLER_TAG) { + continue; + } + + if (!$this->reflectionProvider->hasClass($serviceClass)) { + continue; + } + + $reflectionClass = $this->reflectionProvider->getClass($serviceClass); + + /** @var array{handles?: class-string, method?: string} $tagAttributes */ + $tagAttributes = $tag->getAttributes(); + + if (isset($tagAttributes['handles'])) { + $handles = [$tagAttributes['handles'] => ['method' => $tagAttributes['method'] ?? self::DEFAULT_HANDLER_METHOD]]; + } else { + $handles = $this->guessHandledMessages($reflectionClass); + } + + foreach ($handles as $messageClassName => $options) { + $methodName = $options['method'] ?? self::DEFAULT_HANDLER_METHOD; + + if (!$reflectionClass->hasNativeMethod($methodName)) { + continue; + } + + $methodReflection = $reflectionClass->getNativeMethod($methodName); + + foreach ($methodReflection->getVariants() as $variant) { + $returnTypesMap[$messageClassName][] = $variant->getReturnType(); + } + } + } + } + + $messageMap = []; + foreach ($returnTypesMap as $messageClassName => $returnTypes) { + if (count($returnTypes) !== 1) { + continue; + } + + $messageMap[$messageClassName] = $returnTypes[0]; + } + + return new MessageMap($messageMap); + } + + /** @return iterable> */ + private function guessHandledMessages(ClassReflection $reflectionClass): iterable + { + if ($reflectionClass->implementsInterface(MessageSubscriberInterface::class)) { + $className = $reflectionClass->getName(); + + foreach ($className::getHandledMessages() as $index => $value) { + $containOptions = self::containOptions($index, $value); + if ($containOptions === true) { + yield $index => $value; + } elseif ($containOptions === false) { + yield $value => ['method' => self::DEFAULT_HANDLER_METHOD]; + } + } + + return; + } + + if (!$reflectionClass->hasNativeMethod(self::DEFAULT_HANDLER_METHOD)) { + return; + } + + $methodReflection = $reflectionClass->getNativeMethod(self::DEFAULT_HANDLER_METHOD); + + $variants = $methodReflection->getVariants(); + if (count($variants) !== 1) { + return; + } + + $parameters = $variants[0]->getParameters(); + + if (count($parameters) !== 1) { + return; + } + + $classNames = $parameters[0]->getType()->getObjectClassNames(); + + if (count($classNames) !== 1) { + return; + } + + yield $classNames[0] => ['method' => self::DEFAULT_HANDLER_METHOD]; + } + + /** + * @param mixed $index + * @param mixed $value + * @phpstan-assert-if-true =class-string $index + * @phpstan-assert-if-true =array $value + * @phpstan-assert-if-false =int $index + * @phpstan-assert-if-false =class-string $value + */ + private static function containOptions($index, $value): ?bool + { + if (is_string($index) && class_exists($index) && is_array($value)) { + return true; + } elseif (is_int($index) && is_string($value) && class_exists($value)) { + return false; + } + + return null; + } + +} diff --git a/src/Symfony/Service.php b/src/Symfony/Service.php index c31324f5..1cc465ac 100644 --- a/src/Symfony/Service.php +++ b/src/Symfony/Service.php @@ -20,12 +20,17 @@ final class Service implements ServiceDefinition /** @var string|null */ private $alias; + /** @var ServiceTag[] */ + private $tags; + + /** @param ServiceTag[] $tags */ public function __construct( string $id, ?string $class, bool $public, bool $synthetic, - ?string $alias + ?string $alias, + array $tags = [] ) { $this->id = $id; @@ -33,6 +38,7 @@ public function __construct( $this->public = $public; $this->synthetic = $synthetic; $this->alias = $alias; + $this->tags = $tags; } public function getId(): string @@ -60,4 +66,9 @@ public function getAlias(): ?string return $this->alias; } + public function getTags(): array + { + return $this->tags; + } + } diff --git a/src/Symfony/ServiceDefinition.php b/src/Symfony/ServiceDefinition.php index 6df34cba..3862fa8d 100644 --- a/src/Symfony/ServiceDefinition.php +++ b/src/Symfony/ServiceDefinition.php @@ -18,4 +18,7 @@ public function isSynthetic(): bool; public function getAlias(): ?string; + /** @return ServiceTag[] */ + public function getTags(): array; + } diff --git a/src/Symfony/ServiceTag.php b/src/Symfony/ServiceTag.php new file mode 100644 index 00000000..a8437fd1 --- /dev/null +++ b/src/Symfony/ServiceTag.php @@ -0,0 +1,31 @@ + */ + private $attributes; + + /** @param array $attributes */ + public function __construct(string $name, array $attributes = []) + { + $this->name = $name; + $this->attributes = $attributes; + } + + public function getName(): string + { + return $this->name; + } + + public function getAttributes(): array + { + return $this->attributes; + } + +} diff --git a/src/Symfony/ServiceTagDefinition.php b/src/Symfony/ServiceTagDefinition.php new file mode 100644 index 00000000..b0f66d9c --- /dev/null +++ b/src/Symfony/ServiceTagDefinition.php @@ -0,0 +1,13 @@ + */ + public function getAttributes(): array; + +} diff --git a/src/Symfony/XmlServiceMapFactory.php b/src/Symfony/XmlServiceMapFactory.php index 1cae5d97..0c44207e 100644 --- a/src/Symfony/XmlServiceMapFactory.php +++ b/src/Symfony/XmlServiceMapFactory.php @@ -47,12 +47,22 @@ public function create(): ServiceMap continue; } + $serviceTags = []; + foreach ($def->tag as $tag) { + $tagAttrs = ((array) $tag->attributes())['@attributes'] ?? []; + $tagName = $tagAttrs['name']; + unset($tagAttrs['name']); + + $serviceTags[] = new ServiceTag($tagName, $tagAttrs); + } + $service = new Service( $this->cleanServiceId((string) $attrs->id), isset($attrs->class) ? (string) $attrs->class : null, isset($attrs->public) && (string) $attrs->public === 'true', isset($attrs->synthetic) && (string) $attrs->synthetic === 'true', - isset($attrs->alias) ? $this->cleanServiceId((string) $attrs->alias) : null + isset($attrs->alias) ? $this->cleanServiceId((string) $attrs->alias) : null, + $serviceTags ); if ($service->getAlias() !== null) { diff --git a/src/Type/Symfony/MessengerHandleTraitReturnTypeExtension.php b/src/Type/Symfony/MessengerHandleTraitReturnTypeExtension.php new file mode 100644 index 00000000..a5dce362 --- /dev/null +++ b/src/Type/Symfony/MessengerHandleTraitReturnTypeExtension.php @@ -0,0 +1,91 @@ +messageMapFactory = $symfonyMessageMapFactory; + } + + public function getType(Expr $expr, Scope $scope): ?Type + { + if ($this->isSupported($expr, $scope)) { + $args = $expr->getArgs(); + if (count($args) !== 1) { + return null; + } + + $arg = $args[0]->value; + $argClassNames = $scope->getType($arg)->getObjectClassNames(); + + if (count($argClassNames) === 1) { + $messageMap = $this->getMessageMap(); + $returnType = $messageMap->getTypeForClass($argClassNames[0]); + + if (!is_null($returnType)) { + return $returnType; + } + } + } + + return null; + } + + private function getMessageMap(): MessageMap + { + if ($this->messageMap === null) { + $this->messageMap = $this->messageMapFactory->create(); + } + + return $this->messageMap; + } + + /** + * @phpstan-assert-if-true =MethodCall $expr + */ + private function isSupported(Expr $expr, Scope $scope): bool + { + if (!($expr instanceof MethodCall) || !($expr->name instanceof Identifier) || $expr->name->name !== self::TRAIT_METHOD_NAME) { + return false; + } + + if (!$scope->isInClass()) { + return false; + } + + $reflectionClass = $scope->getClassReflection()->getNativeReflection(); + + if (!$reflectionClass->hasMethod(self::TRAIT_METHOD_NAME)) { + return false; + } + + $methodReflection = $reflectionClass->getMethod(self::TRAIT_METHOD_NAME); + $declaringClassReflection = $methodReflection->getBetterReflection()->getDeclaringClass(); + + return $declaringClassReflection->getName() === self::TRAIT_NAME; + } + +} diff --git a/tests/Type/Symfony/ExtensionTest.php b/tests/Type/Symfony/ExtensionTest.php index a076caac..40420be0 100644 --- a/tests/Type/Symfony/ExtensionTest.php +++ b/tests/Type/Symfony/ExtensionTest.php @@ -14,6 +14,7 @@ class ExtensionTest extends TypeInferenceTestCase /** @return mixed[] */ public function dataFileAsserts(): iterable { + yield from $this->gatherAssertTypes(__DIR__ . '/data/messenger_handle_trait.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/envelope_all.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/header_bag_get.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/response_header_bag_get_cookies.php'); diff --git a/tests/Type/Symfony/container.xml b/tests/Type/Symfony/container.xml index 224c72db..16d4b7fe 100644 --- a/tests/Type/Symfony/container.xml +++ b/tests/Type/Symfony/container.xml @@ -354,5 +354,23 @@ + + + + + + + + + + + + + + + + + + diff --git a/tests/Type/Symfony/data/messenger_handle_trait.php b/tests/Type/Symfony/data/messenger_handle_trait.php new file mode 100644 index 00000000..7a86d482 --- /dev/null +++ b/tests/Type/Symfony/data/messenger_handle_trait.php @@ -0,0 +1,113 @@ + ['method' => 'handleInt']; + yield FloatQuery::class => ['method' => 'handleFloat']; + yield StringQuery::class => ['method' => 'handleString']; + } + + public function __invoke(BooleanQuery $query): bool + { + return true; + } + + public function handleInt(IntQuery $query): int + { + return 0; + } + + public function handleFloat(FloatQuery $query): float + { + return 0.0; + } + + public function handleString(StringQuery $query): string + { + return 'string result'; + } +} + +class TaggedQuery {} +class TaggedResult {} +class TaggedHandler +{ + public function handle(TaggedQuery $query): TaggedResult + { + return new TaggedResult(); + } +} + +class MultiHandlesForInTheSameHandlerQuery {} +class MultiHandlesForInTheSameHandler implements MessageSubscriberInterface +{ + public static function getHandledMessages(): iterable + { + yield MultiHandlesForInTheSameHandlerQuery::class; + yield MultiHandlesForInTheSameHandlerQuery::class => ['priority' => '0']; + } + + public function __invoke(MultiHandlesForInTheSameHandlerQuery $query): bool + { + return true; + } +} + +class MultiHandlersForTheSameMessageQuery {} +class MultiHandlersForTheSameMessageHandler1 +{ + public function __invoke(MultiHandlersForTheSameMessageQuery $query): bool + { + return true; + } +} +class MultiHandlersForTheSameMessageHandler2 +{ + public function __invoke(MultiHandlersForTheSameMessageQuery $query): bool + { + return false; + } +} + +class HandleTraitClass { + use HandleTrait; + + public function __invoke() + { + assertType(RegularQueryResult::class, $this->handle(new RegularQuery())); + + assertType('bool', $this->handle(new BooleanQuery())); + assertType('int', $this->handle(new IntQuery())); + assertType('float', $this->handle(new FloatQuery())); + assertType('string', $this->handle(new StringQuery())); + + assertType(TaggedResult::class, $this->handle(new TaggedQuery())); + + // HandleTrait will throw exception in fact due to multiple handle methods/handlers per single query + assertType('mixed', $this->handle(new MultiHandlesForInTheSameHandlerQuery())); + assertType('mixed', $this->handle(new MultiHandlersForTheSameMessageQuery())); + } +} From c08cd8e54a08d651bc402d304cfa161c3c3766c4 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Sat, 4 Jan 2025 14:58:15 +0100 Subject: [PATCH 190/208] Fix CS after merge --- src/Symfony/MessageMap.php | 2 +- src/Symfony/MessageMapFactory.php | 6 ++---- src/Symfony/Service.php | 2 +- src/Symfony/ServiceTag.php | 5 ++--- .../Symfony/MessengerHandleTraitReturnTypeExtension.php | 6 ++---- 5 files changed, 8 insertions(+), 13 deletions(-) diff --git a/src/Symfony/MessageMap.php b/src/Symfony/MessageMap.php index 7523742c..97bb8734 100644 --- a/src/Symfony/MessageMap.php +++ b/src/Symfony/MessageMap.php @@ -8,7 +8,7 @@ final class MessageMap { /** @var array */ - private $messageMap; + private array $messageMap; /** @param array $messageMap */ public function __construct(array $messageMap) diff --git a/src/Symfony/MessageMapFactory.php b/src/Symfony/MessageMapFactory.php index 5c9ef152..3d7663ca 100644 --- a/src/Symfony/MessageMapFactory.php +++ b/src/Symfony/MessageMapFactory.php @@ -17,11 +17,9 @@ final class MessageMapFactory private const MESSENGER_HANDLER_TAG = 'messenger.message_handler'; private const DEFAULT_HANDLER_METHOD = '__invoke'; - /** @var ReflectionProvider */ - private $reflectionProvider; + private ReflectionProvider $reflectionProvider; - /** @var ServiceMap */ - private $serviceMap; + private ServiceMap $serviceMap; public function __construct(ServiceMap $symfonyServiceMap, ReflectionProvider $reflectionProvider) { diff --git a/src/Symfony/Service.php b/src/Symfony/Service.php index 9897f45d..44c0d1d7 100644 --- a/src/Symfony/Service.php +++ b/src/Symfony/Service.php @@ -16,7 +16,7 @@ final class Service implements ServiceDefinition private ?string $alias = null; /** @var ServiceTag[] */ - private $tags; + private array $tags; /** @param ServiceTag[] $tags */ public function __construct( diff --git a/src/Symfony/ServiceTag.php b/src/Symfony/ServiceTag.php index a8437fd1..3b22ee34 100644 --- a/src/Symfony/ServiceTag.php +++ b/src/Symfony/ServiceTag.php @@ -5,11 +5,10 @@ final class ServiceTag implements ServiceTagDefinition { - /** @var string */ - private $name; + private string $name; /** @var array */ - private $attributes; + private array $attributes; /** @param array $attributes */ public function __construct(string $name, array $attributes = []) diff --git a/src/Type/Symfony/MessengerHandleTraitReturnTypeExtension.php b/src/Type/Symfony/MessengerHandleTraitReturnTypeExtension.php index a5dce362..2c7b1fbe 100644 --- a/src/Type/Symfony/MessengerHandleTraitReturnTypeExtension.php +++ b/src/Type/Symfony/MessengerHandleTraitReturnTypeExtension.php @@ -19,11 +19,9 @@ final class MessengerHandleTraitReturnTypeExtension implements ExpressionTypeRes private const TRAIT_NAME = 'Symfony\Component\Messenger\HandleTrait'; private const TRAIT_METHOD_NAME = 'handle'; - /** @var MessageMapFactory */ - private $messageMapFactory; + private MessageMapFactory $messageMapFactory; - /** @var MessageMap|null */ - private $messageMap; + private ?MessageMap $messageMap = null; public function __construct(MessageMapFactory $symfonyMessageMapFactory) { From 7417f3a9f6e362b2e5737b5ba553e8c6a9638c1a Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Tue, 21 Jan 2025 15:59:07 +0100 Subject: [PATCH 191/208] Configuration class no longer needed, pass arguments directly to services --- extension.neon | 25 ++++++++------ src/Symfony/Configuration.php | 34 ------------------- src/Symfony/ConsoleApplicationResolver.php | 4 +-- src/Symfony/XmlParameterMapFactory.php | 4 +-- src/Symfony/XmlServiceMapFactory.php | 4 +-- .../ParameterDynamicReturnTypeExtension.php | 5 ++- .../ServiceDynamicReturnTypeExtension.php | 5 ++- ...nerInterfacePrivateServiceRuleFakeTest.php | 3 +- ...ntainerInterfacePrivateServiceRuleTest.php | 3 +- ...nerInterfaceUnknownServiceRuleFakeTest.php | 3 +- ...ntainerInterfaceUnknownServiceRuleTest.php | 3 +- .../Symfony/UndefinedArgumentRuleTest.php | 3 +- .../Rules/Symfony/UndefinedOptionRuleTest.php | 3 +- tests/Symfony/DefaultParameterMapTest.php | 4 +-- tests/Symfony/DefaultServiceMapTest.php | 4 +-- 15 files changed, 34 insertions(+), 73 deletions(-) delete mode 100644 src/Symfony/Configuration.php diff --git a/extension.neon b/extension.neon index 26090319..06580626 100644 --- a/extension.neon +++ b/extension.neon @@ -101,17 +101,18 @@ parametersSchema: ]) services: - - - factory: PHPStan\Symfony\Configuration(%symfony%) - # console resolver - factory: PHPStan\Symfony\ConsoleApplicationResolver + arguments: + consoleApplicationLoader: %symfony.consoleApplicationLoader% # service map symfony.serviceMapFactory: class: PHPStan\Symfony\ServiceMapFactory factory: PHPStan\Symfony\XmlServiceMapFactory + arguments: + containerXmlPath: %symfony.containerXmlPath% - factory: @symfony.serviceMapFactory::create() @@ -119,6 +120,8 @@ services: symfony.parameterMapFactory: class: PHPStan\Symfony\ParameterMapFactory factory: PHPStan\Symfony\XmlParameterMapFactory + arguments: + containerXmlPath: %symfony.containerXmlPath% - factory: @symfony.parameterMapFactory::create() @@ -131,16 +134,16 @@ services: # ControllerTrait::get()/has() return type - - factory: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Symfony\Component\DependencyInjection\ContainerInterface) + factory: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Symfony\Component\DependencyInjection\ContainerInterface, %symfony.constantHassers%) tags: [phpstan.broker.dynamicMethodReturnTypeExtension] - - factory: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Psr\Container\ContainerInterface) + factory: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Psr\Container\ContainerInterface, %symfony.constantHassers%) tags: [phpstan.broker.dynamicMethodReturnTypeExtension] - - factory: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Symfony\Bundle\FrameworkBundle\Controller\Controller) + factory: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Symfony\Bundle\FrameworkBundle\Controller\Controller, %symfony.constantHassers%) tags: [phpstan.broker.dynamicMethodReturnTypeExtension] - - factory: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Symfony\Bundle\FrameworkBundle\Controller\AbstractController) + factory: PHPStan\Type\Symfony\ServiceDynamicReturnTypeExtension(Symfony\Bundle\FrameworkBundle\Controller\AbstractController, %symfony.constantHassers%) tags: [phpstan.broker.dynamicMethodReturnTypeExtension] # ControllerTrait::has() type specification @@ -296,20 +299,20 @@ services: # ParameterBagInterface::get()/has() return type - - factory: PHPStan\Type\Symfony\ParameterDynamicReturnTypeExtension(Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface, 'get', 'has') + factory: PHPStan\Type\Symfony\ParameterDynamicReturnTypeExtension(Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface, 'get', 'has', %symfony.constantHassers%) tags: [phpstan.broker.dynamicMethodReturnTypeExtension] # ContainerInterface::getParameter()/hasParameter() return type - - factory: PHPStan\Type\Symfony\ParameterDynamicReturnTypeExtension(Symfony\Component\DependencyInjection\ContainerInterface, 'getParameter', 'hasParameter') + factory: PHPStan\Type\Symfony\ParameterDynamicReturnTypeExtension(Symfony\Component\DependencyInjection\ContainerInterface, 'getParameter', 'hasParameter', %symfony.constantHassers%) tags: [phpstan.broker.dynamicMethodReturnTypeExtension] # (Abstract)Controller::getParameter() return type - - factory: PHPStan\Type\Symfony\ParameterDynamicReturnTypeExtension(Symfony\Bundle\FrameworkBundle\Controller\AbstractController, 'getParameter', null) + factory: PHPStan\Type\Symfony\ParameterDynamicReturnTypeExtension(Symfony\Bundle\FrameworkBundle\Controller\AbstractController, 'getParameter', null, %symfony.constantHassers%) tags: [phpstan.broker.dynamicMethodReturnTypeExtension] - - factory: PHPStan\Type\Symfony\ParameterDynamicReturnTypeExtension(Symfony\Bundle\FrameworkBundle\Controller\Controller, 'getParameter', null) + factory: PHPStan\Type\Symfony\ParameterDynamicReturnTypeExtension(Symfony\Bundle\FrameworkBundle\Controller\Controller, 'getParameter', null, %symfony.constantHassers%) tags: [phpstan.broker.dynamicMethodReturnTypeExtension] - class: PHPStan\Symfony\InputBagStubFilesExtension diff --git a/src/Symfony/Configuration.php b/src/Symfony/Configuration.php deleted file mode 100644 index acdd8508..00000000 --- a/src/Symfony/Configuration.php +++ /dev/null @@ -1,34 +0,0 @@ - */ - private array $parameters; - - /** - * @param array $parameters - */ - public function __construct(array $parameters) - { - $this->parameters = $parameters; - } - - public function getContainerXmlPath(): ?string - { - return $this->parameters['containerXmlPath']; - } - - public function hasConstantHassers(): bool - { - return $this->parameters['constantHassers']; - } - - public function getConsoleApplicationLoader(): ?string - { - return $this->parameters['consoleApplicationLoader']; - } - -} diff --git a/src/Symfony/ConsoleApplicationResolver.php b/src/Symfony/ConsoleApplicationResolver.php index a29740b7..13b24d26 100644 --- a/src/Symfony/ConsoleApplicationResolver.php +++ b/src/Symfony/ConsoleApplicationResolver.php @@ -20,9 +20,9 @@ final class ConsoleApplicationResolver private ?Application $consoleApplication = null; - public function __construct(Configuration $configuration) + public function __construct(?string $consoleApplicationLoader) { - $this->consoleApplicationLoader = $configuration->getConsoleApplicationLoader(); + $this->consoleApplicationLoader = $consoleApplicationLoader; } public function hasConsoleApplicationLoader(): bool diff --git a/src/Symfony/XmlParameterMapFactory.php b/src/Symfony/XmlParameterMapFactory.php index afb58ba8..b893308f 100644 --- a/src/Symfony/XmlParameterMapFactory.php +++ b/src/Symfony/XmlParameterMapFactory.php @@ -17,9 +17,9 @@ final class XmlParameterMapFactory implements ParameterMapFactory private ?string $containerXml = null; - public function __construct(Configuration $configuration) + public function __construct(?string $containerXmlPath) { - $this->containerXml = $configuration->getContainerXmlPath(); + $this->containerXml = $containerXmlPath; } public function create(): ParameterMap diff --git a/src/Symfony/XmlServiceMapFactory.php b/src/Symfony/XmlServiceMapFactory.php index 684b64d6..734c22c7 100644 --- a/src/Symfony/XmlServiceMapFactory.php +++ b/src/Symfony/XmlServiceMapFactory.php @@ -14,9 +14,9 @@ final class XmlServiceMapFactory implements ServiceMapFactory private ?string $containerXml = null; - public function __construct(Configuration $configuration) + public function __construct(?string $containerXmlPath) { - $this->containerXml = $configuration->getContainerXmlPath(); + $this->containerXml = $containerXmlPath; } public function create(): ServiceMap diff --git a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php index 5f0fb9bd..687b0c33 100644 --- a/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/ParameterDynamicReturnTypeExtension.php @@ -7,7 +7,6 @@ use PHPStan\PhpDoc\TypeStringResolver; use PHPStan\Reflection\MethodReflection; use PHPStan\ShouldNotHappenException; -use PHPStan\Symfony\Configuration; use PHPStan\Symfony\ParameterMap; use PHPStan\Type\ArrayType; use PHPStan\Type\BooleanType; @@ -62,7 +61,7 @@ public function __construct( string $className, ?string $methodGet, ?string $methodHas, - Configuration $configuration, + bool $constantHassers, ParameterMap $symfonyParameterMap, TypeStringResolver $typeStringResolver ) @@ -70,7 +69,7 @@ public function __construct( $this->className = $className; $this->methodGet = $methodGet; $this->methodHas = $methodHas; - $this->constantHassers = $configuration->hasConstantHassers(); + $this->constantHassers = $constantHassers; $this->parameterMap = $symfonyParameterMap; $this->typeStringResolver = $typeStringResolver; } diff --git a/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php b/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php index 13097469..0667d30c 100644 --- a/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/ServiceDynamicReturnTypeExtension.php @@ -6,7 +6,6 @@ use PHPStan\Analyser\Scope; use PHPStan\Reflection\MethodReflection; use PHPStan\ShouldNotHappenException; -use PHPStan\Symfony\Configuration; use PHPStan\Symfony\ParameterMap; use PHPStan\Symfony\ServiceDefinition; use PHPStan\Symfony\ServiceMap; @@ -38,13 +37,13 @@ final class ServiceDynamicReturnTypeExtension implements DynamicMethodReturnType */ public function __construct( string $className, - Configuration $configuration, + bool $constantHassers, ServiceMap $symfonyServiceMap, ParameterMap $symfonyParameterMap ) { $this->className = $className; - $this->constantHassers = $configuration->hasConstantHassers(); + $this->constantHassers = $constantHassers; $this->serviceMap = $symfonyServiceMap; $this->parameterMap = $symfonyParameterMap; } diff --git a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleFakeTest.php b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleFakeTest.php index e4cb4bea..bbecb2e8 100644 --- a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleFakeTest.php +++ b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleFakeTest.php @@ -3,7 +3,6 @@ namespace PHPStan\Rules\Symfony; use PHPStan\Rules\Rule; -use PHPStan\Symfony\Configuration; use PHPStan\Symfony\XmlServiceMapFactory; use PHPStan\Testing\RuleTestCase; use function class_exists; @@ -17,7 +16,7 @@ final class ContainerInterfacePrivateServiceRuleFakeTest extends RuleTestCase protected function getRule(): Rule { - return new ContainerInterfacePrivateServiceRule((new XmlServiceMapFactory(new Configuration([])))->create()); + return new ContainerInterfacePrivateServiceRule((new XmlServiceMapFactory(null))->create()); } public function testGetPrivateService(): void diff --git a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php index 8c5efe8d..dfa3d2b7 100644 --- a/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php +++ b/tests/Rules/Symfony/ContainerInterfacePrivateServiceRuleTest.php @@ -3,7 +3,6 @@ namespace PHPStan\Rules\Symfony; use PHPStan\Rules\Rule; -use PHPStan\Symfony\Configuration; use PHPStan\Symfony\XmlServiceMapFactory; use PHPStan\Testing\RuleTestCase; use function class_exists; @@ -17,7 +16,7 @@ final class ContainerInterfacePrivateServiceRuleTest extends RuleTestCase protected function getRule(): Rule { - return new ContainerInterfacePrivateServiceRule((new XmlServiceMapFactory(new Configuration(['containerXmlPath' => __DIR__ . '/container.xml'])))->create()); + return new ContainerInterfacePrivateServiceRule((new XmlServiceMapFactory(__DIR__ . '/container.xml'))->create()); } public function testGetPrivateService(): void diff --git a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php index 065fa067..8d70f1c3 100644 --- a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php +++ b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleFakeTest.php @@ -4,7 +4,6 @@ use PHPStan\Node\Printer\Printer; use PHPStan\Rules\Rule; -use PHPStan\Symfony\Configuration; use PHPStan\Symfony\XmlServiceMapFactory; use PHPStan\Testing\RuleTestCase; use PHPStan\Type\MethodTypeSpecifyingExtension; @@ -20,7 +19,7 @@ final class ContainerInterfaceUnknownServiceRuleFakeTest extends RuleTestCase protected function getRule(): Rule { - return new ContainerInterfaceUnknownServiceRule((new XmlServiceMapFactory(new Configuration([])))->create(), self::getContainer()->getByType(Printer::class)); + return new ContainerInterfaceUnknownServiceRule((new XmlServiceMapFactory(null))->create(), self::getContainer()->getByType(Printer::class)); } /** diff --git a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php index aa7683e2..c975750f 100644 --- a/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php +++ b/tests/Rules/Symfony/ContainerInterfaceUnknownServiceRuleTest.php @@ -4,7 +4,6 @@ use PHPStan\Node\Printer\Printer; use PHPStan\Rules\Rule; -use PHPStan\Symfony\Configuration; use PHPStan\Symfony\XmlServiceMapFactory; use PHPStan\Testing\RuleTestCase; use function class_exists; @@ -18,7 +17,7 @@ final class ContainerInterfaceUnknownServiceRuleTest extends RuleTestCase protected function getRule(): Rule { - return new ContainerInterfaceUnknownServiceRule((new XmlServiceMapFactory(new Configuration(['containerXmlPath' => __DIR__ . '/container.xml'])))->create(), self::getContainer()->getByType(Printer::class)); + return new ContainerInterfaceUnknownServiceRule((new XmlServiceMapFactory(__DIR__ . '/container.xml'))->create(), self::getContainer()->getByType(Printer::class)); } public function testGetPrivateService(): void diff --git a/tests/Rules/Symfony/UndefinedArgumentRuleTest.php b/tests/Rules/Symfony/UndefinedArgumentRuleTest.php index 8b1b869c..d9970ef6 100644 --- a/tests/Rules/Symfony/UndefinedArgumentRuleTest.php +++ b/tests/Rules/Symfony/UndefinedArgumentRuleTest.php @@ -4,7 +4,6 @@ use PHPStan\Node\Printer\Printer; use PHPStan\Rules\Rule; -use PHPStan\Symfony\Configuration; use PHPStan\Symfony\ConsoleApplicationResolver; use PHPStan\Testing\RuleTestCase; @@ -16,7 +15,7 @@ final class UndefinedArgumentRuleTest extends RuleTestCase protected function getRule(): Rule { - return new UndefinedArgumentRule(new ConsoleApplicationResolver(new Configuration(['consoleApplicationLoader' => __DIR__ . '/console_application_loader.php'])), self::getContainer()->getByType(Printer::class)); + return new UndefinedArgumentRule(new ConsoleApplicationResolver(__DIR__ . '/console_application_loader.php'), self::getContainer()->getByType(Printer::class)); } public function testGetArgument(): void diff --git a/tests/Rules/Symfony/UndefinedOptionRuleTest.php b/tests/Rules/Symfony/UndefinedOptionRuleTest.php index d376fa4b..7f759213 100644 --- a/tests/Rules/Symfony/UndefinedOptionRuleTest.php +++ b/tests/Rules/Symfony/UndefinedOptionRuleTest.php @@ -4,7 +4,6 @@ use PHPStan\Node\Printer\Printer; use PHPStan\Rules\Rule; -use PHPStan\Symfony\Configuration; use PHPStan\Symfony\ConsoleApplicationResolver; use PHPStan\Testing\RuleTestCase; @@ -16,7 +15,7 @@ final class UndefinedOptionRuleTest extends RuleTestCase protected function getRule(): Rule { - return new UndefinedOptionRule(new ConsoleApplicationResolver(new Configuration(['consoleApplicationLoader' => __DIR__ . '/console_application_loader.php'])), self::getContainer()->getByType(Printer::class)); + return new UndefinedOptionRule(new ConsoleApplicationResolver(__DIR__ . '/console_application_loader.php'), self::getContainer()->getByType(Printer::class)); } public function testGetArgument(): void diff --git a/tests/Symfony/DefaultParameterMapTest.php b/tests/Symfony/DefaultParameterMapTest.php index 8e2e5b58..018a68a9 100644 --- a/tests/Symfony/DefaultParameterMapTest.php +++ b/tests/Symfony/DefaultParameterMapTest.php @@ -13,13 +13,13 @@ final class DefaultParameterMapTest extends TestCase */ public function testGetParameter(string $key, callable $validator): void { - $factory = new XmlParameterMapFactory(new Configuration(['containerXmlPath' => __DIR__ . '/container.xml'])); + $factory = new XmlParameterMapFactory(__DIR__ . '/container.xml'); $validator($factory->create()->getParameter($key)); } public function testGetParameterEscapedPath(): void { - $factory = new XmlParameterMapFactory(new Configuration(['containerXmlPath' => __DIR__ . '/containers/bugfix%2Fcontainer.xml'])); + $factory = new XmlParameterMapFactory(__DIR__ . '/containers/bugfix%2Fcontainer.xml'); $serviceMap = $factory->create(); self::assertNotNull($serviceMap->getParameter('app.string')); diff --git a/tests/Symfony/DefaultServiceMapTest.php b/tests/Symfony/DefaultServiceMapTest.php index a0a27d98..b43bee49 100644 --- a/tests/Symfony/DefaultServiceMapTest.php +++ b/tests/Symfony/DefaultServiceMapTest.php @@ -13,13 +13,13 @@ final class DefaultServiceMapTest extends TestCase */ public function testGetService(string $id, callable $validator): void { - $factory = new XmlServiceMapFactory(new Configuration(['containerXmlPath' => __DIR__ . '/container.xml'])); + $factory = new XmlServiceMapFactory(__DIR__ . '/container.xml'); $validator($factory->create()->getService($id)); } public function testGetContainerEscapedPath(): void { - $factory = new XmlServiceMapFactory(new Configuration(['containerXmlPath' => __DIR__ . '/containers/bugfix%2Fcontainer.xml'])); + $factory = new XmlServiceMapFactory(__DIR__ . '/containers/bugfix%2Fcontainer.xml'); $serviceMap = $factory->create(); self::assertNotNull($serviceMap->getService('withClass')); From 65f02c7e585f3c7372e42e14d3d87da034031553 Mon Sep 17 00:00:00 2001 From: Greg Korba Date: Tue, 21 Jan 2025 19:57:07 +0100 Subject: [PATCH 192/208] Add result cache meta extension for DI container --- composer.json | 2 +- extension.neon | 5 + ...mfonyContainerResultCacheMetaExtension.php | 62 ++++ src/Symfony/XmlParameterMapFactory.php | 23 +- src/Symfony/XmlServiceMapFactory.php | 63 ++-- ...yContainerResultCacheMetaExtensionTest.php | 294 ++++++++++++++++++ 6 files changed, 412 insertions(+), 37 deletions(-) create mode 100644 src/Symfony/SymfonyContainerResultCacheMetaExtension.php create mode 100644 tests/Symfony/SymfonyContainerResultCacheMetaExtensionTest.php diff --git a/composer.json b/composer.json index 6cf39b10..a832e1bd 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ "require": { "php": "^7.4 || ^8.0", "ext-simplexml": "*", - "phpstan/phpstan": "^2.0" + "phpstan/phpstan": "^2.1.2" }, "conflict": { "symfony/framework-bundle": "<3.0" diff --git a/extension.neon b/extension.neon index 06580626..34c7d889 100644 --- a/extension.neon +++ b/extension.neon @@ -363,3 +363,8 @@ services: - factory: PHPStan\Type\Symfony\ExtensionGetConfigurationReturnTypeExtension tags: [phpstan.broker.dynamicMethodReturnTypeExtension] + + - + class: PHPStan\Symfony\SymfonyContainerResultCacheMetaExtension + tags: + - phpstan.resultCacheMetaExtension diff --git a/src/Symfony/SymfonyContainerResultCacheMetaExtension.php b/src/Symfony/SymfonyContainerResultCacheMetaExtension.php new file mode 100644 index 00000000..8e2f8028 --- /dev/null +++ b/src/Symfony/SymfonyContainerResultCacheMetaExtension.php @@ -0,0 +1,62 @@ +parameterMap = $parameterMap; + $this->serviceMap = $serviceMap; + } + + public function getKey(): string + { + return 'symfonyDiContainer'; + } + + public function getHash(): string + { + $services = $parameters = []; + + foreach ($this->parameterMap->getParameters() as $parameter) { + $parameters[$parameter->getKey()] = $parameter->getValue(); + } + ksort($parameters); + + foreach ($this->serviceMap->getServices() as $service) { + $serviceTags = array_map( + static fn (ServiceTag $tag) => [ + 'name' => $tag->getName(), + 'attributes' => $tag->getAttributes(), + ], + $service->getTags(), + ); + sort($serviceTags); + + $services[$service->getId()] = [ + 'class' => $service->getClass(), + 'public' => $service->isPublic() ? 'yes' : 'no', + 'synthetic' => $service->isSynthetic() ? 'yes' : 'no', + 'alias' => $service->getAlias(), + 'tags' => $serviceTags, + ]; + } + ksort($services); + + return hash('sha256', var_export(['parameters' => $parameters, 'services' => $services], true)); + } + +} diff --git a/src/Symfony/XmlParameterMapFactory.php b/src/Symfony/XmlParameterMapFactory.php index b893308f..4d3d3578 100644 --- a/src/Symfony/XmlParameterMapFactory.php +++ b/src/Symfony/XmlParameterMapFactory.php @@ -6,8 +6,10 @@ use PHPStan\ShouldNotHappenException; use SimpleXMLElement; use function base64_decode; +use function count; use function file_get_contents; use function is_numeric; +use function ksort; use function simplexml_load_string; use function sprintf; use function strpos; @@ -40,18 +42,23 @@ public function create(): ParameterMap /** @var Parameter[] $parameters */ $parameters = []; - foreach ($xml->parameters->parameter as $def) { - /** @var SimpleXMLElement $attrs */ - $attrs = $def->attributes(); - $parameter = new Parameter( - (string) $attrs->key, - $this->getNodeValue($def), - ); + if (count($xml->parameters) > 0) { + foreach ($xml->parameters->parameter as $def) { + /** @var SimpleXMLElement $attrs */ + $attrs = $def->attributes(); - $parameters[$parameter->getKey()] = $parameter; + $parameter = new Parameter( + (string) $attrs->key, + $this->getNodeValue($def), + ); + + $parameters[$parameter->getKey()] = $parameter; + } } + ksort($parameters); + return new DefaultParameterMap($parameters); } diff --git a/src/Symfony/XmlServiceMapFactory.php b/src/Symfony/XmlServiceMapFactory.php index 734c22c7..ac79cb30 100644 --- a/src/Symfony/XmlServiceMapFactory.php +++ b/src/Symfony/XmlServiceMapFactory.php @@ -3,7 +3,9 @@ namespace PHPStan\Symfony; use SimpleXMLElement; +use function count; use function file_get_contents; +use function ksort; use function simplexml_load_string; use function sprintf; use function strpos; @@ -39,35 +41,38 @@ public function create(): ServiceMap $services = []; /** @var Service[] $aliases */ $aliases = []; - foreach ($xml->services->service as $def) { - /** @var SimpleXMLElement $attrs */ - $attrs = $def->attributes(); - if (!isset($attrs->id)) { - continue; - } - - $serviceTags = []; - foreach ($def->tag as $tag) { - $tagAttrs = ((array) $tag->attributes())['@attributes'] ?? []; - $tagName = $tagAttrs['name']; - unset($tagAttrs['name']); - - $serviceTags[] = new ServiceTag($tagName, $tagAttrs); - } - - $service = new Service( - $this->cleanServiceId((string) $attrs->id), - isset($attrs->class) ? (string) $attrs->class : null, - isset($attrs->public) && (string) $attrs->public === 'true', - isset($attrs->synthetic) && (string) $attrs->synthetic === 'true', - isset($attrs->alias) ? $this->cleanServiceId((string) $attrs->alias) : null, - $serviceTags, - ); - if ($service->getAlias() !== null) { - $aliases[] = $service; - } else { - $services[$service->getId()] = $service; + if (count($xml->services) > 0) { + foreach ($xml->services->service as $def) { + /** @var SimpleXMLElement $attrs */ + $attrs = $def->attributes(); + if (!isset($attrs->id)) { + continue; + } + + $serviceTags = []; + foreach ($def->tag as $tag) { + $tagAttrs = ((array) $tag->attributes())['@attributes'] ?? []; + $tagName = $tagAttrs['name']; + unset($tagAttrs['name']); + + $serviceTags[] = new ServiceTag($tagName, $tagAttrs); + } + + $service = new Service( + $this->cleanServiceId((string) $attrs->id), + isset($attrs->class) ? (string) $attrs->class : null, + isset($attrs->public) && (string) $attrs->public === 'true', + isset($attrs->synthetic) && (string) $attrs->synthetic === 'true', + isset($attrs->alias) ? $this->cleanServiceId((string) $attrs->alias) : null, + $serviceTags, + ); + + if ($service->getAlias() !== null) { + $aliases[] = $service; + } else { + $services[$service->getId()] = $service; + } } } foreach ($aliases as $service) { @@ -85,6 +90,8 @@ public function create(): ServiceMap ); } + ksort($services); + return new DefaultServiceMap($services); } diff --git a/tests/Symfony/SymfonyContainerResultCacheMetaExtensionTest.php b/tests/Symfony/SymfonyContainerResultCacheMetaExtensionTest.php new file mode 100644 index 00000000..f5c8503f --- /dev/null +++ b/tests/Symfony/SymfonyContainerResultCacheMetaExtensionTest.php @@ -0,0 +1,294 @@ + $sameHashContents + * @param ContainerContents $invalidatingContent + * + * @dataProvider provideContainerHashIsCalculatedCorrectlyCases + */ + public function testContainerHashIsCalculatedCorrectly( + array $sameHashContents, + array $invalidatingContent + ): void + { + $hash = null; + + self::assertGreaterThan(0, count($sameHashContents)); + + foreach ($sameHashContents as $content) { + $currentHash = (new SymfonyContainerResultCacheMetaExtension( + $content['parameters'] ?? new DefaultParameterMap([]), + $content['services'] ?? new DefaultServiceMap([]), + ))->getHash(); + + if ($hash === null) { + $hash = $currentHash; + } else { + self::assertSame($hash, $currentHash); + } + } + + self::assertNotSame( + $hash, + (new SymfonyContainerResultCacheMetaExtension( + $invalidatingContent['parameters'] ?? new DefaultParameterMap([]), + $invalidatingContent['services'] ?? new DefaultServiceMap([]), + ))->getHash(), + ); + } + + /** + * @return iterable, ContainerContents}> + */ + public static function provideContainerHashIsCalculatedCorrectlyCases(): iterable + { + yield 'service "class" changes' => [ + [ + [ + 'services' => new DefaultServiceMap([ + new Service('Foo', 'Foo', true, false, null), + new Service('Bar', 'Bar', true, false, null), + ]), + ], + // Swapping services order in XML file does not affect the calculated hash + [ + 'services' => new DefaultServiceMap([ + new Service('Bar', 'Bar', true, false, null), + new Service('Foo', 'Foo', true, false, null), + ]), + ], + ], + [ + 'services' => new DefaultServiceMap([ + new Service('Foo', 'Foo', true, false, null), + new Service('Bar', 'BarAdapter', true, false, null), + ]), + ], + ]; + + yield 'service visibility changes' => [ + [ + [ + 'services' => new DefaultServiceMap([ + new Service('Foo', 'Foo', true, false, null), + ]), + ], + ], + [ + 'services' => new DefaultServiceMap([ + new Service('Foo', 'Foo', false, false, null), + ]), + ], + ]; + + yield 'service syntheticity changes' => [ + [ + [ + 'services' => new DefaultServiceMap([ + new Service('Foo', 'Foo', true, false, null), + ]), + ], + ], + [ + 'services' => new DefaultServiceMap([ + new Service('Foo', 'Foo', true, true, null), + ]), + ], + ]; + + yield 'service alias changes' => [ + [ + [ + 'services' => new DefaultServiceMap([ + new Service('Foo', 'Foo', true, false, null), + new Service('Bar', 'Bar', true, false, null), + new Service('Baz', null, true, false, 'Foo'), + ]), + ], + // Swapping services order in XML file does not affect the calculated hash + [ + 'services' => new DefaultServiceMap([ + new Service('Baz', null, true, false, 'Foo'), + new Service('Bar', 'Bar', true, false, null), + new Service('Foo', 'Foo', true, false, null), + ]), + ], + ], + [ + 'services' => new DefaultServiceMap([ + new Service('Foo', 'Foo', true, false, null), + new Service('Bar', 'Bar', true, false, null), + new Service('Baz', null, true, false, 'Bar'), + ]), + ], + ]; + + yield 'service tag attributes changes' => [ + [ + [ + 'services' => new DefaultServiceMap([ + new Service('Foo', 'Foo', true, false, null, [ + new ServiceTag('foo.bar', ['baz' => 'bar']), + new ServiceTag('foo.baz', ['baz' => 'baz']), + ]), + ]), + ], + [ + 'services' => new DefaultServiceMap([ + new Service('Foo', 'Foo', true, false, null, [ + new ServiceTag('foo.baz', ['baz' => 'baz']), + new ServiceTag('foo.bar', ['baz' => 'bar']), + ]), + ]), + ], + ], + [ + 'services' => new DefaultServiceMap([ + new Service('Foo', 'Foo', true, false, null, [ + new ServiceTag('foo.bar', ['baz' => 'bar']), + new ServiceTag('foo.baz', ['baz' => 'buzz']), + ]), + ]), + ], + ]; + + yield 'service tag added' => [ + [ + [ + 'services' => new DefaultServiceMap([ + new Service('Foo', 'Foo', true, false, null, [ + new ServiceTag('foo.bar', ['baz' => 'bar']), + ]), + ]), + ], + ], + [ + 'services' => new DefaultServiceMap([ + new Service('Foo', 'Foo', true, false, null, [ + new ServiceTag('foo.bar', ['baz' => 'bar']), + new ServiceTag('foo.baz', ['baz' => 'baz']), + ]), + ]), + ], + ]; + + yield 'service tag removed' => [ + [ + [ + 'services' => new DefaultServiceMap([ + new Service('Foo', 'Foo', true, false, null, [ + new ServiceTag('foo.bar', ['baz' => 'bar']), + new ServiceTag('foo.baz', ['baz' => 'baz']), + ]), + ]), + ], + ], + [ + 'services' => new DefaultServiceMap([ + new Service('Foo', 'Foo', true, false, null, [ + new ServiceTag('foo.bar', ['baz' => 'bar']), + ]), + ]), + ], + ]; + + yield 'new service added' => [ + [ + [ + 'services' => new DefaultServiceMap([ + new Service('Foo', 'Foo', true, false, null), + ]), + ], + ], + [ + 'services' => new DefaultServiceMap([ + new Service('Foo', 'Foo', true, false, null), + new Service('Bar', 'Bar', true, false, null), + ]), + ], + ]; + + yield 'service removed' => [ + [ + [ + 'services' => new DefaultServiceMap([ + new Service('Foo', 'Foo', true, false, null), + new Service('Bar', 'Bar', true, false, null), + ]), + ], + ], + [ + 'services' => new DefaultServiceMap([ + new Service('Foo', 'Foo', true, false, null), + ]), + ], + ]; + + yield 'parameter value changes' => [ + [ + [ + 'parameters' => new DefaultParameterMap([ + new Parameter('foo', 'foo'), + new Parameter('bar', 'bar'), + ]), + ], + [ + 'parameters' => new DefaultParameterMap([ + new Parameter('bar', 'bar'), + new Parameter('foo', 'foo'), + ]), + ], + ], + [ + 'parameters' => new DefaultParameterMap([ + new Parameter('foo', 'foo'), + new Parameter('bar', 'buzz'), + ]), + ], + ]; + + yield 'new parameter added' => [ + [ + [ + 'parameters' => new DefaultParameterMap([ + new Parameter('foo', 'foo'), + ]), + ], + ], + [ + 'parameters' => new DefaultParameterMap([ + new Parameter('foo', 'foo'), + new Parameter('bar', 'bar'), + ]), + ], + ]; + + yield 'parameter removed' => [ + [ + [ + 'parameters' => new DefaultParameterMap([ + new Parameter('foo', 'foo'), + new Parameter('bar', 'bar'), + ]), + ], + ], + [ + 'parameters' => new DefaultParameterMap([ + new Parameter('foo', 'foo'), + ]), + ], + ]; + } + +} From d589514e03653639845bd4750db325f83176dbd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Mirtes?= Date: Tue, 28 Jan 2025 10:24:48 +0100 Subject: [PATCH 193/208] Update LICENSE --- LICENSE | 1 + 1 file changed, 1 insertion(+) diff --git a/LICENSE b/LICENSE index 0b9f74d9..cb2e557c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,7 @@ The MIT License (MIT) Copyright (c) 2017 Lukáš Unger +Copyright (c) 2025 PHPStan s.r.o. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 08b97ab6621a57d6bbb8add1a358c5bf25cd98df Mon Sep 17 00:00:00 2001 From: Christophe Coevoet Date: Wed, 19 Mar 2025 12:54:42 +0100 Subject: [PATCH 194/208] Synchronize the EventSubscriberInterface with the upstream type As of Symfony 5.4, Symfony ships a precise type for this interface but the stub overrides it. --- .../Component/EventDispatcher/EventSubscriberInterface.stub | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stubs/Symfony/Component/EventDispatcher/EventSubscriberInterface.stub b/stubs/Symfony/Component/EventDispatcher/EventSubscriberInterface.stub index 35a77c93..62474d10 100644 --- a/stubs/Symfony/Component/EventDispatcher/EventSubscriberInterface.stub +++ b/stubs/Symfony/Component/EventDispatcher/EventSubscriberInterface.stub @@ -5,7 +5,7 @@ namespace Symfony\Component\EventDispatcher; interface EventSubscriberInterface { /** - * @return array|array|array>> + * @return array> */ public static function getSubscribedEvents(); } From deec7cc280ea37e0af5fad8afb1f64e91f67b7a0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 24 Mar 2025 01:48:24 +0000 Subject: [PATCH 195/208] chore(deps): update metcalfc/changelog-generator action to v4.5.0 --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b1a669a9..be6cad08 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,7 +18,7 @@ jobs: - name: Generate changelog id: changelog - uses: metcalfc/changelog-generator@v4.3.1 + uses: metcalfc/changelog-generator@v4.5.0 with: myToken: ${{ secrets.PHPSTAN_BOT_TOKEN }} From 78b6b5a62f56731d938031c8f59817ed83b2328a Mon Sep 17 00:00:00 2001 From: Christophe Coevoet Date: Wed, 19 Mar 2025 18:31:11 +0100 Subject: [PATCH 196/208] Remove the generic type for PasswordUpgraderInterface This interface method can be called on a user provider even for user types not supported by that provider (for instance when using a chain provider). The implementation is expected to deal with that case gracefully, which will be enforced by phpstan when the generics are gone. --- extension.neon | 5 --- ...ordAuthenticatedUserStubFilesExtension.php | 36 ------------------- .../PasswordAuthenticatedUserInterface.stub | 7 ---- .../Core/User/PasswordUpgraderInterface.stub | 14 -------- 4 files changed, 62 deletions(-) delete mode 100644 src/Symfony/PasswordAuthenticatedUserStubFilesExtension.php delete mode 100644 stubs/Symfony/Component/Security/Core/User/PasswordAuthenticatedUserInterface.stub delete mode 100644 stubs/Symfony/Component/Security/Core/User/PasswordUpgraderInterface.stub diff --git a/extension.neon b/extension.neon index cbdfd73d..a38fd4bf 100644 --- a/extension.neon +++ b/extension.neon @@ -22,7 +22,6 @@ parameters: - Symfony\Component\Form\FormTypeInterface - Symfony\Component\OptionsResolver\Options - Symfony\Component\Security\Core\Authorization\Voter\Voter - - Symfony\Component\Security\Core\User\PasswordUpgraderInterface stubFiles: - stubs/Psr/Cache/CacheException.stub - stubs/Psr/Cache/CacheItemInterface.stub @@ -333,10 +332,6 @@ services: class: PHPStan\Symfony\InputBagStubFilesExtension tags: - phpstan.stubFilesExtension - - - class: PHPStan\Symfony\PasswordAuthenticatedUserStubFilesExtension - tags: - - phpstan.stubFilesExtension - class: PHPStan\Symfony\SymfonyDiagnoseExtension tags: diff --git a/src/Symfony/PasswordAuthenticatedUserStubFilesExtension.php b/src/Symfony/PasswordAuthenticatedUserStubFilesExtension.php deleted file mode 100644 index 8f8c4782..00000000 --- a/src/Symfony/PasswordAuthenticatedUserStubFilesExtension.php +++ /dev/null @@ -1,36 +0,0 @@ -reflector = $reflector; - } - - public function getFiles(): array - { - try { - $this->reflector->reflectClass('Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface'); - } catch (IdentifierNotFound $e) { - return []; - } - - return [ - __DIR__ . '/../../stubs/Symfony/Component/Security/Core/User/PasswordAuthenticatedUserInterface.stub', - __DIR__ . '/../../stubs/Symfony/Component/Security/Core/User/PasswordUpgraderInterface.stub', - ]; - } - -} diff --git a/stubs/Symfony/Component/Security/Core/User/PasswordAuthenticatedUserInterface.stub b/stubs/Symfony/Component/Security/Core/User/PasswordAuthenticatedUserInterface.stub deleted file mode 100644 index 19cc6040..00000000 --- a/stubs/Symfony/Component/Security/Core/User/PasswordAuthenticatedUserInterface.stub +++ /dev/null @@ -1,7 +0,0 @@ - Date: Mon, 7 Apr 2025 03:24:16 +0000 Subject: [PATCH 197/208] chore(deps): update metcalfc/changelog-generator action to v4.6.2 --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index be6cad08..b8c96d48 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,7 +18,7 @@ jobs: - name: Generate changelog id: changelog - uses: metcalfc/changelog-generator@v4.5.0 + uses: metcalfc/changelog-generator@v4.6.2 with: myToken: ${{ secrets.PHPSTAN_BOT_TOKEN }} From 1430c2d0a385ac4987c91cddc6cc4c51fdb397ba Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Sat, 19 Apr 2025 22:05:57 +0200 Subject: [PATCH 198/208] Fix build --- composer.json | 2 +- phpstan-baseline.neon | 63 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 59 insertions(+), 6 deletions(-) diff --git a/composer.json b/composer.json index a832e1bd..19c6a8be 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ "require": { "php": "^7.4 || ^8.0", "ext-simplexml": "*", - "phpstan/phpstan": "^2.1.2" + "phpstan/phpstan": "^2.1.13" }, "conflict": { "symfony/framework-bundle": "<3.0" diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 0f6edd5c..79e87db9 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,26 +1,79 @@ parameters: ignoreErrors: - - message: "#^Although PHPStan\\\\Reflection\\\\Php\\\\PhpPropertyReflection is covered by backward compatibility promise, this instanceof assumption might break because it's not guaranteed to always stay the same\\.$#" + message: '#^Call to internal method Symfony\\Component\\Console\\Command\\Command\:\:mergeApplicationDefinition\(\) from outside its root namespace Symfony\.$#' + identifier: method.internal + count: 1 + path: src/Rules/Symfony/UndefinedArgumentRule.php + + - + message: '#^Call to internal method Symfony\\Component\\Console\\Command\\Command\:\:mergeApplicationDefinition\(\) from outside its root namespace Symfony\.$#' + identifier: method.internal + count: 1 + path: src/Rules/Symfony/UndefinedOptionRule.php + + - + message: '#^Although PHPStan\\Reflection\\Php\\PhpPropertyReflection is covered by backward compatibility promise, this instanceof assumption might break because it''s not guaranteed to always stay the same\.$#' + identifier: phpstanApi.instanceofAssumption count: 1 path: src/Symfony/RequiredAutowiringExtension.php - - message: "#^Call to function method_exists\\(\\) with Symfony\\\\Component\\\\Console\\\\Input\\\\InputOption and 'isNegatable' will always evaluate to true\\.$#" + message: '#^Call to internal method Symfony\\Component\\Console\\Command\\Command\:\:mergeApplicationDefinition\(\) from outside its root namespace Symfony\.$#' + identifier: method.internal + count: 1 + path: src/Type/Symfony/CommandGetHelperDynamicReturnTypeExtension.php + + - + message: '#^Call to function method_exists\(\) with Symfony\\Component\\Console\\Input\\InputOption and ''isNegatable'' will always evaluate to true\.$#' + identifier: function.alreadyNarrowedType count: 1 path: src/Type/Symfony/GetOptionTypeHelper.php - - message: "#^Accessing PHPStan\\\\Rules\\\\Methods\\\\CallMethodsRule\\:\\:class is not covered by backward compatibility promise\\. The class might change in a minor PHPStan version\\.$#" + message: '#^Call to internal method Symfony\\Component\\Console\\Command\\Command\:\:mergeApplicationDefinition\(\) from outside its root namespace Symfony\.$#' + identifier: method.internal + count: 1 + path: src/Type/Symfony/InputInterfaceGetArgumentDynamicReturnTypeExtension.php + + - + message: '#^Call to internal method Symfony\\Component\\Console\\Command\\Command\:\:mergeApplicationDefinition\(\) from outside its root namespace Symfony\.$#' + identifier: method.internal + count: 1 + path: src/Type/Symfony/InputInterfaceGetOptionDynamicReturnTypeExtension.php + + - + message: '#^Call to internal method Symfony\\Component\\Console\\Command\\Command\:\:mergeApplicationDefinition\(\) from outside its root namespace Symfony\.$#' + identifier: method.internal + count: 1 + path: src/Type/Symfony/InputInterfaceGetOptionsDynamicReturnTypeExtension.php + + - + message: '#^Call to internal method Symfony\\Component\\Console\\Command\\Command\:\:mergeApplicationDefinition\(\) from outside its root namespace Symfony\.$#' + identifier: method.internal + count: 1 + path: src/Type/Symfony/InputInterfaceHasArgumentDynamicReturnTypeExtension.php + + - + message: '#^Call to internal method Symfony\\Component\\Console\\Command\\Command\:\:mergeApplicationDefinition\(\) from outside its root namespace Symfony\.$#' + identifier: method.internal + count: 1 + path: src/Type/Symfony/InputInterfaceHasOptionDynamicReturnTypeExtension.php + + - + message: '#^Accessing PHPStan\\Rules\\Methods\\CallMethodsRule\:\:class is not covered by backward compatibility promise\. The class might change in a minor PHPStan version\.$#' + identifier: phpstanApi.classConstant count: 1 path: tests/Rules/NonexistentInputBagClassTest.php - - message: "#^Accessing PHPStan\\\\Rules\\\\Properties\\\\UninitializedPropertyRule\\:\\:class is not covered by backward compatibility promise\\. The class might change in a minor PHPStan version\\.$#" + message: '#^Accessing PHPStan\\Rules\\Properties\\UninitializedPropertyRule\:\:class is not covered by backward compatibility promise\. The class might change in a minor PHPStan version\.$#' + identifier: phpstanApi.classConstant count: 1 path: tests/Symfony/RequiredAutowiringExtensionTest.php - - message: "#^Accessing PHPStan\\\\Rules\\\\Comparison\\\\ImpossibleCheckTypeMethodCallRule\\:\\:class is not covered by backward compatibility promise\\. The class might change in a minor PHPStan version\\.$#" + message: '#^Accessing PHPStan\\Rules\\Comparison\\ImpossibleCheckTypeMethodCallRule\:\:class is not covered by backward compatibility promise\. The class might change in a minor PHPStan version\.$#' + identifier: phpstanApi.classConstant count: 1 path: tests/Type/Symfony/ImpossibleCheckTypeMethodCallRuleTest.php From cf8c9c6994cf2a07c7aefddef2b26941d19efba1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 17 Mar 2025 01:51:32 +0000 Subject: [PATCH 199/208] chore(deps): update dependency psr/container to v1.1.2 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 19c6a8be..c03d2c99 100644 --- a/composer.json +++ b/composer.json @@ -25,7 +25,7 @@ "phpstan/phpstan-phpunit": "^2.0", "phpstan/phpstan-strict-rules": "^2.0", "phpunit/phpunit": "^9.6", - "psr/container": "1.0 || 1.1.1", + "psr/container": "1.1.2", "symfony/config": "^5.4 || ^6.1", "symfony/console": "^5.4 || ^6.1", "symfony/dependency-injection": "^5.4 || ^6.1", From 582ddbc641cee028386e985972c631d57ceef406 Mon Sep 17 00:00:00 2001 From: Marko Kaznovac Date: Sun, 11 May 2025 21:13:47 +0200 Subject: [PATCH 200/208] Export-ignore github and editor config; remove leftover excludes --- .gitattributes | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitattributes b/.gitattributes index 1545ee73..94b7c8c2 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,11 +1,11 @@ *.php text eol=lf +.github export-ignore tests export-ignore tmp export-ignore -.coveralls.yml export-ignore +.editorconfig export-ignore .gitattributes export-ignore .gitignore export-ignore -.travis.yml export-ignore Makefile export-ignore phpstan.neon export-ignore phpstan-baseline.neon export-ignore From 5005288e07583546ea00b52de4a9ac412eb869d7 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Wed, 14 May 2025 09:00:05 +0200 Subject: [PATCH 201/208] Do not narrow `KernelTestCase::getContainer()` return type to internal TestContainer --- extension.neon | 3 --- .../ContainerInterfacePrivateServiceRule.php | 25 +++++++++++++++++-- .../Bundle/FrameworkBundle/KernelBrowser.stub | 13 ---------- .../FrameworkBundle/Test/KernelTestCase.stub | 16 ------------ .../FrameworkBundle/Test/TestContainer.stub | 7 ------ ...ntainerInterfacePrivateServiceRuleTest.php | 10 ++++++++ tests/Rules/Symfony/ExampleTest.php | 23 +++++++++++++++++ 7 files changed, 56 insertions(+), 41 deletions(-) delete mode 100644 stubs/Symfony/Bundle/FrameworkBundle/KernelBrowser.stub delete mode 100644 stubs/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.stub delete mode 100644 stubs/Symfony/Bundle/FrameworkBundle/Test/TestContainer.stub create mode 100644 tests/Rules/Symfony/ExampleTest.php diff --git a/extension.neon b/extension.neon index 0803248f..c76d573f 100644 --- a/extension.neon +++ b/extension.neon @@ -13,9 +13,6 @@ parameters: - stubs/Psr/Cache/CacheItemInterface.stub - stubs/Psr/Cache/InvalidArgumentException.stub - stubs/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.stub - - stubs/Symfony/Bundle/FrameworkBundle/KernelBrowser.stub - - stubs/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.stub - - stubs/Symfony/Bundle/FrameworkBundle/Test/TestContainer.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 diff --git a/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php b/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php index 96f1efea..c8ec11a8 100644 --- a/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php +++ b/src/Rules/Symfony/ContainerInterfacePrivateServiceRule.php @@ -43,11 +43,11 @@ public function processNode(Node $node, Scope $scope): array $argType = $scope->getType($node->var); - $isTestContainerType = (new ObjectType('Symfony\Bundle\FrameworkBundle\Test\TestContainer'))->isSuperTypeOf($argType); + $isTestContainer = $this->isTestContainer($argType, $scope); $isOldServiceSubscriber = (new ObjectType('Symfony\Component\DependencyInjection\ServiceSubscriberInterface'))->isSuperTypeOf($argType); $isServiceSubscriber = $this->isServiceSubscriber($argType, $scope); $isServiceLocator = (new ObjectType('Symfony\Component\DependencyInjection\ServiceLocator'))->isSuperTypeOf($argType); - if ($isTestContainerType->yes() || $isOldServiceSubscriber->yes() || $isServiceSubscriber->yes() || $isServiceLocator->yes()) { + if ($isTestContainer->yes() || $isOldServiceSubscriber->yes() || $isServiceSubscriber->yes() || $isServiceLocator->yes()) { return []; } @@ -91,4 +91,25 @@ private function isServiceSubscriber(Type $containerType, Scope $scope): Trinary return $isContainerServiceSubscriber->or($serviceSubscriberInterfaceType->isSuperTypeOf($containedClassType)->result); } + private function isTestContainer(Type $containerType, Scope $scope): TrinaryLogic + { + $testContainer = new ObjectType('Symfony\Bundle\FrameworkBundle\Test\TestContainer'); + $isTestContainer = $testContainer->isSuperTypeOf($containerType)->result; + + $classReflection = $scope->getClassReflection(); + if ($classReflection === null) { + return $isTestContainer; + } + + $containerInterface = new ObjectType('Symfony\Component\DependencyInjection\ContainerInterface'); + $kernelTestCase = new ObjectType('Symfony\Bundle\FrameworkBundle\Test\KernelTestCase'); + $containedClassType = new ObjectType($classReflection->getName()); + + return $isTestContainer->or( + $containerInterface->isSuperTypeOf($containerType)->result->and( + $kernelTestCase->isSuperTypeOf($containedClassType)->result, + ), + ); + } + } diff --git a/stubs/Symfony/Bundle/FrameworkBundle/KernelBrowser.stub b/stubs/Symfony/Bundle/FrameworkBundle/KernelBrowser.stub deleted file mode 100644 index ec54f1eb..00000000 --- a/stubs/Symfony/Bundle/FrameworkBundle/KernelBrowser.stub +++ /dev/null @@ -1,13 +0,0 @@ -analyse( + [ + __DIR__ . '/ExampleTest.php', + ], + [], + ); + } + } diff --git a/tests/Rules/Symfony/ExampleTest.php b/tests/Rules/Symfony/ExampleTest.php new file mode 100644 index 00000000..10b06703 --- /dev/null +++ b/tests/Rules/Symfony/ExampleTest.php @@ -0,0 +1,23 @@ +get('private'); + } + + public function foo(KernelBrowser $browser): void + { + $container = $browser->getContainer(); + $container->get('private'); + } + +} From 9c272bdc7f87f27a8965f44458d0d96416b3620a Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Thu, 29 May 2025 21:19:35 +0200 Subject: [PATCH 202/208] Add PropertyAccessor stub file alongside PropertyAccessorInterface --- extension.neon | 1 + .../PropertyAccess/PropertyAccessor.stub | 23 +++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 stubs/Symfony/Component/PropertyAccess/PropertyAccessor.stub diff --git a/extension.neon b/extension.neon index c76d573f..ecc2ddf3 100644 --- a/extension.neon +++ b/extension.neon @@ -55,6 +55,7 @@ parameters: - 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 diff --git a/stubs/Symfony/Component/PropertyAccess/PropertyAccessor.stub b/stubs/Symfony/Component/PropertyAccess/PropertyAccessor.stub new file mode 100644 index 00000000..af5423f0 --- /dev/null +++ b/stubs/Symfony/Component/PropertyAccess/PropertyAccessor.stub @@ -0,0 +1,23 @@ + + * @phpstan-param T &$objectOrArray + * @phpstan-param-out ($objectOrArray is object ? T : array) $objectOrArray + * @phpstan-param string|PropertyPathInterface $propertyPath + * @phpstan-param mixed $value + * + * @return void + * + * @throws Exception\InvalidArgumentException If the property path is invalid + * @throws Exception\AccessException If a property/index does not exist or is not public + * @throws Exception\UnexpectedTypeException If a value within the path is neither object nor array + */ + public function setValue(&$objectOrArray, $propertyPath, $value); + +} From 392f7ab8f52a0a776977be4e62535358c28e1b15 Mon Sep 17 00:00:00 2001 From: Nicolas Reynis Date: Tue, 20 May 2025 14:23:09 +0200 Subject: [PATCH 203/208] fix: Improve ParameterBag stub --- stubs/Symfony/Component/HttpFoundation/ParameterBag.stub | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stubs/Symfony/Component/HttpFoundation/ParameterBag.stub b/stubs/Symfony/Component/HttpFoundation/ParameterBag.stub index 0a6858e3..e412e9e0 100644 --- a/stubs/Symfony/Component/HttpFoundation/ParameterBag.stub +++ b/stubs/Symfony/Component/HttpFoundation/ParameterBag.stub @@ -3,12 +3,12 @@ namespace Symfony\Component\HttpFoundation; /** - * @implements \IteratorAggregate + * @implements \IteratorAggregate */ class ParameterBag implements \IteratorAggregate { /** - * @return list + * @return list */ public function keys(): array { From 3d8847fb5865e5e3319b2189ead03fc6a3482eb2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 28 Jul 2025 14:15:20 +0000 Subject: [PATCH 204/208] chore(deps): update eomm/why-don-t-you-tweet action to v2 --- .github/workflows/release-tweet.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-tweet.yml b/.github/workflows/release-tweet.yml index 09b39ded..d81f34ca 100644 --- a/.github/workflows/release-tweet.yml +++ b/.github/workflows/release-tweet.yml @@ -10,7 +10,7 @@ jobs: tweet: runs-on: ubuntu-latest steps: - - uses: Eomm/why-don-t-you-tweet@v1 + - uses: Eomm/why-don-t-you-tweet@v2 if: ${{ !github.event.repository.private }} with: # GitHub event payload From 60b312da9ce2a3778e236ccb081eecefa4df2e2e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 18 Aug 2025 00:49:24 +0000 Subject: [PATCH 205/208] chore(deps): update actions/checkout action to v5 --- .github/workflows/build.yml | 10 +++++----- .github/workflows/create-tag.yml | 2 +- .github/workflows/release.yml | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 88543fb5..db0977ec 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -25,7 +25,7 @@ jobs: steps: - name: "Checkout" - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: "Install PHP" uses: "shivammathur/setup-php@v2" @@ -49,10 +49,10 @@ jobs: steps: - name: "Checkout" - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: "Checkout build-cs" - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: repository: "phpstan/build-cs" path: "build-cs" @@ -100,7 +100,7 @@ jobs: steps: - name: "Checkout" - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: "Install PHP" uses: "shivammathur/setup-php@v2" @@ -139,7 +139,7 @@ jobs: steps: - name: "Checkout" - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: "Install PHP" uses: "shivammathur/setup-php@v2" diff --git a/.github/workflows/create-tag.yml b/.github/workflows/create-tag.yml index a8535014..fd918164 100644 --- a/.github/workflows/create-tag.yml +++ b/.github/workflows/create-tag.yml @@ -21,7 +21,7 @@ jobs: runs-on: "ubuntu-latest" steps: - name: "Checkout" - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 0 token: ${{ secrets.PHPSTAN_BOT_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b8c96d48..ed7e51ad 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,7 +14,7 @@ jobs: steps: - name: "Checkout" - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Generate changelog id: changelog From 18df9086a84fc28e9a231ea8e91d5aff1a0a3d6f Mon Sep 17 00:00:00 2001 From: bnowak Date: Sun, 7 Sep 2025 08:55:28 +0200 Subject: [PATCH 206/208] Handle optional MessageSubscriberInterface if exist --- src/Symfony/MessageMapFactory.php | 3 +- tests/Type/Symfony/ExtensionTest.php | 6 ++ .../Symfony/data/messenger_handle_trait.php | 57 --------------- ...messenger_handle_trait_with_subscriber.php | 72 +++++++++++++++++++ 4 files changed, 80 insertions(+), 58 deletions(-) create mode 100644 tests/Type/Symfony/data/messenger_handle_trait_with_subscriber.php diff --git a/src/Symfony/MessageMapFactory.php b/src/Symfony/MessageMapFactory.php index 5c9ef152..d94f046f 100644 --- a/src/Symfony/MessageMapFactory.php +++ b/src/Symfony/MessageMapFactory.php @@ -7,6 +7,7 @@ use Symfony\Component\Messenger\Handler\MessageSubscriberInterface; use function class_exists; use function count; +use function interface_exists; use function is_array; use function is_int; use function is_string; @@ -91,7 +92,7 @@ public function create(): MessageMap /** @return iterable> */ private function guessHandledMessages(ClassReflection $reflectionClass): iterable { - if ($reflectionClass->implementsInterface(MessageSubscriberInterface::class)) { + if (interface_exists(MessageSubscriberInterface::class) && $reflectionClass->implementsInterface(MessageSubscriberInterface::class)) { $className = $reflectionClass->getName(); foreach ($className::getHandledMessages() as $index => $value) { diff --git a/tests/Type/Symfony/ExtensionTest.php b/tests/Type/Symfony/ExtensionTest.php index 40420be0..0848715e 100644 --- a/tests/Type/Symfony/ExtensionTest.php +++ b/tests/Type/Symfony/ExtensionTest.php @@ -6,6 +6,7 @@ use ReflectionMethod; use Symfony\Component\HttpFoundation\Request; use function class_exists; +use function interface_exists; use function strpos; class ExtensionTest extends TypeInferenceTestCase @@ -15,6 +16,11 @@ class ExtensionTest extends TypeInferenceTestCase public function dataFileAsserts(): iterable { yield from $this->gatherAssertTypes(__DIR__ . '/data/messenger_handle_trait.php'); + + if (interface_exists('Symfony\Component\Messenger\Handler\MessageSubscriberInterface')) { + yield from $this->gatherAssertTypes(__DIR__ . '/data/messenger_handle_trait_with_subscriber.php'); + } + yield from $this->gatherAssertTypes(__DIR__ . '/data/envelope_all.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/header_bag_get.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/response_header_bag_get_cookies.php'); diff --git a/tests/Type/Symfony/data/messenger_handle_trait.php b/tests/Type/Symfony/data/messenger_handle_trait.php index 7a86d482..10f62a0c 100644 --- a/tests/Type/Symfony/data/messenger_handle_trait.php +++ b/tests/Type/Symfony/data/messenger_handle_trait.php @@ -2,7 +2,6 @@ namespace MessengerHandleTrait; -use Symfony\Component\Messenger\Handler\MessageSubscriberInterface; use Symfony\Component\Messenger\HandleTrait; use function PHPStan\Testing\assertType; @@ -16,41 +15,6 @@ public function __invoke(RegularQuery $query): RegularQueryResult } } -class BooleanQuery {} -class StringQuery {} -class IntQuery {} -class FloatQuery {} -class MultiQueryHandler implements MessageSubscriberInterface -{ - public static function getHandledMessages(): iterable - { - yield BooleanQuery::class; - yield IntQuery::class => ['method' => 'handleInt']; - yield FloatQuery::class => ['method' => 'handleFloat']; - yield StringQuery::class => ['method' => 'handleString']; - } - - public function __invoke(BooleanQuery $query): bool - { - return true; - } - - public function handleInt(IntQuery $query): int - { - return 0; - } - - public function handleFloat(FloatQuery $query): float - { - return 0.0; - } - - public function handleString(StringQuery $query): string - { - return 'string result'; - } -} - class TaggedQuery {} class TaggedResult {} class TaggedHandler @@ -61,21 +25,6 @@ public function handle(TaggedQuery $query): TaggedResult } } -class MultiHandlesForInTheSameHandlerQuery {} -class MultiHandlesForInTheSameHandler implements MessageSubscriberInterface -{ - public static function getHandledMessages(): iterable - { - yield MultiHandlesForInTheSameHandlerQuery::class; - yield MultiHandlesForInTheSameHandlerQuery::class => ['priority' => '0']; - } - - public function __invoke(MultiHandlesForInTheSameHandlerQuery $query): bool - { - return true; - } -} - class MultiHandlersForTheSameMessageQuery {} class MultiHandlersForTheSameMessageHandler1 { @@ -99,15 +48,9 @@ public function __invoke() { assertType(RegularQueryResult::class, $this->handle(new RegularQuery())); - assertType('bool', $this->handle(new BooleanQuery())); - assertType('int', $this->handle(new IntQuery())); - assertType('float', $this->handle(new FloatQuery())); - assertType('string', $this->handle(new StringQuery())); - assertType(TaggedResult::class, $this->handle(new TaggedQuery())); // HandleTrait will throw exception in fact due to multiple handle methods/handlers per single query - assertType('mixed', $this->handle(new MultiHandlesForInTheSameHandlerQuery())); assertType('mixed', $this->handle(new MultiHandlersForTheSameMessageQuery())); } } diff --git a/tests/Type/Symfony/data/messenger_handle_trait_with_subscriber.php b/tests/Type/Symfony/data/messenger_handle_trait_with_subscriber.php new file mode 100644 index 00000000..03456231 --- /dev/null +++ b/tests/Type/Symfony/data/messenger_handle_trait_with_subscriber.php @@ -0,0 +1,72 @@ + ['method' => 'handleInt']; + yield FloatQuery::class => ['method' => 'handleFloat']; + yield StringQuery::class => ['method' => 'handleString']; + } + + public function __invoke(BooleanQuery $query): bool + { + return true; + } + + public function handleInt(IntQuery $query): int + { + return 0; + } + + public function handleFloat(FloatQuery $query): float + { + return 0.0; + } + + public function handleString(StringQuery $query): string + { + return 'string result'; + } +} + +class MultiHandlesForInTheSameHandlerQuery {} +class MultiHandlesForInTheSameHandler implements MessageSubscriberInterface +{ + public static function getHandledMessages(): iterable + { + yield MultiHandlesForInTheSameHandlerQuery::class; + yield MultiHandlesForInTheSameHandlerQuery::class => ['priority' => '0']; + } + + public function __invoke(MultiHandlesForInTheSameHandlerQuery $query): bool + { + return true; + } +} + +class HandleTraitClass { + use HandleTrait; + + public function __invoke() + { + assertType('bool', $this->handle(new BooleanQuery())); + assertType('int', $this->handle(new IntQuery())); + assertType('float', $this->handle(new FloatQuery())); + assertType('string', $this->handle(new StringQuery())); + + // HandleTrait will throw exception in fact due to multiple handle methods/handlers per single query + assertType('mixed', $this->handle(new MultiHandlesForInTheSameHandlerQuery())); + } +} From 76cea4d4def2f12c151666d19a93bc00d294fb4b Mon Sep 17 00:00:00 2001 From: Markus Staab <47448731+clxmstaab@users.noreply.github.com> Date: Tue, 7 Oct 2025 15:57:16 +0200 Subject: [PATCH 207/208] Fix "Possibly invalid array key type string|null." --- src/Symfony/XmlServiceMapFactory.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/XmlServiceMapFactory.php b/src/Symfony/XmlServiceMapFactory.php index ac79cb30..41365e14 100644 --- a/src/Symfony/XmlServiceMapFactory.php +++ b/src/Symfony/XmlServiceMapFactory.php @@ -77,7 +77,7 @@ public function create(): ServiceMap } foreach ($aliases as $service) { $alias = $service->getAlias(); - if ($alias !== null && !isset($services[$alias])) { + if ($alias === null || !isset($services[$alias])) { continue; } $id = $service->getId(); From b003bb4eef874dbbab83c3326eb8bdb5205368ee Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Thu, 30 Oct 2025 07:31:35 +0100 Subject: [PATCH 208/208] Update phpstan-phpunit to 2.0.8 --- composer.json | 2 +- tests/Symfony/DefaultParameterMapTest.php | 3 --- tests/Symfony/DefaultServiceMapTest.php | 3 --- tests/Type/Symfony/ExtensionTest.php | 1 - tests/Type/Symfony/ExtensionTestWithoutContainer.php | 2 -- 5 files changed, 1 insertion(+), 10 deletions(-) diff --git a/composer.json b/composer.json index c03d2c99..fc6a9d4f 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,7 @@ }, "require-dev": { "php-parallel-lint/php-parallel-lint": "^1.2", - "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-phpunit": "^2.0.8", "phpstan/phpstan-strict-rules": "^2.0", "phpunit/phpunit": "^9.6", "psr/container": "1.1.2", diff --git a/tests/Symfony/DefaultParameterMapTest.php b/tests/Symfony/DefaultParameterMapTest.php index 018a68a9..318590d3 100644 --- a/tests/Symfony/DefaultParameterMapTest.php +++ b/tests/Symfony/DefaultParameterMapTest.php @@ -25,9 +25,6 @@ public function testGetParameterEscapedPath(): void self::assertNotNull($serviceMap->getParameter('app.string')); } - /** - * @return Iterator - */ public function getParameterProvider(): Iterator { yield [ diff --git a/tests/Symfony/DefaultServiceMapTest.php b/tests/Symfony/DefaultServiceMapTest.php index b43bee49..1188e64e 100644 --- a/tests/Symfony/DefaultServiceMapTest.php +++ b/tests/Symfony/DefaultServiceMapTest.php @@ -25,9 +25,6 @@ public function testGetContainerEscapedPath(): void self::assertNotNull($serviceMap->getService('withClass')); } - /** - * @return Iterator - */ public function getServiceProvider(): Iterator { yield [ diff --git a/tests/Type/Symfony/ExtensionTest.php b/tests/Type/Symfony/ExtensionTest.php index 0848715e..c749203a 100644 --- a/tests/Type/Symfony/ExtensionTest.php +++ b/tests/Type/Symfony/ExtensionTest.php @@ -12,7 +12,6 @@ class ExtensionTest extends TypeInferenceTestCase { - /** @return mixed[] */ public function dataFileAsserts(): iterable { yield from $this->gatherAssertTypes(__DIR__ . '/data/messenger_handle_trait.php'); diff --git a/tests/Type/Symfony/ExtensionTestWithoutContainer.php b/tests/Type/Symfony/ExtensionTestWithoutContainer.php index fd1785c7..4d68aa04 100644 --- a/tests/Type/Symfony/ExtensionTestWithoutContainer.php +++ b/tests/Type/Symfony/ExtensionTestWithoutContainer.php @@ -8,7 +8,6 @@ class ExtensionTestWithoutContainer extends TypeInferenceTestCase { - /** @return mixed[] */ public function dataExampleController(): iterable { if (!class_exists('Symfony\Bundle\FrameworkBundle\Controller\Controller')) { @@ -18,7 +17,6 @@ public function dataExampleController(): iterable yield from $this->gatherAssertTypes(__DIR__ . '/data/ExampleController.php'); } - /** @return mixed[] */ public function dataAbstractController(): iterable { if (!class_exists('Symfony\Bundle\FrameworkBundle\Controller\AbstractController')) {