From 98b4b0a1114abd3a1d5ffe0715fe7a8d97a70de3 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sun, 25 Jun 2023 09:42:20 +0200 Subject: [PATCH 1/6] Implement JsonValidateTypeSpecifyingExtension --- conf/config.neon | 5 +++ .../JsonValidateTypeSpecifyingExtension.php | 42 +++++++++++++++++++ .../Analyser/NodeScopeResolverTest.php | 1 + tests/PHPStan/Analyser/data/json_validate.php | 28 +++++++++++++ 4 files changed, 76 insertions(+) create mode 100644 src/Type/Php/JsonValidateTypeSpecifyingExtension.php create mode 100644 tests/PHPStan/Analyser/data/json_validate.php diff --git a/conf/config.neon b/conf/config.neon index ee0690bd7e..0131d6ce39 100644 --- a/conf/config.neon +++ b/conf/config.neon @@ -1537,6 +1537,11 @@ services: tags: - phpstan.dynamicFunctionThrowTypeExtension + - + class: PHPStan\Type\Php\JsonValidateTypeSpecifyingExtension + tags: + - phpstan.typeSpecifier.functionTypeSpecifyingExtension + - class: PHPStan\Type\Php\ReflectionClassConstructorThrowTypeExtension tags: diff --git a/src/Type/Php/JsonValidateTypeSpecifyingExtension.php b/src/Type/Php/JsonValidateTypeSpecifyingExtension.php new file mode 100644 index 0000000000..cc64493c63 --- /dev/null +++ b/src/Type/Php/JsonValidateTypeSpecifyingExtension.php @@ -0,0 +1,42 @@ +getName() === 'json_validate' && isset($node->getArgs()[0]) && $context->true(); + } + + public function specifyTypes(FunctionReflection $functionReflection, FuncCall $node, Scope $scope, TypeSpecifierContext $context): SpecifiedTypes + { + $args = $node->getArgs(); + + if (count($args) < 1) { + return new SpecifiedTypes([], []); + } + + return $this->typeSpecifier->create($node->getArgs()[0]->value, new AccessoryNonFalsyStringType(), $context, false, $scope); + } + + public function setTypeSpecifier(TypeSpecifier $typeSpecifier): void + { + $this->typeSpecifier = $typeSpecifier; + } + +} diff --git a/tests/PHPStan/Analyser/NodeScopeResolverTest.php b/tests/PHPStan/Analyser/NodeScopeResolverTest.php index 131545aebc..34126dcc38 100644 --- a/tests/PHPStan/Analyser/NodeScopeResolverTest.php +++ b/tests/PHPStan/Analyser/NodeScopeResolverTest.php @@ -1284,6 +1284,7 @@ public function dataFileAsserts(): iterable } yield from $this->gatherAssertTypes(__DIR__ . '/data/gettype.php'); + yield from $this->gatherAssertTypes(__DIR__ . '/data/json_validate.php'); } /** diff --git a/tests/PHPStan/Analyser/data/json_validate.php b/tests/PHPStan/Analyser/data/json_validate.php new file mode 100644 index 0000000000..ea6e216abb --- /dev/null +++ b/tests/PHPStan/Analyser/data/json_validate.php @@ -0,0 +1,28 @@ + Date: Tue, 27 Jun 2023 17:25:52 +0200 Subject: [PATCH 2/6] Scope test to PHP 8.3 --- tests/PHPStan/Analyser/NodeScopeResolverTest.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/PHPStan/Analyser/NodeScopeResolverTest.php b/tests/PHPStan/Analyser/NodeScopeResolverTest.php index 34126dcc38..dba9c9fa05 100644 --- a/tests/PHPStan/Analyser/NodeScopeResolverTest.php +++ b/tests/PHPStan/Analyser/NodeScopeResolverTest.php @@ -1283,8 +1283,10 @@ public function dataFileAsserts(): iterable yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-5782b-php7.php'); } + if (PHP_VERSION_ID >= 80300) { + yield from $this->gatherAssertTypes(__DIR__ . '/data/json_validate.php'); + } yield from $this->gatherAssertTypes(__DIR__ . '/data/gettype.php'); - yield from $this->gatherAssertTypes(__DIR__ . '/data/json_validate.php'); } /** From f00cb4affbeb327edc26d583ae2bfe54fa7f4995 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Wed, 28 Jun 2023 08:41:24 +0200 Subject: [PATCH 3/6] Added PHP8.3 signature delta map --- resources/functionMap_php83delta.php | 29 +++++++++++++++++++ .../FunctionSignatureMapProvider.php | 9 ++++++ .../CallToFunctionParametersRuleTest.php | 14 +++++++++ .../Rules/Functions/data/json_validate.php | 13 +++++++++ 4 files changed, 65 insertions(+) create mode 100644 resources/functionMap_php83delta.php create mode 100644 tests/PHPStan/Rules/Functions/data/json_validate.php diff --git a/resources/functionMap_php83delta.php b/resources/functionMap_php83delta.php new file mode 100644 index 0000000000..dbf547dd45 --- /dev/null +++ b/resources/functionMap_php83delta.php @@ -0,0 +1,29 @@ + [ + 'json_validate' => ['bool', 'json'=>'string', 'depth='=>'positive-int', 'flags='=>'int-mask'], + ], + 'old' => [ + + ] +]; diff --git a/src/Reflection/SignatureMap/FunctionSignatureMapProvider.php b/src/Reflection/SignatureMap/FunctionSignatureMapProvider.php index d87aa91f9f..f626f65ac1 100644 --- a/src/Reflection/SignatureMap/FunctionSignatureMapProvider.php +++ b/src/Reflection/SignatureMap/FunctionSignatureMapProvider.php @@ -233,6 +233,15 @@ public function getSignatureMap(): array $signatureMap = $this->computeSignatureMap($signatureMap, $php82MapDelta); } + if ($this->phpVersion->getVersionId() >= 80300) { + $php83MapDelta = require __DIR__ . '/../../../resources/functionMap_php83delta.php'; + if (!is_array($php83MapDelta)) { + throw new ShouldNotHappenException('Signature map could not be loaded.'); + } + + $signatureMap = $this->computeSignatureMap($signatureMap, $php83MapDelta); + } + $this->signatureMap = $signatureMap; } diff --git a/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php b/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php index 2863e9e36e..1661c62860 100644 --- a/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php +++ b/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php @@ -1388,4 +1388,18 @@ public function testFlockParams(): void ]); } + public function testJsonValidate(): void + { + $this->analyse([__DIR__ . '/data/json_validate.php'], [ + [ + 'Parameter #2 $depth of function json_validate expects int<1, max>, 0 given.', + 6, + ], + [ + 'Parameter #3 $flags of function json_validate expects 0|1048576, 2 given.', + 7 + ] + ]); + } + } diff --git a/tests/PHPStan/Rules/Functions/data/json_validate.php b/tests/PHPStan/Rules/Functions/data/json_validate.php new file mode 100644 index 0000000000..08c1268bb7 --- /dev/null +++ b/tests/PHPStan/Rules/Functions/data/json_validate.php @@ -0,0 +1,13 @@ + Date: Wed, 28 Jun 2023 08:44:59 +0200 Subject: [PATCH 4/6] cs --- .../Rules/Functions/CallToFunctionParametersRuleTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php b/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php index 1661c62860..0a72eed73a 100644 --- a/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php +++ b/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php @@ -1397,8 +1397,8 @@ public function testJsonValidate(): void ], [ 'Parameter #3 $flags of function json_validate expects 0|1048576, 2 given.', - 7 - ] + 7, + ], ]); } From 14214b6318fdf3b6567b00530f7a556f140211cf Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Wed, 28 Jun 2023 10:23:16 +0200 Subject: [PATCH 5/6] test requires PHP 8.3 --- .../Rules/Functions/CallToFunctionParametersRuleTest.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php b/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php index 0a72eed73a..fbfee4b77e 100644 --- a/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php +++ b/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php @@ -1390,6 +1390,10 @@ public function testFlockParams(): void public function testJsonValidate(): void { + if (PHP_VERSION_ID < 80300) { + $this->markTestSkipped('Test requires PHP 8.3'); + } + $this->analyse([__DIR__ . '/data/json_validate.php'], [ [ 'Parameter #2 $depth of function json_validate expects int<1, max>, 0 given.', From edd267bea2adbf4b424cbbc0088e3f5c3ccb056e Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Wed, 28 Jun 2023 13:30:38 +0200 Subject: [PATCH 6/6] fix type --- src/Type/Php/JsonValidateTypeSpecifyingExtension.php | 4 ++-- tests/PHPStan/Analyser/data/json_validate.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Type/Php/JsonValidateTypeSpecifyingExtension.php b/src/Type/Php/JsonValidateTypeSpecifyingExtension.php index cc64493c63..cd74d69149 100644 --- a/src/Type/Php/JsonValidateTypeSpecifyingExtension.php +++ b/src/Type/Php/JsonValidateTypeSpecifyingExtension.php @@ -9,7 +9,7 @@ use PHPStan\Analyser\TypeSpecifierAwareExtension; use PHPStan\Analyser\TypeSpecifierContext; use PHPStan\Reflection\FunctionReflection; -use PHPStan\Type\Accessory\AccessoryNonFalsyStringType; +use PHPStan\Type\Accessory\AccessoryNonEmptyStringType; use PHPStan\Type\FunctionTypeSpecifyingExtension; use function count; @@ -31,7 +31,7 @@ public function specifyTypes(FunctionReflection $functionReflection, FuncCall $n return new SpecifiedTypes([], []); } - return $this->typeSpecifier->create($node->getArgs()[0]->value, new AccessoryNonFalsyStringType(), $context, false, $scope); + return $this->typeSpecifier->create($node->getArgs()[0]->value, new AccessoryNonEmptyStringType(), $context, false, $scope); } public function setTypeSpecifier(TypeSpecifier $typeSpecifier): void diff --git a/tests/PHPStan/Analyser/data/json_validate.php b/tests/PHPStan/Analyser/data/json_validate.php index ea6e216abb..f288fbddcf 100644 --- a/tests/PHPStan/Analyser/data/json_validate.php +++ b/tests/PHPStan/Analyser/data/json_validate.php @@ -8,7 +8,7 @@ function doFoo($m): void { assertType('bool', json_validate($m)); if (json_validate($m)) { - assertType('non-falsy-string', $m); + assertType('non-empty-string', $m); } else { assertType('mixed', $m); } @@ -20,7 +20,7 @@ function doFoo($m): void { */ function doBar($nonES): void { if (json_validate($nonES)) { - assertType('non-falsy-string', $nonES); + assertType('non-empty-string', $nonES); } else { assertType('non-empty-string', $nonES); }