From 4ec02fc70b1dd104575f492861cebff24ac8f32c Mon Sep 17 00:00:00 2001 From: Rodrigo Primo Date: Thu, 7 Aug 2025 15:25:04 -0300 Subject: [PATCH 1/2] DB/PreparedSQL: move intentional syntax error test to its own file --- WordPress/Tests/DB/PreparedSQLUnitTest.1.inc | 3 --- WordPress/Tests/DB/PreparedSQLUnitTest.3.inc | 8 ++++++++ 2 files changed, 8 insertions(+), 3 deletions(-) create mode 100644 WordPress/Tests/DB/PreparedSQLUnitTest.3.inc diff --git a/WordPress/Tests/DB/PreparedSQLUnitTest.1.inc b/WordPress/Tests/DB/PreparedSQLUnitTest.1.inc index 1f1a49076c..c6dce05c20 100644 --- a/WordPress/Tests/DB/PreparedSQLUnitTest.1.inc +++ b/WordPress/Tests/DB/PreparedSQLUnitTest.1.inc @@ -142,6 +142,3 @@ echo $wpdb::CONSTANT_NAME; // Not an identifiable method call. $wpdb->{$methodName}('query'); - -// Don't throw an error during live coding. -wpdb::prepare( "SELECT * FROM $wpdb->posts diff --git a/WordPress/Tests/DB/PreparedSQLUnitTest.3.inc b/WordPress/Tests/DB/PreparedSQLUnitTest.3.inc new file mode 100644 index 0000000000..4047216b82 --- /dev/null +++ b/WordPress/Tests/DB/PreparedSQLUnitTest.3.inc @@ -0,0 +1,8 @@ +posts From e1e226083bb6a8b86a5a8d2cffbd05739f911522 Mon Sep 17 00:00:00 2001 From: Rodrigo Primo Date: Mon, 6 Jul 2026 19:57:30 +0000 Subject: [PATCH 2/2] WPDBTrait::is_wpdb_method_call(): fix false positives for static method calls to non-global classes called wpdb The `DB/PreparedSQL` and `DB/PreparedSQLPlaceholders` sniffs were producing false positives for static method calls to a class named `wpdb` that is preceded by a namespace. The root cause was in `WPDBTrait::is_wpdb_method_call()`, which did not check whether the `wpdb` token was namespaced before treating it as a call to the global `wpdb` class. The fix makes the method return `false` when the `wpdb` token is a namespaced call to a non-global `wpdb` class. Fixes 2710 --- WordPress/Helpers/WPDBTrait.php | 6 ++++++ .../Tests/DB/PreparedSQLPlaceholdersUnitTest.inc | 14 ++++++++++++++ .../Tests/DB/PreparedSQLPlaceholdersUnitTest.php | 4 ++++ WordPress/Tests/DB/PreparedSQLUnitTest.1.inc | 15 +++++++++++++++ WordPress/Tests/DB/PreparedSQLUnitTest.php | 2 ++ 5 files changed, 41 insertions(+) diff --git a/WordPress/Helpers/WPDBTrait.php b/WordPress/Helpers/WPDBTrait.php index d2f729da5b..5843ea4225 100644 --- a/WordPress/Helpers/WPDBTrait.php +++ b/WordPress/Helpers/WPDBTrait.php @@ -13,6 +13,7 @@ use PHP_CodeSniffer\Util\Tokens; use PHPCSUtils\BackCompat\BCFile; use PHPCSUtils\Tokens\Collections; +use WordPressCS\WordPress\Helpers\ContextHelper; /** * Helper utilities for sniffs which examine WPDB method calls. @@ -79,6 +80,11 @@ final protected function is_wpdb_method_call( File $phpcsFile, $stackPtr, array return false; } + // If calling the method statically, ensure we are calling the global wpdb class. + if ( \T_STRING === $tokens[ $stackPtr ]['code'] && ContextHelper::is_token_namespaced( $phpcsFile, $stackPtr ) ) { + return false; + } + $methodPtr = $phpcsFile->findNext( Tokens::$emptyTokens, ( $is_object_call + 1 ), null, true, null, true ); if ( false === $methodPtr ) { return false; diff --git a/WordPress/Tests/DB/PreparedSQLPlaceholdersUnitTest.inc b/WordPress/Tests/DB/PreparedSQLPlaceholdersUnitTest.inc index 601877bfa4..791df5a509 100644 --- a/WordPress/Tests/DB/PreparedSQLPlaceholdersUnitTest.inc +++ b/WordPress/Tests/DB/PreparedSQLPlaceholdersUnitTest.inc @@ -518,3 +518,17 @@ $where = $wpdb->prepare( */ $callback = $wpdb->prepare(...); // OK. +/* + * Safeguard correct handling of all types of namespaced calls to the wpdb::prepare() method. + * + * Note that calling wpdb::prepare() statically will result in an error. Still, the tests are included here since the + * sniff handles those calls. + * + * Related to: https://github.com/WordPress/WordPress-Coding-Standards/issues/2710. + */ +$sql = \wpdb::prepare( "SELECT * FROM $wpdb->users WHERE id = %d AND user_login = %s" ); // Error. +$sql = \WPDB::prepare( "SELECT * FROM $wpdb->users WHERE id = %d AND user_login = %s" ); // Error. +$sql = MyNamespace\wpdb::prepare( "SELECT * FROM $wpdb->users WHERE id = %d AND user_login = %s" ); // OK. +$sql = \MyNamespace\WPDB::prepare( "SELECT * FROM $wpdb->users WHERE id = %d AND user_login = %s" ); // OK. +$sql = namespace\wpdb::prepare( "SELECT * FROM $wpdb->users WHERE id = %d AND user_login = %s" ); // Ok. The sniff should start flagging this once it can resolve relative namespaces as this test file is not namespaced. +$sql = namespace\Sub\wpdb::prepare( "SELECT * FROM $wpdb->users WHERE id = %d AND user_login = %s" ); // OK. diff --git a/WordPress/Tests/DB/PreparedSQLPlaceholdersUnitTest.php b/WordPress/Tests/DB/PreparedSQLPlaceholdersUnitTest.php index df0bc753bf..916567cf3e 100644 --- a/WordPress/Tests/DB/PreparedSQLPlaceholdersUnitTest.php +++ b/WordPress/Tests/DB/PreparedSQLPlaceholdersUnitTest.php @@ -106,6 +106,10 @@ public function getErrorList() { // Named parameter support. 418 => 1, + + // Fully qualified calls to the global class wpdb. + 529 => 1, + 530 => 1, ); } diff --git a/WordPress/Tests/DB/PreparedSQLUnitTest.1.inc b/WordPress/Tests/DB/PreparedSQLUnitTest.1.inc index c6dce05c20..ac97d71ce8 100644 --- a/WordPress/Tests/DB/PreparedSQLUnitTest.1.inc +++ b/WordPress/Tests/DB/PreparedSQLUnitTest.1.inc @@ -142,3 +142,18 @@ echo $wpdb::CONSTANT_NAME; // Not an identifiable method call. $wpdb->{$methodName}('query'); + +/* + * Safeguard correct handling of all types of namespaced calls to the wpdb::prepare() method. + * + * Note that calling wpdb::prepare() statically will result in an error. Still, the tests are included here since the + * sniff handles those calls. + * + * Related to: https://github.com/WordPress/WordPress-Coding-Standards/issues/2710. + */ +\wpdb::prepare( "SELECT * FROM $wpdb->posts WHERE post_title LIKE '" . foo() . "';" ); // Bad. +\WPDB::prepare( "SELECT * FROM $wpdb->posts WHERE post_title LIKE '" . foo() . "';" ); // Bad. +MyNamespace\wpdb::prepare( "SELECT * FROM $wpdb->posts WHERE post_title LIKE '" . foo() . "';" ); // Ok. +\MyNamespace\WPDB::prepare( "SELECT * FROM $wpdb->posts WHERE post_title LIKE '" . foo() . "';" ); // Ok. +namespace\wpdb::prepare( "SELECT * FROM $wpdb->posts WHERE post_title LIKE '" . foo() . "';" ); // Ok. The sniff should start flagging this once it can resolve relative namespaces as this test file is not namespaced. +namespace\Sub\wpdb::prepare( "SELECT * FROM $wpdb->posts WHERE post_title LIKE '" . foo() . "';" ); // Ok. diff --git a/WordPress/Tests/DB/PreparedSQLUnitTest.php b/WordPress/Tests/DB/PreparedSQLUnitTest.php index 0a296ff984..8d887dd044 100644 --- a/WordPress/Tests/DB/PreparedSQLUnitTest.php +++ b/WordPress/Tests/DB/PreparedSQLUnitTest.php @@ -66,6 +66,8 @@ public function getErrorList( $testFile = '' ) { 124 => 1, 128 => 1, 132 => 2, + 154 => 1, + 155 => 1, ); case 'PreparedSQLUnitTest.2.inc':