Commit 7e9b67b17e5 for woocommerce
commit 7e9b67b17e58bbd32516f4f3c1bb28f676376c69
Author: MILLER/F <fab@millerf.com>
Date: Tue Aug 11 10:03:46 2026 +0200
fix: interpolate session table names instead of %i placeholder (#66372)
* fix: interpolate session table names instead of %i placeholder
Session queries used the %i identifier placeholder, which a $wpdb drop-in
running on a supported WordPress version may not implement (its
has_cap( 'identifier_placeholders' ) returns false), silently producing
malformed queries. The session table names are trusted developer-provided
values, so interpolate them directly into the query string instead.
Supersedes #66262.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Remove unecessary variable assignation
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
diff --git a/plugins/woocommerce/changelog/fix-identifier-placeholders-sessions b/plugins/woocommerce/changelog/fix-identifier-placeholders-sessions
new file mode 100644
index 00000000000..2a23759718a
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-identifier-placeholders-sessions
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Interpolate trusted session table names directly instead of using the `%i` placeholder, so session queries stay valid on database layers that run on a supported WordPress version but don't implement `%i`.
diff --git a/plugins/woocommerce/includes/class-wc-session-handler.php b/plugins/woocommerce/includes/class-wc-session-handler.php
index 5e18d2168eb..5c995259363 100644
--- a/plugins/woocommerce/includes/class-wc-session-handler.php
+++ b/plugins/woocommerce/includes/class-wc-session-handler.php
@@ -567,9 +567,9 @@ class WC_Session_Handler extends WC_Session {
$wpdb->query(
$wpdb->prepare(
- 'INSERT INTO %i (`session_key`, `session_value`, `session_expiry`) VALUES (%s, %s, %d)
- ON DUPLICATE KEY UPDATE `session_value` = VALUES(`session_value`), `session_expiry` = VALUES(`session_expiry`)',
- $this->_table,
+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- trusted table name.
+ "INSERT INTO {$this->_table} (`session_key`, `session_value`, `session_expiry`) VALUES (%s, %s, %d)
+ ON DUPLICATE KEY UPDATE `session_value` = VALUES(`session_value`), `session_expiry` = VALUES(`session_expiry`)",
$this->get_customer_id(),
maybe_serialize( $this->_data ),
$this->_session_expiration
@@ -641,10 +641,10 @@ class WC_Session_Handler extends WC_Session {
$batch_size = 100;
$deleted_entries_total = 0;
do {
- $deleted_entries_count = (int) $wpdb->query(
+ $deleted_entries_count = (int) $wpdb->query(
$wpdb->prepare(
- 'DELETE FROM %i WHERE session_expiry < %d ORDER BY session_expiry LIMIT %d',
- $this->_table,
+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- trusted table name.
+ "DELETE FROM {$this->_table} WHERE session_expiry < %d ORDER BY session_expiry LIMIT %d",
time(),
$batch_size
)
@@ -676,7 +676,8 @@ class WC_Session_Handler extends WC_Session {
$value = wp_cache_get( $this->get_cache_prefix() . $customer_id, WC_SESSION_CACHE_GROUP );
if ( false === $value ) {
- $value = $wpdb->get_var( $wpdb->prepare( 'SELECT session_value FROM %i WHERE session_key = %s', $this->_table, $customer_id ) );
+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- trusted table name.
+ $value = $wpdb->get_var( $wpdb->prepare( "SELECT session_value FROM {$this->_table} WHERE session_key = %s", $customer_id ) );
if ( is_null( $value ) ) {
$value = $default_value;
@@ -765,6 +766,7 @@ class WC_Session_Handler extends WC_Session {
* @return bool
*/
private function session_exists( $customer_id ) {
- return $customer_id && null !== $GLOBALS['wpdb']->get_var( $GLOBALS['wpdb']->prepare( 'SELECT session_key FROM %i WHERE session_key = %s', $this->_table, $customer_id ) );
+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- trusted table name.
+ return $customer_id && null !== $GLOBALS['wpdb']->get_var( $GLOBALS['wpdb']->prepare( "SELECT session_key FROM {$this->_table} WHERE session_key = %s", $customer_id ) );
}
}
diff --git a/plugins/woocommerce/src/StoreApi/SessionHandler.php b/plugins/woocommerce/src/StoreApi/SessionHandler.php
index 33c05b12139..7c510a604ac 100644
--- a/plugins/woocommerce/src/StoreApi/SessionHandler.php
+++ b/plugins/woocommerce/src/StoreApi/SessionHandler.php
@@ -136,8 +136,8 @@ final class SessionHandler extends WC_Session {
$value = $wpdb->get_var(
$wpdb->prepare(
- 'SELECT session_value FROM %i WHERE session_key = %s',
- $this->table,
+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- trusted table name.
+ "SELECT session_value FROM {$this->table} WHERE session_key = %s",
$customer_id
)
);
@@ -195,8 +195,8 @@ final class SessionHandler extends WC_Session {
$wpdb->query(
$wpdb->prepare(
- 'INSERT INTO %i (`session_key`, `session_value`, `session_expiry`) VALUES (%s, %s, %d) ON DUPLICATE KEY UPDATE `session_value` = VALUES(`session_value`), `session_expiry` = VALUES(`session_expiry`)',
- $this->table,
+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- trusted table name.
+ "INSERT INTO {$this->table} (`session_key`, `session_value`, `session_expiry`) VALUES (%s, %s, %d) ON DUPLICATE KEY UPDATE `session_value` = VALUES(`session_value`), `session_expiry` = VALUES(`session_expiry`)",
$this->get_customer_id(),
maybe_serialize( $this->_data ),
$this->session_expiration