From ee88a01bc48f608143d3376802ec952270737cb8 Mon Sep 17 00:00:00 2001 From: Jan Nedbal Date: Thu, 18 Jul 2024 12:19:10 +0200 Subject: [PATCH 01/33] 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 02/33] 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 03/33] 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 04/33] 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 05/33] 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 06/33] 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 07/33] 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 08/33] 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 09/33] 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 10/33] 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 11/33] 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 12/33] 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 13/33] 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 14/33] 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 15/33] [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 16/33] 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 17/33] 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 18/33] 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 19/33] 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 20/33] 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 21/33] 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 22/33] 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 23/33] 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 24/33] 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 25/33] 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 26/33] 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 27/33] 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 28/33] 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 29/33] 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 30/33] 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 31/33] 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 32/33] 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 33/33] 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",