From 38f6006eb6d1d778797628742cad7b6764698ad5 Mon Sep 17 00:00:00 2001 From: phpstan-bot <79867460+phpstan-bot@users.noreply.github.com> Date: Wed, 15 Apr 2026 18:47:39 +0000 Subject: [PATCH 1/7] Fix phpstan/phpstan#14473: false positive logicalOr.leftAlwaysTrue on repeated builtin function call When a builtin function with possible side effects (like mysqli_connect()) was assigned with the `or die()` pattern, the conditional expression mechanism stored a narrowed type for the function call expression in the scope. On a subsequent assignment of the same function call, this stale type was retrieved instead of the fresh return type, causing a false positive "left side of or is always true" error. The fix skips creating conditional expressions for FuncCall expressions that refer to builtin functions with possible side effects, since those function calls can return different values each time and the narrowed type should not persist across assignments. --- src/Analyser/ExprHandler/AssignHandler.php | 27 +++++++++++++++++-- .../BooleanOrConstantConditionRuleTest.php | 7 +++++ .../Rules/Comparison/data/bug-14473.php | 27 +++++++++++++++++++ 3 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 tests/PHPStan/Rules/Comparison/data/bug-14473.php diff --git a/src/Analyser/ExprHandler/AssignHandler.php b/src/Analyser/ExprHandler/AssignHandler.php index 51995ac6657..d1cfad35792 100644 --- a/src/Analyser/ExprHandler/AssignHandler.php +++ b/src/Analyser/ExprHandler/AssignHandler.php @@ -45,6 +45,7 @@ use PHPStan\Node\PropertyAssignNode; use PHPStan\Node\VariableAssignNode; use PHPStan\Php\PhpVersion; +use PHPStan\Reflection\ReflectionProvider; use PHPStan\ShouldNotHappenException; use PHPStan\TrinaryLogic; use PHPStan\Type\Accessory\AccessoryArrayListType; @@ -84,6 +85,7 @@ final class AssignHandler implements ExprHandler public function __construct( private TypeSpecifier $typeSpecifier, private PhpVersion $phpVersion, + private ReflectionProvider $reflectionProvider, ) { } @@ -861,10 +863,13 @@ private function processSureTypesForConditionalExpressionsAfterAssign(Scope $sco if ($expr->name === $variableName) { continue; } + } elseif ($expr instanceof FuncCall) { + if ($this->isBuiltinFunctionCallWithPossibleSideEffects($expr, $scope)) { + continue; + } } elseif ( !$expr instanceof PropertyFetch && !$expr instanceof ArrayDimFetch - && !$expr instanceof FuncCall ) { continue; } @@ -900,10 +905,13 @@ private function processSureNotTypesForConditionalExpressionsAfterAssign(Scope $ if ($expr->name === $variableName) { continue; } + } elseif ($expr instanceof FuncCall) { + if ($this->isBuiltinFunctionCallWithPossibleSideEffects($expr, $scope)) { + continue; + } } elseif ( !$expr instanceof PropertyFetch && !$expr instanceof ArrayDimFetch - && !$expr instanceof FuncCall ) { continue; } @@ -924,6 +932,21 @@ private function processSureNotTypesForConditionalExpressionsAfterAssign(Scope $ return $conditionalExpressions; } + private function isBuiltinFunctionCallWithPossibleSideEffects(Expr $expr, Scope $scope): bool + { + if (!$expr instanceof FuncCall || !$expr->name instanceof Name) { + return false; + } + + if (!$this->reflectionProvider->hasFunction($expr->name, $scope)) { + return false; + } + + $functionReflection = $this->reflectionProvider->getFunction($expr->name, $scope); + + return $functionReflection->isBuiltin() && !$functionReflection->hasSideEffects()->no(); + } + /** * @param list $dimFetchStack */ diff --git a/tests/PHPStan/Rules/Comparison/BooleanOrConstantConditionRuleTest.php b/tests/PHPStan/Rules/Comparison/BooleanOrConstantConditionRuleTest.php index cc628575f79..31397bf28b0 100644 --- a/tests/PHPStan/Rules/Comparison/BooleanOrConstantConditionRuleTest.php +++ b/tests/PHPStan/Rules/Comparison/BooleanOrConstantConditionRuleTest.php @@ -384,4 +384,11 @@ public function testBug10305(): void ]); } + public function testBug14473(): void + { + $this->treatPhpDocTypesAsCertain = true; + + $this->analyse([__DIR__ . '/data/bug-14473.php'], []); + } + } diff --git a/tests/PHPStan/Rules/Comparison/data/bug-14473.php b/tests/PHPStan/Rules/Comparison/data/bug-14473.php new file mode 100644 index 00000000000..c45fa4c05e5 --- /dev/null +++ b/tests/PHPStan/Rules/Comparison/data/bug-14473.php @@ -0,0 +1,27 @@ + Date: Wed, 15 Apr 2026 19:20:54 +0000 Subject: [PATCH 2/7] Revert AssignHandler changes for bug #14473 The fix for the false positive logicalOr.leftAlwaysTrue on repeated builtin function calls does not require code changes to AssignHandler. The root cause is that TypeSpecifier already returns empty SpecifiedTypes for functions with hasSideEffects()->yes() (TypeSpecifier.php line 2246), which prevents conditional expressions from being created in AssignHandler. The proper fix is to mark mysqli_connect in the function metadata, which is done in the next commit. Co-Authored-By: Claude Opus 4.6 --- src/Analyser/ExprHandler/AssignHandler.php | 27 ++-------------------- 1 file changed, 2 insertions(+), 25 deletions(-) diff --git a/src/Analyser/ExprHandler/AssignHandler.php b/src/Analyser/ExprHandler/AssignHandler.php index d1cfad35792..51995ac6657 100644 --- a/src/Analyser/ExprHandler/AssignHandler.php +++ b/src/Analyser/ExprHandler/AssignHandler.php @@ -45,7 +45,6 @@ use PHPStan\Node\PropertyAssignNode; use PHPStan\Node\VariableAssignNode; use PHPStan\Php\PhpVersion; -use PHPStan\Reflection\ReflectionProvider; use PHPStan\ShouldNotHappenException; use PHPStan\TrinaryLogic; use PHPStan\Type\Accessory\AccessoryArrayListType; @@ -85,7 +84,6 @@ final class AssignHandler implements ExprHandler public function __construct( private TypeSpecifier $typeSpecifier, private PhpVersion $phpVersion, - private ReflectionProvider $reflectionProvider, ) { } @@ -863,13 +861,10 @@ private function processSureTypesForConditionalExpressionsAfterAssign(Scope $sco if ($expr->name === $variableName) { continue; } - } elseif ($expr instanceof FuncCall) { - if ($this->isBuiltinFunctionCallWithPossibleSideEffects($expr, $scope)) { - continue; - } } elseif ( !$expr instanceof PropertyFetch && !$expr instanceof ArrayDimFetch + && !$expr instanceof FuncCall ) { continue; } @@ -905,13 +900,10 @@ private function processSureNotTypesForConditionalExpressionsAfterAssign(Scope $ if ($expr->name === $variableName) { continue; } - } elseif ($expr instanceof FuncCall) { - if ($this->isBuiltinFunctionCallWithPossibleSideEffects($expr, $scope)) { - continue; - } } elseif ( !$expr instanceof PropertyFetch && !$expr instanceof ArrayDimFetch + && !$expr instanceof FuncCall ) { continue; } @@ -932,21 +924,6 @@ private function processSureNotTypesForConditionalExpressionsAfterAssign(Scope $ return $conditionalExpressions; } - private function isBuiltinFunctionCallWithPossibleSideEffects(Expr $expr, Scope $scope): bool - { - if (!$expr instanceof FuncCall || !$expr->name instanceof Name) { - return false; - } - - if (!$this->reflectionProvider->hasFunction($expr->name, $scope)) { - return false; - } - - $functionReflection = $this->reflectionProvider->getFunction($expr->name, $scope); - - return $functionReflection->isBuiltin() && !$functionReflection->hasSideEffects()->no(); - } - /** * @param list $dimFetchStack */ From 36db1ffe434fc74dcf6454813cbf79d3b2eb93cf Mon Sep 17 00:00:00 2001 From: phpstan-bot Date: Wed, 15 Apr 2026 19:21:00 +0000 Subject: [PATCH 3/7] Mark mysqli_connect as having side effects in function metadata mysqli_connect establishes a network connection to a database server, which is inherently a side effect. Marking it as hasSideEffects => true causes TypeSpecifier to return empty SpecifiedTypes for the call, which prevents stale conditional expressions from being created and fixes the false positive logicalOr.leftAlwaysTrue on repeated calls. Fixes phpstan/phpstan#14473 Co-Authored-By: Claude Opus 4.6 --- bin/functionMetadata_original.php | 1 + resources/functionMetadata.php | 1 + 2 files changed, 2 insertions(+) diff --git a/bin/functionMetadata_original.php b/bin/functionMetadata_original.php index 17be4631a9e..f2e0a8f9c20 100644 --- a/bin/functionMetadata_original.php +++ b/bin/functionMetadata_original.php @@ -94,6 +94,7 @@ 'link' => ['hasSideEffects' => true], 'mkdir' => ['hasSideEffects' => true], 'move_uploaded_file' => ['hasSideEffects' => true], + 'mysqli_connect' => ['hasSideEffects' => true], 'ob_clean' => ['hasSideEffects' => true], 'ob_end_clean' => ['hasSideEffects' => true], 'ob_end_flush' => ['hasSideEffects' => true], diff --git a/resources/functionMetadata.php b/resources/functionMetadata.php index a01a7181a6e..d7f43b3ea3b 100644 --- a/resources/functionMetadata.php +++ b/resources/functionMetadata.php @@ -1392,6 +1392,7 @@ 'msgfmt_parse_message' => ['hasSideEffects' => false], 'mt_getrandmax' => ['hasSideEffects' => false], 'mt_rand' => ['hasSideEffects' => true], + 'mysqli_connect' => ['hasSideEffects' => true], 'net_get_interfaces' => ['hasSideEffects' => false], 'ngettext' => ['hasSideEffects' => false], 'nl2br' => ['hasSideEffects' => false], From 861bdde22c211dc7d15da939f309f65df9ffe110 Mon Sep 17 00:00:00 2001 From: phpstan-bot Date: Wed, 15 Apr 2026 20:10:11 +0000 Subject: [PATCH 4/7] Mark all side-effectful mysqli_ functions in function metadata In response to review feedback: many other mysqli_ functions beyond mysqli_connect have side effects (network I/O, state mutation, cursor advancement) and were defaulting to hasSideEffects=maybe. This could cause the same false positive as phpstan/phpstan#14473 for any of these functions when used with the `or die()` pattern. Added 56 additional mysqli_ functions covering connection management, query execution, transactions, result fetching, cursor positioning, prepared statements, and administrative operations. Co-Authored-By: Claude Opus 4.6 --- bin/functionMetadata_original.php | 56 +++++++++++++++++++++++++++++++ resources/functionMetadata.php | 56 +++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+) diff --git a/bin/functionMetadata_original.php b/bin/functionMetadata_original.php index f2e0a8f9c20..ab615afcb20 100644 --- a/bin/functionMetadata_original.php +++ b/bin/functionMetadata_original.php @@ -94,7 +94,63 @@ 'link' => ['hasSideEffects' => true], 'mkdir' => ['hasSideEffects' => true], 'move_uploaded_file' => ['hasSideEffects' => true], + 'mysqli_autocommit' => ['hasSideEffects' => true], + 'mysqli_begin_transaction' => ['hasSideEffects' => true], + 'mysqli_change_user' => ['hasSideEffects' => true], + 'mysqli_close' => ['hasSideEffects' => true], + 'mysqli_commit' => ['hasSideEffects' => true], 'mysqli_connect' => ['hasSideEffects' => true], + 'mysqli_data_seek' => ['hasSideEffects' => true], + 'mysqli_debug' => ['hasSideEffects' => true], + 'mysqli_dump_debug_info' => ['hasSideEffects' => true], + 'mysqli_fetch_all' => ['hasSideEffects' => true], + 'mysqli_fetch_array' => ['hasSideEffects' => true], + 'mysqli_fetch_assoc' => ['hasSideEffects' => true], + 'mysqli_fetch_column' => ['hasSideEffects' => true], + 'mysqli_fetch_field' => ['hasSideEffects' => true], + 'mysqli_fetch_field_direct' => ['hasSideEffects' => true], + 'mysqli_fetch_object' => ['hasSideEffects' => true], + 'mysqli_fetch_row' => ['hasSideEffects' => true], + 'mysqli_field_seek' => ['hasSideEffects' => true], + 'mysqli_free_result' => ['hasSideEffects' => true], + 'mysqli_init' => ['hasSideEffects' => true], + 'mysqli_kill' => ['hasSideEffects' => true], + 'mysqli_multi_query' => ['hasSideEffects' => true], + 'mysqli_next_result' => ['hasSideEffects' => true], + 'mysqli_options' => ['hasSideEffects' => true], + 'mysqli_ping' => ['hasSideEffects' => true], + 'mysqli_poll' => ['hasSideEffects' => true], + 'mysqli_prepare' => ['hasSideEffects' => true], + 'mysqli_query' => ['hasSideEffects' => true], + 'mysqli_real_connect' => ['hasSideEffects' => true], + 'mysqli_real_query' => ['hasSideEffects' => true], + 'mysqli_reap_async_query' => ['hasSideEffects' => true], + 'mysqli_refresh' => ['hasSideEffects' => true], + 'mysqli_release_savepoint' => ['hasSideEffects' => true], + 'mysqli_report' => ['hasSideEffects' => true], + 'mysqli_rollback' => ['hasSideEffects' => true], + 'mysqli_savepoint' => ['hasSideEffects' => true], + 'mysqli_select_db' => ['hasSideEffects' => true], + 'mysqli_set_charset' => ['hasSideEffects' => true], + 'mysqli_ssl_set' => ['hasSideEffects' => true], + 'mysqli_stat' => ['hasSideEffects' => true], + 'mysqli_stmt_attr_set' => ['hasSideEffects' => true], + 'mysqli_stmt_bind_param' => ['hasSideEffects' => true], + 'mysqli_stmt_bind_result' => ['hasSideEffects' => true], + 'mysqli_stmt_close' => ['hasSideEffects' => true], + 'mysqli_stmt_data_seek' => ['hasSideEffects' => true], + 'mysqli_stmt_execute' => ['hasSideEffects' => true], + 'mysqli_stmt_fetch' => ['hasSideEffects' => true], + 'mysqli_stmt_free_result' => ['hasSideEffects' => true], + 'mysqli_stmt_get_result' => ['hasSideEffects' => true], + 'mysqli_stmt_init' => ['hasSideEffects' => true], + 'mysqli_stmt_next_result' => ['hasSideEffects' => true], + 'mysqli_stmt_prepare' => ['hasSideEffects' => true], + 'mysqli_stmt_reset' => ['hasSideEffects' => true], + 'mysqli_stmt_send_long_data' => ['hasSideEffects' => true], + 'mysqli_stmt_store_result' => ['hasSideEffects' => true], + 'mysqli_store_result' => ['hasSideEffects' => true], + 'mysqli_use_result' => ['hasSideEffects' => true], 'ob_clean' => ['hasSideEffects' => true], 'ob_end_clean' => ['hasSideEffects' => true], 'ob_end_flush' => ['hasSideEffects' => true], diff --git a/resources/functionMetadata.php b/resources/functionMetadata.php index d7f43b3ea3b..9c5c26200f2 100644 --- a/resources/functionMetadata.php +++ b/resources/functionMetadata.php @@ -1392,7 +1392,63 @@ 'msgfmt_parse_message' => ['hasSideEffects' => false], 'mt_getrandmax' => ['hasSideEffects' => false], 'mt_rand' => ['hasSideEffects' => true], + 'mysqli_autocommit' => ['hasSideEffects' => true], + 'mysqli_begin_transaction' => ['hasSideEffects' => true], + 'mysqli_change_user' => ['hasSideEffects' => true], + 'mysqli_close' => ['hasSideEffects' => true], + 'mysqli_commit' => ['hasSideEffects' => true], 'mysqli_connect' => ['hasSideEffects' => true], + 'mysqli_data_seek' => ['hasSideEffects' => true], + 'mysqli_debug' => ['hasSideEffects' => true], + 'mysqli_dump_debug_info' => ['hasSideEffects' => true], + 'mysqli_fetch_all' => ['hasSideEffects' => true], + 'mysqli_fetch_array' => ['hasSideEffects' => true], + 'mysqli_fetch_assoc' => ['hasSideEffects' => true], + 'mysqli_fetch_column' => ['hasSideEffects' => true], + 'mysqli_fetch_field' => ['hasSideEffects' => true], + 'mysqli_fetch_field_direct' => ['hasSideEffects' => true], + 'mysqli_fetch_object' => ['hasSideEffects' => true], + 'mysqli_fetch_row' => ['hasSideEffects' => true], + 'mysqli_field_seek' => ['hasSideEffects' => true], + 'mysqli_free_result' => ['hasSideEffects' => true], + 'mysqli_init' => ['hasSideEffects' => true], + 'mysqli_kill' => ['hasSideEffects' => true], + 'mysqli_multi_query' => ['hasSideEffects' => true], + 'mysqli_next_result' => ['hasSideEffects' => true], + 'mysqli_options' => ['hasSideEffects' => true], + 'mysqli_ping' => ['hasSideEffects' => true], + 'mysqli_poll' => ['hasSideEffects' => true], + 'mysqli_prepare' => ['hasSideEffects' => true], + 'mysqli_query' => ['hasSideEffects' => true], + 'mysqli_real_connect' => ['hasSideEffects' => true], + 'mysqli_real_query' => ['hasSideEffects' => true], + 'mysqli_reap_async_query' => ['hasSideEffects' => true], + 'mysqli_refresh' => ['hasSideEffects' => true], + 'mysqli_release_savepoint' => ['hasSideEffects' => true], + 'mysqli_report' => ['hasSideEffects' => true], + 'mysqli_rollback' => ['hasSideEffects' => true], + 'mysqli_savepoint' => ['hasSideEffects' => true], + 'mysqli_select_db' => ['hasSideEffects' => true], + 'mysqli_set_charset' => ['hasSideEffects' => true], + 'mysqli_ssl_set' => ['hasSideEffects' => true], + 'mysqli_stat' => ['hasSideEffects' => true], + 'mysqli_stmt_attr_set' => ['hasSideEffects' => true], + 'mysqli_stmt_bind_param' => ['hasSideEffects' => true], + 'mysqli_stmt_bind_result' => ['hasSideEffects' => true], + 'mysqli_stmt_close' => ['hasSideEffects' => true], + 'mysqli_stmt_data_seek' => ['hasSideEffects' => true], + 'mysqli_stmt_execute' => ['hasSideEffects' => true], + 'mysqli_stmt_fetch' => ['hasSideEffects' => true], + 'mysqli_stmt_free_result' => ['hasSideEffects' => true], + 'mysqli_stmt_get_result' => ['hasSideEffects' => true], + 'mysqli_stmt_init' => ['hasSideEffects' => true], + 'mysqli_stmt_next_result' => ['hasSideEffects' => true], + 'mysqli_stmt_prepare' => ['hasSideEffects' => true], + 'mysqli_stmt_reset' => ['hasSideEffects' => true], + 'mysqli_stmt_send_long_data' => ['hasSideEffects' => true], + 'mysqli_stmt_store_result' => ['hasSideEffects' => true], + 'mysqli_store_result' => ['hasSideEffects' => true], + 'mysqli_use_result' => ['hasSideEffects' => true], 'net_get_interfaces' => ['hasSideEffects' => false], 'ngettext' => ['hasSideEffects' => false], 'nl2br' => ['hasSideEffects' => false], From 00cce37af850aab6dae0712d7ca8d37215b40c1e Mon Sep 17 00:00:00 2001 From: phpstan-bot Date: Wed, 15 Apr 2026 20:36:10 +0000 Subject: [PATCH 5/7] Mark all remaining mysqli_ functions as having side effects All mysqli_* functions interact with database/connection state and are not pure. Functions like mysqli_affected_rows depend on the last query, mysqli_errno/mysqli_error depend on the last operation's error state, mysqli_real_escape_string depends on the connection charset, etc. This adds the 59 previously missing mysqli_ functions including: - State readers: mysqli_affected_rows, mysqli_errno, mysqli_error, mysqli_error_list, mysqli_insert_id, mysqli_info, mysqli_sqlstate, mysqli_warning_count, mysqli_num_rows, mysqli_num_fields, etc. - Aliases: mysqli_bind_param, mysqli_bind_result, mysqli_execute, mysqli_fetch, mysqli_escape_string, mysqli_client_encoding, etc. - Connection info: mysqli_get_host_info, mysqli_get_server_info, mysqli_get_server_version, mysqli_get_charset, etc. - Statement state: mysqli_stmt_affected_rows, mysqli_stmt_errno, mysqli_stmt_error, mysqli_stmt_num_rows, etc. Co-Authored-By: Claude Opus 4.6 --- bin/functionMetadata_original.php | 59 +++++++++++++++++++++++++++++++ resources/functionMetadata.php | 59 +++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+) diff --git a/bin/functionMetadata_original.php b/bin/functionMetadata_original.php index ab615afcb20..1d24b4421f9 100644 --- a/bin/functionMetadata_original.php +++ b/bin/functionMetadata_original.php @@ -94,35 +94,73 @@ 'link' => ['hasSideEffects' => true], 'mkdir' => ['hasSideEffects' => true], 'move_uploaded_file' => ['hasSideEffects' => true], + 'mysqli_affected_rows' => ['hasSideEffects' => true], 'mysqli_autocommit' => ['hasSideEffects' => true], 'mysqli_begin_transaction' => ['hasSideEffects' => true], + 'mysqli_bind_param' => ['hasSideEffects' => true], + 'mysqli_bind_result' => ['hasSideEffects' => true], 'mysqli_change_user' => ['hasSideEffects' => true], + 'mysqli_character_set_name' => ['hasSideEffects' => true], + 'mysqli_client_encoding' => ['hasSideEffects' => true], 'mysqli_close' => ['hasSideEffects' => true], 'mysqli_commit' => ['hasSideEffects' => true], 'mysqli_connect' => ['hasSideEffects' => true], + 'mysqli_connect_errno' => ['hasSideEffects' => true], + 'mysqli_connect_error' => ['hasSideEffects' => true], 'mysqli_data_seek' => ['hasSideEffects' => true], 'mysqli_debug' => ['hasSideEffects' => true], 'mysqli_dump_debug_info' => ['hasSideEffects' => true], + 'mysqli_errno' => ['hasSideEffects' => true], + 'mysqli_error' => ['hasSideEffects' => true], + 'mysqli_error_list' => ['hasSideEffects' => true], + 'mysqli_escape_string' => ['hasSideEffects' => true], + 'mysqli_execute' => ['hasSideEffects' => true], + 'mysqli_execute_query' => ['hasSideEffects' => true], + 'mysqli_fetch' => ['hasSideEffects' => true], 'mysqli_fetch_all' => ['hasSideEffects' => true], 'mysqli_fetch_array' => ['hasSideEffects' => true], 'mysqli_fetch_assoc' => ['hasSideEffects' => true], 'mysqli_fetch_column' => ['hasSideEffects' => true], 'mysqli_fetch_field' => ['hasSideEffects' => true], 'mysqli_fetch_field_direct' => ['hasSideEffects' => true], + 'mysqli_fetch_fields' => ['hasSideEffects' => true], + 'mysqli_fetch_lengths' => ['hasSideEffects' => true], 'mysqli_fetch_object' => ['hasSideEffects' => true], 'mysqli_fetch_row' => ['hasSideEffects' => true], + 'mysqli_field_count' => ['hasSideEffects' => true], 'mysqli_field_seek' => ['hasSideEffects' => true], + 'mysqli_field_tell' => ['hasSideEffects' => true], 'mysqli_free_result' => ['hasSideEffects' => true], + 'mysqli_get_cache_stats' => ['hasSideEffects' => true], + 'mysqli_get_charset' => ['hasSideEffects' => true], + 'mysqli_get_client_info' => ['hasSideEffects' => true], + 'mysqli_get_client_stats' => ['hasSideEffects' => true], + 'mysqli_get_client_version' => ['hasSideEffects' => true], + 'mysqli_get_connection_stats' => ['hasSideEffects' => true], + 'mysqli_get_host_info' => ['hasSideEffects' => true], + 'mysqli_get_links_stats' => ['hasSideEffects' => true], + 'mysqli_get_metadata' => ['hasSideEffects' => true], + 'mysqli_get_proto_info' => ['hasSideEffects' => true], + 'mysqli_get_server_info' => ['hasSideEffects' => true], + 'mysqli_get_server_version' => ['hasSideEffects' => true], + 'mysqli_get_warnings' => ['hasSideEffects' => true], + 'mysqli_info' => ['hasSideEffects' => true], 'mysqli_init' => ['hasSideEffects' => true], + 'mysqli_insert_id' => ['hasSideEffects' => true], 'mysqli_kill' => ['hasSideEffects' => true], + 'mysqli_more_results' => ['hasSideEffects' => true], 'mysqli_multi_query' => ['hasSideEffects' => true], 'mysqli_next_result' => ['hasSideEffects' => true], + 'mysqli_num_fields' => ['hasSideEffects' => true], + 'mysqli_num_rows' => ['hasSideEffects' => true], 'mysqli_options' => ['hasSideEffects' => true], + 'mysqli_param_count' => ['hasSideEffects' => true], 'mysqli_ping' => ['hasSideEffects' => true], 'mysqli_poll' => ['hasSideEffects' => true], 'mysqli_prepare' => ['hasSideEffects' => true], 'mysqli_query' => ['hasSideEffects' => true], 'mysqli_real_connect' => ['hasSideEffects' => true], + 'mysqli_real_escape_string' => ['hasSideEffects' => true], 'mysqli_real_query' => ['hasSideEffects' => true], 'mysqli_reap_async_query' => ['hasSideEffects' => true], 'mysqli_refresh' => ['hasSideEffects' => true], @@ -131,26 +169,47 @@ 'mysqli_rollback' => ['hasSideEffects' => true], 'mysqli_savepoint' => ['hasSideEffects' => true], 'mysqli_select_db' => ['hasSideEffects' => true], + 'mysqli_send_long_data' => ['hasSideEffects' => true], 'mysqli_set_charset' => ['hasSideEffects' => true], + 'mysqli_set_local_infile_default' => ['hasSideEffects' => true], + 'mysqli_set_local_infile_handler' => ['hasSideEffects' => true], + 'mysqli_set_opt' => ['hasSideEffects' => true], + 'mysqli_sqlstate' => ['hasSideEffects' => true], 'mysqli_ssl_set' => ['hasSideEffects' => true], 'mysqli_stat' => ['hasSideEffects' => true], + 'mysqli_stmt_affected_rows' => ['hasSideEffects' => true], + 'mysqli_stmt_attr_get' => ['hasSideEffects' => true], 'mysqli_stmt_attr_set' => ['hasSideEffects' => true], 'mysqli_stmt_bind_param' => ['hasSideEffects' => true], 'mysqli_stmt_bind_result' => ['hasSideEffects' => true], 'mysqli_stmt_close' => ['hasSideEffects' => true], 'mysqli_stmt_data_seek' => ['hasSideEffects' => true], + 'mysqli_stmt_errno' => ['hasSideEffects' => true], + 'mysqli_stmt_error' => ['hasSideEffects' => true], + 'mysqli_stmt_error_list' => ['hasSideEffects' => true], 'mysqli_stmt_execute' => ['hasSideEffects' => true], 'mysqli_stmt_fetch' => ['hasSideEffects' => true], + 'mysqli_stmt_field_count' => ['hasSideEffects' => true], 'mysqli_stmt_free_result' => ['hasSideEffects' => true], 'mysqli_stmt_get_result' => ['hasSideEffects' => true], + 'mysqli_stmt_get_warnings' => ['hasSideEffects' => true], 'mysqli_stmt_init' => ['hasSideEffects' => true], + 'mysqli_stmt_insert_id' => ['hasSideEffects' => true], + 'mysqli_stmt_more_results' => ['hasSideEffects' => true], 'mysqli_stmt_next_result' => ['hasSideEffects' => true], + 'mysqli_stmt_num_rows' => ['hasSideEffects' => true], + 'mysqli_stmt_param_count' => ['hasSideEffects' => true], 'mysqli_stmt_prepare' => ['hasSideEffects' => true], 'mysqli_stmt_reset' => ['hasSideEffects' => true], + 'mysqli_stmt_result_metadata' => ['hasSideEffects' => true], 'mysqli_stmt_send_long_data' => ['hasSideEffects' => true], + 'mysqli_stmt_sqlstate' => ['hasSideEffects' => true], 'mysqli_stmt_store_result' => ['hasSideEffects' => true], 'mysqli_store_result' => ['hasSideEffects' => true], + 'mysqli_thread_id' => ['hasSideEffects' => true], + 'mysqli_thread_safe' => ['hasSideEffects' => true], 'mysqli_use_result' => ['hasSideEffects' => true], + 'mysqli_warning_count' => ['hasSideEffects' => true], 'ob_clean' => ['hasSideEffects' => true], 'ob_end_clean' => ['hasSideEffects' => true], 'ob_end_flush' => ['hasSideEffects' => true], diff --git a/resources/functionMetadata.php b/resources/functionMetadata.php index 9c5c26200f2..259a4f2909d 100644 --- a/resources/functionMetadata.php +++ b/resources/functionMetadata.php @@ -1392,35 +1392,73 @@ 'msgfmt_parse_message' => ['hasSideEffects' => false], 'mt_getrandmax' => ['hasSideEffects' => false], 'mt_rand' => ['hasSideEffects' => true], + 'mysqli_affected_rows' => ['hasSideEffects' => true], 'mysqli_autocommit' => ['hasSideEffects' => true], 'mysqli_begin_transaction' => ['hasSideEffects' => true], + 'mysqli_bind_param' => ['hasSideEffects' => true], + 'mysqli_bind_result' => ['hasSideEffects' => true], 'mysqli_change_user' => ['hasSideEffects' => true], + 'mysqli_character_set_name' => ['hasSideEffects' => true], + 'mysqli_client_encoding' => ['hasSideEffects' => true], 'mysqli_close' => ['hasSideEffects' => true], 'mysqli_commit' => ['hasSideEffects' => true], 'mysqli_connect' => ['hasSideEffects' => true], + 'mysqli_connect_errno' => ['hasSideEffects' => true], + 'mysqli_connect_error' => ['hasSideEffects' => true], 'mysqli_data_seek' => ['hasSideEffects' => true], 'mysqli_debug' => ['hasSideEffects' => true], 'mysqli_dump_debug_info' => ['hasSideEffects' => true], + 'mysqli_errno' => ['hasSideEffects' => true], + 'mysqli_error' => ['hasSideEffects' => true], + 'mysqli_error_list' => ['hasSideEffects' => true], + 'mysqli_escape_string' => ['hasSideEffects' => true], + 'mysqli_execute' => ['hasSideEffects' => true], + 'mysqli_execute_query' => ['hasSideEffects' => true], + 'mysqli_fetch' => ['hasSideEffects' => true], 'mysqli_fetch_all' => ['hasSideEffects' => true], 'mysqli_fetch_array' => ['hasSideEffects' => true], 'mysqli_fetch_assoc' => ['hasSideEffects' => true], 'mysqli_fetch_column' => ['hasSideEffects' => true], 'mysqli_fetch_field' => ['hasSideEffects' => true], 'mysqli_fetch_field_direct' => ['hasSideEffects' => true], + 'mysqli_fetch_fields' => ['hasSideEffects' => true], + 'mysqli_fetch_lengths' => ['hasSideEffects' => true], 'mysqli_fetch_object' => ['hasSideEffects' => true], 'mysqli_fetch_row' => ['hasSideEffects' => true], + 'mysqli_field_count' => ['hasSideEffects' => true], 'mysqli_field_seek' => ['hasSideEffects' => true], + 'mysqli_field_tell' => ['hasSideEffects' => true], 'mysqli_free_result' => ['hasSideEffects' => true], + 'mysqli_get_cache_stats' => ['hasSideEffects' => true], + 'mysqli_get_charset' => ['hasSideEffects' => true], + 'mysqli_get_client_info' => ['hasSideEffects' => true], + 'mysqli_get_client_stats' => ['hasSideEffects' => true], + 'mysqli_get_client_version' => ['hasSideEffects' => true], + 'mysqli_get_connection_stats' => ['hasSideEffects' => true], + 'mysqli_get_host_info' => ['hasSideEffects' => true], + 'mysqli_get_links_stats' => ['hasSideEffects' => true], + 'mysqli_get_metadata' => ['hasSideEffects' => true], + 'mysqli_get_proto_info' => ['hasSideEffects' => true], + 'mysqli_get_server_info' => ['hasSideEffects' => true], + 'mysqli_get_server_version' => ['hasSideEffects' => true], + 'mysqli_get_warnings' => ['hasSideEffects' => true], + 'mysqli_info' => ['hasSideEffects' => true], 'mysqli_init' => ['hasSideEffects' => true], + 'mysqli_insert_id' => ['hasSideEffects' => true], 'mysqli_kill' => ['hasSideEffects' => true], + 'mysqli_more_results' => ['hasSideEffects' => true], 'mysqli_multi_query' => ['hasSideEffects' => true], 'mysqli_next_result' => ['hasSideEffects' => true], + 'mysqli_num_fields' => ['hasSideEffects' => true], + 'mysqli_num_rows' => ['hasSideEffects' => true], 'mysqli_options' => ['hasSideEffects' => true], + 'mysqli_param_count' => ['hasSideEffects' => true], 'mysqli_ping' => ['hasSideEffects' => true], 'mysqli_poll' => ['hasSideEffects' => true], 'mysqli_prepare' => ['hasSideEffects' => true], 'mysqli_query' => ['hasSideEffects' => true], 'mysqli_real_connect' => ['hasSideEffects' => true], + 'mysqli_real_escape_string' => ['hasSideEffects' => true], 'mysqli_real_query' => ['hasSideEffects' => true], 'mysqli_reap_async_query' => ['hasSideEffects' => true], 'mysqli_refresh' => ['hasSideEffects' => true], @@ -1429,26 +1467,47 @@ 'mysqli_rollback' => ['hasSideEffects' => true], 'mysqli_savepoint' => ['hasSideEffects' => true], 'mysqli_select_db' => ['hasSideEffects' => true], + 'mysqli_send_long_data' => ['hasSideEffects' => true], 'mysqli_set_charset' => ['hasSideEffects' => true], + 'mysqli_set_local_infile_default' => ['hasSideEffects' => true], + 'mysqli_set_local_infile_handler' => ['hasSideEffects' => true], + 'mysqli_set_opt' => ['hasSideEffects' => true], + 'mysqli_sqlstate' => ['hasSideEffects' => true], 'mysqli_ssl_set' => ['hasSideEffects' => true], 'mysqli_stat' => ['hasSideEffects' => true], + 'mysqli_stmt_affected_rows' => ['hasSideEffects' => true], + 'mysqli_stmt_attr_get' => ['hasSideEffects' => true], 'mysqli_stmt_attr_set' => ['hasSideEffects' => true], 'mysqli_stmt_bind_param' => ['hasSideEffects' => true], 'mysqli_stmt_bind_result' => ['hasSideEffects' => true], 'mysqli_stmt_close' => ['hasSideEffects' => true], 'mysqli_stmt_data_seek' => ['hasSideEffects' => true], + 'mysqli_stmt_errno' => ['hasSideEffects' => true], + 'mysqli_stmt_error' => ['hasSideEffects' => true], + 'mysqli_stmt_error_list' => ['hasSideEffects' => true], 'mysqli_stmt_execute' => ['hasSideEffects' => true], 'mysqli_stmt_fetch' => ['hasSideEffects' => true], + 'mysqli_stmt_field_count' => ['hasSideEffects' => true], 'mysqli_stmt_free_result' => ['hasSideEffects' => true], 'mysqli_stmt_get_result' => ['hasSideEffects' => true], + 'mysqli_stmt_get_warnings' => ['hasSideEffects' => true], 'mysqli_stmt_init' => ['hasSideEffects' => true], + 'mysqli_stmt_insert_id' => ['hasSideEffects' => true], + 'mysqli_stmt_more_results' => ['hasSideEffects' => true], 'mysqli_stmt_next_result' => ['hasSideEffects' => true], + 'mysqli_stmt_num_rows' => ['hasSideEffects' => true], + 'mysqli_stmt_param_count' => ['hasSideEffects' => true], 'mysqli_stmt_prepare' => ['hasSideEffects' => true], 'mysqli_stmt_reset' => ['hasSideEffects' => true], + 'mysqli_stmt_result_metadata' => ['hasSideEffects' => true], 'mysqli_stmt_send_long_data' => ['hasSideEffects' => true], + 'mysqli_stmt_sqlstate' => ['hasSideEffects' => true], 'mysqli_stmt_store_result' => ['hasSideEffects' => true], 'mysqli_store_result' => ['hasSideEffects' => true], + 'mysqli_thread_id' => ['hasSideEffects' => true], + 'mysqli_thread_safe' => ['hasSideEffects' => true], 'mysqli_use_result' => ['hasSideEffects' => true], + 'mysqli_warning_count' => ['hasSideEffects' => true], 'net_get_interfaces' => ['hasSideEffects' => false], 'ngettext' => ['hasSideEffects' => false], 'nl2br' => ['hasSideEffects' => false], From 2abbe2e56ee6ae17a8b305f0e348b5f637120611 Mon Sep 17 00:00:00 2001 From: phpstan-bot Date: Wed, 15 Apr 2026 21:20:24 +0000 Subject: [PATCH 6/7] Mark pure mysqli_ result-metadata functions as hasSideEffects=false MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Functions like mysqli_fetch_fields, mysqli_fetch_field_direct, mysqli_num_rows, and mysqli_num_fields always return the same value for the same mysqli_result — they read fixed metadata and don't advance any cursor. These should not be marked as having side effects. Co-Authored-By: Claude Opus 4.6 --- bin/functionMetadata_original.php | 8 ++++---- resources/functionMetadata.php | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/bin/functionMetadata_original.php b/bin/functionMetadata_original.php index 1d24b4421f9..15fbf328e24 100644 --- a/bin/functionMetadata_original.php +++ b/bin/functionMetadata_original.php @@ -122,8 +122,8 @@ 'mysqli_fetch_assoc' => ['hasSideEffects' => true], 'mysqli_fetch_column' => ['hasSideEffects' => true], 'mysqli_fetch_field' => ['hasSideEffects' => true], - 'mysqli_fetch_field_direct' => ['hasSideEffects' => true], - 'mysqli_fetch_fields' => ['hasSideEffects' => true], + 'mysqli_fetch_field_direct' => ['hasSideEffects' => false], + 'mysqli_fetch_fields' => ['hasSideEffects' => false], 'mysqli_fetch_lengths' => ['hasSideEffects' => true], 'mysqli_fetch_object' => ['hasSideEffects' => true], 'mysqli_fetch_row' => ['hasSideEffects' => true], @@ -151,8 +151,8 @@ 'mysqli_more_results' => ['hasSideEffects' => true], 'mysqli_multi_query' => ['hasSideEffects' => true], 'mysqli_next_result' => ['hasSideEffects' => true], - 'mysqli_num_fields' => ['hasSideEffects' => true], - 'mysqli_num_rows' => ['hasSideEffects' => true], + 'mysqli_num_fields' => ['hasSideEffects' => false], + 'mysqli_num_rows' => ['hasSideEffects' => false], 'mysqli_options' => ['hasSideEffects' => true], 'mysqli_param_count' => ['hasSideEffects' => true], 'mysqli_ping' => ['hasSideEffects' => true], diff --git a/resources/functionMetadata.php b/resources/functionMetadata.php index 259a4f2909d..b26b1c8ea2a 100644 --- a/resources/functionMetadata.php +++ b/resources/functionMetadata.php @@ -1420,8 +1420,8 @@ 'mysqli_fetch_assoc' => ['hasSideEffects' => true], 'mysqli_fetch_column' => ['hasSideEffects' => true], 'mysqli_fetch_field' => ['hasSideEffects' => true], - 'mysqli_fetch_field_direct' => ['hasSideEffects' => true], - 'mysqli_fetch_fields' => ['hasSideEffects' => true], + 'mysqli_fetch_field_direct' => ['hasSideEffects' => false], + 'mysqli_fetch_fields' => ['hasSideEffects' => false], 'mysqli_fetch_lengths' => ['hasSideEffects' => true], 'mysqli_fetch_object' => ['hasSideEffects' => true], 'mysqli_fetch_row' => ['hasSideEffects' => true], @@ -1449,8 +1449,8 @@ 'mysqli_more_results' => ['hasSideEffects' => true], 'mysqli_multi_query' => ['hasSideEffects' => true], 'mysqli_next_result' => ['hasSideEffects' => true], - 'mysqli_num_fields' => ['hasSideEffects' => true], - 'mysqli_num_rows' => ['hasSideEffects' => true], + 'mysqli_num_fields' => ['hasSideEffects' => false], + 'mysqli_num_rows' => ['hasSideEffects' => false], 'mysqli_options' => ['hasSideEffects' => true], 'mysqli_param_count' => ['hasSideEffects' => true], 'mysqli_ping' => ['hasSideEffects' => true], From 17ec063724734a00e87df63e96188df291ff7b55 Mon Sep 17 00:00:00 2001 From: phpstan-bot Date: Thu, 16 Apr 2026 08:03:37 +0000 Subject: [PATCH 7/7] Remove redundant hasSideEffects=false entries for pure mysqli_ functions mysqli_fetch_field_direct, mysqli_fetch_fields, mysqli_num_fields, and mysqli_num_rows don't need explicit hasSideEffects=false since that is the default behavior when a function is not listed in the metadata. Co-Authored-By: Claude Opus 4.6 --- bin/functionMetadata_original.php | 4 ---- resources/functionMetadata.php | 4 ---- 2 files changed, 8 deletions(-) diff --git a/bin/functionMetadata_original.php b/bin/functionMetadata_original.php index 15fbf328e24..7a1f8930ab2 100644 --- a/bin/functionMetadata_original.php +++ b/bin/functionMetadata_original.php @@ -122,8 +122,6 @@ 'mysqli_fetch_assoc' => ['hasSideEffects' => true], 'mysqli_fetch_column' => ['hasSideEffects' => true], 'mysqli_fetch_field' => ['hasSideEffects' => true], - 'mysqli_fetch_field_direct' => ['hasSideEffects' => false], - 'mysqli_fetch_fields' => ['hasSideEffects' => false], 'mysqli_fetch_lengths' => ['hasSideEffects' => true], 'mysqli_fetch_object' => ['hasSideEffects' => true], 'mysqli_fetch_row' => ['hasSideEffects' => true], @@ -151,8 +149,6 @@ 'mysqli_more_results' => ['hasSideEffects' => true], 'mysqli_multi_query' => ['hasSideEffects' => true], 'mysqli_next_result' => ['hasSideEffects' => true], - 'mysqli_num_fields' => ['hasSideEffects' => false], - 'mysqli_num_rows' => ['hasSideEffects' => false], 'mysqli_options' => ['hasSideEffects' => true], 'mysqli_param_count' => ['hasSideEffects' => true], 'mysqli_ping' => ['hasSideEffects' => true], diff --git a/resources/functionMetadata.php b/resources/functionMetadata.php index b26b1c8ea2a..2d5fbeda3dd 100644 --- a/resources/functionMetadata.php +++ b/resources/functionMetadata.php @@ -1420,8 +1420,6 @@ 'mysqli_fetch_assoc' => ['hasSideEffects' => true], 'mysqli_fetch_column' => ['hasSideEffects' => true], 'mysqli_fetch_field' => ['hasSideEffects' => true], - 'mysqli_fetch_field_direct' => ['hasSideEffects' => false], - 'mysqli_fetch_fields' => ['hasSideEffects' => false], 'mysqli_fetch_lengths' => ['hasSideEffects' => true], 'mysqli_fetch_object' => ['hasSideEffects' => true], 'mysqli_fetch_row' => ['hasSideEffects' => true], @@ -1449,8 +1447,6 @@ 'mysqli_more_results' => ['hasSideEffects' => true], 'mysqli_multi_query' => ['hasSideEffects' => true], 'mysqli_next_result' => ['hasSideEffects' => true], - 'mysqli_num_fields' => ['hasSideEffects' => false], - 'mysqli_num_rows' => ['hasSideEffects' => false], 'mysqli_options' => ['hasSideEffects' => true], 'mysqli_param_count' => ['hasSideEffects' => true], 'mysqli_ping' => ['hasSideEffects' => true],