From e3bd9f2464dec4a7b6f60c664683b0779b024ea7 Mon Sep 17 00:00:00 2001 From: USAMI Kenta Date: Thu, 13 Apr 2023 02:54:14 +0900 Subject: [PATCH 1/5] Fix FilterVarArrayDynamicReturnTypeExtension for mixed input --- ...lterVarArrayDynamicReturnTypeExtension.php | 13 +++--- .../Analyser/data/filter-var-array.php | 46 +++++++++++++++++++ 2 files changed, 52 insertions(+), 7 deletions(-) diff --git a/src/Type/Php/FilterVarArrayDynamicReturnTypeExtension.php b/src/Type/Php/FilterVarArrayDynamicReturnTypeExtension.php index ac57eb12c0..36923531f4 100644 --- a/src/Type/Php/FilterVarArrayDynamicReturnTypeExtension.php +++ b/src/Type/Php/FilterVarArrayDynamicReturnTypeExtension.php @@ -46,7 +46,7 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection, $functionName = strtolower($functionReflection->getName()); $inputArgType = $scope->getType($functionCall->getArgs()[0]->value); - $inputArrayType = $inputArgType; + $inputArrayType = $inputArgType->getArrays()[0] ?? null; $inputConstantArrayType = null; if ($functionName === 'filter_var_array') { if ($inputArgType->isArray()->no()) { @@ -78,13 +78,10 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection, $addEmpty = $addEmptyType === null || $addEmptyType->isTrue()->yes(); $valueTypesBuilder = ConstantArrayTypeBuilder::createEmpty(); - $inputTypesMap = []; - $optionalKeys = []; if ($filterArgType instanceof ConstantIntegerType) { if ($inputConstantArrayType === null) { - $isList = $inputArrayType->isList()->yes(); - $inputArrayType = $inputArrayType->getArrays()[0] ?? null; + $isList = $inputArgType->isList()->yes(); $valueType = $this->filterFunctionReturnTypeHelper->getType( $inputArrayType === null ? new MixedType() : $inputArrayType->getItemType(), $filterArgType, @@ -116,7 +113,6 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection, } elseif ($filterConstantArrayType === null) { if ($inputConstantArrayType === null) { $isList = $inputArrayType->isList()->yes(); - $inputArrayType = $inputArrayType->getArrays()[0] ?? null; $valueType = $this->filterFunctionReturnTypeHelper->getType($inputArrayType ?? new MixedType(), $filterArgType, null); $arrayType = new ArrayType( @@ -148,7 +144,10 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection, } } else { $optionalKeys = $filterKeysList; - $inputTypesMap = array_fill_keys($optionalKeys, $inputArrayType->getArrays()[0]->getItemType()); + $inputTypesMap = array_fill_keys( + $optionalKeys, + $inputArrayType === null ? new MixedType() : $inputArrayType->getItemType() + ); } } diff --git a/tests/PHPStan/Analyser/data/filter-var-array.php b/tests/PHPStan/Analyser/data/filter-var-array.php index fc959f4d06..7a086e7ba8 100644 --- a/tests/PHPStan/Analyser/data/filter-var-array.php +++ b/tests/PHPStan/Analyser/data/filter-var-array.php @@ -67,6 +67,52 @@ function constantValues(): void ], false)); } +function constantValuesWithMixedInput(mixed $input): void +{ + // filter array with add_empty=default + assertType('array{id: int|false|null}', filter_var_array($input, [ + 'id' => FILTER_VALIDATE_INT, + ])); + + // filter array with add_empty=true + assertType('array{id: int|false|null}', filter_var_array($input, [ + 'id' => FILTER_VALIDATE_INT, + ], true)); + + // filter array with add_empty=false + assertType('array{id?: int|false}', filter_var_array($input, [ + 'id' => FILTER_VALIDATE_INT, + ], false)); + + // filter flag with add_empty=default + assertType('array', filter_var_array($input, FILTER_VALIDATE_INT)); + // filter flag with add_empty=true + assertType('array', filter_var_array($input, FILTER_VALIDATE_INT, true)); + // filter flag with add_empty=false + assertType('array', filter_var_array($input, FILTER_VALIDATE_INT, false)); + + $filter = [ + 'filter' => FILTER_VALIDATE_INT, + 'flag' => FILTER_REQUIRE_SCALAR, + 'options' => ['min_range' => 1, 'max_range' => 10], + ]; + + // filter array with add_empty=default + assertType('array{id: int<1, 10>|false|null}', filter_var_array($input, [ + 'id' => $filter, + ])); + + // filter array with add_empty=default + assertType('array{id: int<1, 10>|false|null}', filter_var_array($input, [ + 'id' => $filter, + ], true)); + + // filter array with add_empty=default + assertType('array{id?: int<1, 10>|false}', filter_var_array($input, [ + 'id' => $filter, + ], false)); +} + function emptyArrayInput(): void { // filter array with add_empty=default From fd280533a31aea91aef0fd31c3fcfdf7511afdc5 Mon Sep 17 00:00:00 2001 From: USAMI Kenta Date: Thu, 13 Apr 2023 03:01:23 +0900 Subject: [PATCH 2/5] fixup! Fix FilterVarArrayDynamicReturnTypeExtension for mixed input --- ...lterVarArrayDynamicReturnTypeExtension.php | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/Type/Php/FilterVarArrayDynamicReturnTypeExtension.php b/src/Type/Php/FilterVarArrayDynamicReturnTypeExtension.php index 36923531f4..ee82aa2110 100644 --- a/src/Type/Php/FilterVarArrayDynamicReturnTypeExtension.php +++ b/src/Type/Php/FilterVarArrayDynamicReturnTypeExtension.php @@ -72,6 +72,13 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection, $inputArrayType = new ArrayType(new StringType(), new MixedType()); } + if ($inputArrayType === null) { + if (!$inputArgType instanceof MixedType) { + return null; + } + $inputArrayType = new ArrayType(new MixedType(), new MixedType()); + } + $filterArgType = $scope->getType($functionCall->getArgs()[1]->value); $filterConstantArrayType = $filterArgType->getConstantArrays()[0] ?? null; $addEmptyType = isset($functionCall->getArgs()[2]) ? $scope->getType($functionCall->getArgs()[2]->value) : null; @@ -83,14 +90,11 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection, if ($inputConstantArrayType === null) { $isList = $inputArgType->isList()->yes(); $valueType = $this->filterFunctionReturnTypeHelper->getType( - $inputArrayType === null ? new MixedType() : $inputArrayType->getItemType(), + $inputArrayType->getItemType(), $filterArgType, null, ); - $arrayType = new ArrayType( - $inputArrayType !== null ? $inputArrayType->getKeyType() : new MixedType(), - $valueType, - ); + $arrayType = new ArrayType($inputArrayType->getKeyType(), $valueType); return $isList ? AccessoryArrayListType::intersectWith($arrayType) : $arrayType; } @@ -144,10 +148,7 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection, } } else { $optionalKeys = $filterKeysList; - $inputTypesMap = array_fill_keys( - $optionalKeys, - $inputArrayType === null ? new MixedType() : $inputArrayType->getItemType() - ); + $inputTypesMap = array_fill_keys($optionalKeys, $inputArrayType->getItemType()); } } From 4a01c301677f0fd473658ae75cd1e1f8e0ba5306 Mon Sep 17 00:00:00 2001 From: USAMI Kenta Date: Thu, 13 Apr 2023 03:03:59 +0900 Subject: [PATCH 3/5] fixup! fixup! Fix FilterVarArrayDynamicReturnTypeExtension for mixed input --- src/Type/Php/FilterVarArrayDynamicReturnTypeExtension.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Type/Php/FilterVarArrayDynamicReturnTypeExtension.php b/src/Type/Php/FilterVarArrayDynamicReturnTypeExtension.php index ee82aa2110..dc4390ea96 100644 --- a/src/Type/Php/FilterVarArrayDynamicReturnTypeExtension.php +++ b/src/Type/Php/FilterVarArrayDynamicReturnTypeExtension.php @@ -117,10 +117,10 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection, } elseif ($filterConstantArrayType === null) { if ($inputConstantArrayType === null) { $isList = $inputArrayType->isList()->yes(); - $valueType = $this->filterFunctionReturnTypeHelper->getType($inputArrayType ?? new MixedType(), $filterArgType, null); + $valueType = $this->filterFunctionReturnTypeHelper->getType($inputArrayType, $filterArgType, null); $arrayType = new ArrayType( - $inputArrayType !== null ? $inputArrayType->getKeyType() : new MixedType(), + $inputArrayType->getKeyType(), $addEmpty ? TypeCombinator::addNull($valueType) : $valueType, ); From 3297139fc5661ad0b960fac51c0db2fbcb34ff4f Mon Sep 17 00:00:00 2001 From: USAMI Kenta Date: Thu, 13 Apr 2023 03:18:33 +0900 Subject: [PATCH 4/5] fixup! Fix FilterVarArrayDynamicReturnTypeExtension for mixed input --- tests/PHPStan/Analyser/data/filter-var-array.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/PHPStan/Analyser/data/filter-var-array.php b/tests/PHPStan/Analyser/data/filter-var-array.php index 7a086e7ba8..746ad08c96 100644 --- a/tests/PHPStan/Analyser/data/filter-var-array.php +++ b/tests/PHPStan/Analyser/data/filter-var-array.php @@ -67,7 +67,7 @@ function constantValues(): void ], false)); } -function constantValuesWithMixedInput(mixed $input): void +function mixedInput(mixed $input): void { // filter array with add_empty=default assertType('array{id: int|false|null}', filter_var_array($input, [ From 29e7bb7ec2d49c53c06b2ca3a241121a53ad9fd8 Mon Sep 17 00:00:00 2001 From: USAMI Kenta Date: Thu, 13 Apr 2023 03:40:04 +0900 Subject: [PATCH 5/5] fixup! Fix FilterVarArrayDynamicReturnTypeExtension for mixed input --- src/Type/Php/FilterVarArrayDynamicReturnTypeExtension.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Type/Php/FilterVarArrayDynamicReturnTypeExtension.php b/src/Type/Php/FilterVarArrayDynamicReturnTypeExtension.php index dc4390ea96..c6a4c34523 100644 --- a/src/Type/Php/FilterVarArrayDynamicReturnTypeExtension.php +++ b/src/Type/Php/FilterVarArrayDynamicReturnTypeExtension.php @@ -73,10 +73,10 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection, } if ($inputArrayType === null) { - if (!$inputArgType instanceof MixedType) { + $inputArrayType = new ArrayType(new MixedType(), new MixedType()); + if ($inputArgType->isSuperTypeOf($inputArrayType)->no()) { return null; } - $inputArrayType = new ArrayType(new MixedType(), new MixedType()); } $filterArgType = $scope->getType($functionCall->getArgs()[1]->value);