From 03e8d19eecc4a59febe107664a3e877f30fb7e77 Mon Sep 17 00:00:00 2001 From: robchett Date: Sat, 16 Sep 2023 15:26:52 +0100 Subject: [PATCH 1/3] Only the binary op 'plus' works with two arrays Treat the result of any other operation as int|float Fixes #2123 --- .../BinaryOp/ArithmeticOpAnalyzer.php | 164 +++++++++--------- tests/BinaryOperationTest.php | 52 ++++++ 2 files changed, 134 insertions(+), 82 deletions(-) diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/ArithmeticOpAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/ArithmeticOpAnalyzer.php index 36ae21d63..4e2b30f63 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/ArithmeticOpAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/ArithmeticOpAnalyzer.php @@ -542,97 +542,97 @@ class ArithmeticOpAnalyzer $has_valid_right_operand = true; } - $result_type = Type::getArray(); - return null; } - $has_valid_right_operand = true; - $has_valid_left_operand = true; + if ($parent instanceof PhpParser\Node\Expr\BinaryOp\Plus) { + $has_valid_right_operand = true; + $has_valid_left_operand = true; - if ($left_type_part instanceof TKeyedArray - && $right_type_part instanceof TKeyedArray - ) { - $definitely_existing_mixed_right_properties = array_diff_key( - $right_type_part->properties, - $left_type_part->properties, - ); - - $properties = $left_type_part->properties; - - foreach ($right_type_part->properties as $key => $type) { - if (!isset($properties[$key])) { - $properties[$key] = $type; - } elseif ($properties[$key]->possibly_undefined) { - $properties[$key] = Type::combineUnionTypes( - $properties[$key], - $type, - $codebase, - false, - true, - 500, - $type->possibly_undefined, - ); - } - } - - if ($left_type_part->fallback_params !== null) { - foreach ($definitely_existing_mixed_right_properties as $key => $type) { - $properties[$key] = Type::combineUnionTypes(Type::getMixed(), $type); - } - } - - if ($left_type_part->fallback_params === null - && $right_type_part->fallback_params === null + if ($left_type_part instanceof TKeyedArray + && $right_type_part instanceof TKeyedArray ) { - $fallback_params = null; - } elseif ($left_type_part->fallback_params !== null - && $right_type_part->fallback_params !== null - ) { - $fallback_params = [ - Type::combineUnionTypes( - $left_type_part->fallback_params[0], - $right_type_part->fallback_params[0], - ), - Type::combineUnionTypes( - $left_type_part->fallback_params[1], - $right_type_part->fallback_params[1], - ), - ]; + $definitely_existing_mixed_right_properties = array_diff_key( + $right_type_part->properties, + $left_type_part->properties, + ); + + $properties = $left_type_part->properties; + + foreach ($right_type_part->properties as $key => $type) { + if (!isset($properties[$key])) { + $properties[$key] = $type; + } elseif ($properties[$key]->possibly_undefined) { + $properties[$key] = Type::combineUnionTypes( + $properties[$key], + $type, + $codebase, + false, + true, + 500, + $type->possibly_undefined, + ); + } + } + + if ($left_type_part->fallback_params !== null) { + foreach ($definitely_existing_mixed_right_properties as $key => $type) { + $properties[$key] = Type::combineUnionTypes(Type::getMixed(), $type); + } + } + + if ($left_type_part->fallback_params === null + && $right_type_part->fallback_params === null + ) { + $fallback_params = null; + } elseif ($left_type_part->fallback_params !== null + && $right_type_part->fallback_params !== null + ) { + $fallback_params = [ + Type::combineUnionTypes( + $left_type_part->fallback_params[0], + $right_type_part->fallback_params[0], + ), + Type::combineUnionTypes( + $left_type_part->fallback_params[1], + $right_type_part->fallback_params[1], + ), + ]; + } else { + $fallback_params = $left_type_part->fallback_params ?: $right_type_part->fallback_params; + } + + $new_keyed_array = new TKeyedArray( + $properties, + null, + $fallback_params, + ); + $result_type_member = new Union([$new_keyed_array]); } else { - $fallback_params = $left_type_part->fallback_params ?: $right_type_part->fallback_params; + $result_type_member = TypeCombiner::combine( + [$left_type_part, $right_type_part], + $codebase, + true, + ); } - $new_keyed_array = new TKeyedArray( - $properties, - null, - $fallback_params, - ); - $result_type_member = new Union([$new_keyed_array]); - } else { - $result_type_member = TypeCombiner::combine( - [$left_type_part, $right_type_part], - $codebase, - true, - ); + $result_type = Type::combineUnionTypes($result_type_member, $result_type, $codebase, true); + + if ($left instanceof PhpParser\Node\Expr\ArrayDimFetch + && $context + && $statements_source instanceof StatementsAnalyzer + ) { + ArrayAssignmentAnalyzer::updateArrayType( + $statements_source, + $left, + $right, + $result_type, + $context, + ); + } + + return null; } - - $result_type = Type::combineUnionTypes($result_type_member, $result_type, $codebase, true); - - if ($left instanceof PhpParser\Node\Expr\ArrayDimFetch - && $context - && $statements_source instanceof StatementsAnalyzer - ) { - ArrayAssignmentAnalyzer::updateArrayType( - $statements_source, - $left, - $right, - $result_type, - $context, - ); - } - - return null; } if (($left_type_part instanceof TNamedObject && strtolower($left_type_part->value) === 'gmp') diff --git a/tests/BinaryOperationTest.php b/tests/BinaryOperationTest.php index 3ac18147f..f0fe0c9e3 100644 --- a/tests/BinaryOperationTest.php +++ b/tests/BinaryOperationTest.php @@ -1022,6 +1022,58 @@ class BinaryOperationTest extends TestCase '$a===' => 'float(9.2233720368548E+18)', ], ], + 'invalidArrayOperations' => [ + 'code' => <<<'PHP' + [ + '$a1' => 'float|int', + '$a2' => 'float|int', + '$a3' => 'array', + '$b1' => 'float|int', + '$b2' => 'float|int', + '$b3' => 'float|int', + '$c1' => 'float|int', + '$c2' => 'float|int', + '$c3' => 'float|int', + '$d1' => 'float|int', + '$d2' => 'float|int', + '$d3' => 'float|int', + '$e1' => 'float|int', + '$e2' => 'float|int', + '$e3' => 'float|int', + '$f1' => 'float|int', + '$f2' => 'float|int', + '$f3' => 'float|int', + ], + 'ignored_issues' => ['InvalidOperand'], + ], ]; } From b96d5a3eba40b5b52bd8ab0f100ea134ad69f3ae Mon Sep 17 00:00:00 2001 From: robchett Date: Sun, 17 Sep 2023 11:37:52 +0100 Subject: [PATCH 2/3] Add openssl_*_export_to_file to impure functions --- dictionaries/ImpureFunctionsList.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dictionaries/ImpureFunctionsList.php b/dictionaries/ImpureFunctionsList.php index c46c2568a..70b1fad7d 100644 --- a/dictionaries/ImpureFunctionsList.php +++ b/dictionaries/ImpureFunctionsList.php @@ -247,4 +247,9 @@ return [ 'hash_update_stream' => true, // unserialize 'unserialize' => true, + // openssl + 'openssl_csr_export_to_file' => true, + 'openssl_pkcs12_export_to_file' => true, + 'openssl_pkey_export_to_file' => true, + 'openssl_x509_export_to_file' => true, ]; From c6648aff8780c94be3051c76576c48104062879f Mon Sep 17 00:00:00 2001 From: kkmuffme <11071985+kkmuffme@users.noreply.github.com> Date: Mon, 18 Sep 2023 14:47:34 +0200 Subject: [PATCH 3/3] add FILTER_SANITIZE_URL filter to filter_var --- .../Provider/ReturnTypeProvider/FilterVarReturnTypeProvider.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/FilterVarReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/FilterVarReturnTypeProvider.php index cbc06baf8..19c839c44 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/FilterVarReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/FilterVarReturnTypeProvider.php @@ -17,6 +17,7 @@ use UnexpectedValueException; use function in_array; use const FILTER_NULL_ON_FAILURE; +use const FILTER_SANITIZE_URL; use const FILTER_VALIDATE_BOOLEAN; use const FILTER_VALIDATE_DOMAIN; use const FILTER_VALIDATE_EMAIL; @@ -78,6 +79,7 @@ class FilterVarReturnTypeProvider implements FunctionReturnTypeProviderInterface case FILTER_VALIDATE_URL: case FILTER_VALIDATE_EMAIL: case FILTER_VALIDATE_DOMAIN: + case FILTER_SANITIZE_URL: $filter_type = Type::getString(); break; }