Commit c949ec0f006 for woocommerce

commit c949ec0f00649cc35f0259bb767ab0326c616e14
Author: Darren Ethier <darren@roughsmootheng.in>
Date:   Fri Sep 4 15:05:10 2026 -0400

    Scope PHPCS suppressions on extension-facing SQL queries (#68298)

diff --git a/plugins/woocommerce/changelog/fix-woo6-117-extensible-sql-contract-suppressions b/plugins/woocommerce/changelog/fix-woo6-117-extensible-sql-contract-suppressions
new file mode 100644
index 00000000000..a470dcf8455
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-woo6-117-extensible-sql-contract-suppressions
@@ -0,0 +1,4 @@
+Significance: patch
+Type: dev
+
+Replace legacy WPCS suppression comments on extension-facing SQL and order note queries with scoped phpcs:ignore annotations.
diff --git a/plugins/woocommerce/includes/admin/reports/class-wc-report-downloads.php b/plugins/woocommerce/includes/admin/reports/class-wc-report-downloads.php
index e59e7dd1193..e0e3ff2bf49 100644
--- a/plugins/woocommerce/includes/admin/reports/class-wc-report-downloads.php
+++ b/plugins/woocommerce/includes/admin/reports/class-wc-report-downloads.php
@@ -330,7 +330,7 @@ class WC_Report_Downloads extends WP_List_Table {
 		$query_from  = apply_filters( 'woocommerce_report_downloads_query_from', $query_from );
 		$query_order = $wpdb->prepare( 'ORDER BY timestamp DESC LIMIT %d, %d;', ( $current_page - 1 ) * $per_page, $per_page );

-		$this->items     = $wpdb->get_results( "SELECT * {$query_from} {$query_order}" ); // WPCS: cache ok, db call ok, unprepared SQL ok.
-		$this->max_items = $wpdb->get_var( "SELECT COUNT( DISTINCT download_log_id ) {$query_from};" ); // WPCS: cache ok, db call ok, unprepared SQL ok.
+		$this->items     = $wpdb->get_results( "SELECT * {$query_from} {$query_order}" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Core builds both fragments with $wpdb->prepare(); the woocommerce_report_downloads_query_from filter lets extensions supply their own SQL.
+		$this->max_items = $wpdb->get_var( "SELECT COUNT( DISTINCT download_log_id ) {$query_from};" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Core builds the fragment with $wpdb->prepare(); the woocommerce_report_downloads_query_from filter lets extensions supply their own SQL.
 	}
 }
diff --git a/plugins/woocommerce/includes/rest-api/Controllers/Version2/class-wc-rest-order-notes-v2-controller.php b/plugins/woocommerce/includes/rest-api/Controllers/Version2/class-wc-rest-order-notes-v2-controller.php
index 2861539e792..54ed6e36fc5 100644
--- a/plugins/woocommerce/includes/rest-api/Controllers/Version2/class-wc-rest-order-notes-v2-controller.php
+++ b/plugins/woocommerce/includes/rest-api/Controllers/Version2/class-wc-rest-order-notes-v2-controller.php
@@ -47,7 +47,7 @@ class WC_REST_Order_Notes_V2_Controller extends WC_REST_Order_Notes_V1_Controlle

 		// Allow filter by order note type.
 		if ( 'customer' === $request['type'] ) {
-			$args['meta_query'] = array( // WPCS: slow query ok.
+			$args['meta_query'] = array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Bounded to one order's notes; the customer flag exists only as commentmeta, so a meta join is the only way to filter on it.
 				array(
 					'key'     => 'is_customer_note',
 					'value'   => 1,
@@ -55,7 +55,7 @@ class WC_REST_Order_Notes_V2_Controller extends WC_REST_Order_Notes_V1_Controlle
 				),
 			);
 		} elseif ( 'internal' === $request['type'] ) {
-			$args['meta_query'] = array( // WPCS: slow query ok.
+			$args['meta_query'] = array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Bounded to one order's notes; internal notes are identified by the absence of the is_customer_note meta, so NOT EXISTS is the only way to filter on them.
 				array(
 					'key'     => 'is_customer_note',
 					'compare' => 'NOT EXISTS',
diff --git a/plugins/woocommerce/includes/wc-order-functions.php b/plugins/woocommerce/includes/wc-order-functions.php
index c38072d2b1b..028062a4f39 100644
--- a/plugins/woocommerce/includes/wc-order-functions.php
+++ b/plugins/woocommerce/includes/wc-order-functions.php
@@ -1253,7 +1253,7 @@ function wc_get_order_notes( $args ) {

 	// Set WooCommerce order type.
 	if ( isset( $args['type'] ) && 'customer' === $args['type'] ) {
-		$args['meta_query'] = array( // WPCS: slow query ok.
+		$args['meta_query'] = array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Order notes live in wp_comments under both HPOS and legacy storage; the customer flag exists only as commentmeta, so a meta join is the only way to filter on it.
 			array(
 				'key'     => 'is_customer_note',
 				'value'   => 1,
@@ -1261,7 +1261,7 @@ function wc_get_order_notes( $args ) {
 			),
 		);
 	} elseif ( isset( $args['type'] ) && 'internal' === $args['type'] ) {
-		$args['meta_query'] = array( // WPCS: slow query ok.
+		$args['meta_query'] = array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Order notes live in wp_comments under both HPOS and legacy storage; internal notes are identified by the absence of the is_customer_note meta, so NOT EXISTS is the only way to filter on them.
 			array(
 				'key'     => 'is_customer_note',
 				'compare' => 'NOT EXISTS',
diff --git a/plugins/woocommerce/includes/widgets/class-wc-widget-price-filter.php b/plugins/woocommerce/includes/widgets/class-wc-widget-price-filter.php
index cba01a08109..31e2330d360 100644
--- a/plugins/woocommerce/includes/widgets/class-wc-widget-price-filter.php
+++ b/plugins/woocommerce/includes/widgets/class-wc-widget-price-filter.php
@@ -185,6 +185,6 @@ class WC_Widget_Price_Filter extends WC_Widget {

 		$sql = apply_filters( 'woocommerce_price_filter_sql', $sql, $meta_query_sql, $tax_query_sql );

-		return $wpdb->get_row( $sql ); // WPCS: unprepared SQL ok.
+		return $wpdb->get_row( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Core assembles $sql from prepared WP_Meta_Query/WP_Tax_Query clauses and esc_sql'd post types; the woocommerce_price_filter_sql filter lets extensions replace the statement.
 	}
 }
diff --git a/plugins/woocommerce/includes/widgets/class-wc-widget-products.php b/plugins/woocommerce/includes/widgets/class-wc-widget-products.php
index 50f1d6b3bf3..d061b4db9fd 100644
--- a/plugins/woocommerce/includes/widgets/class-wc-widget-products.php
+++ b/plugins/woocommerce/includes/widgets/class-wc-widget-products.php
@@ -104,8 +104,8 @@ class WC_Widget_Products extends WC_Widget {
 			'post_type'      => 'product',
 			'no_found_rows'  => 1,
 			'order'          => $order,
-			'meta_query'     => array(), // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- The empty query container does not add a database join.
-			'tax_query'      => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query -- The empty query container does not add a database join.
+			'meta_query'     => array(), // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Optional container; populated only when the hide-free-products option is on.
+			'tax_query'      => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query -- Catalog visibility and out-of-stock exclusion are stored only in the product_visibility taxonomy; term_taxonomy_id lookups use the term_relationships index.
 				'relation' => 'AND',
 			),
 		);
@@ -137,7 +137,7 @@ class WC_Widget_Products extends WC_Widget {
 					'terms'    => $product_visibility_term_ids[ ProductStockStatus::OUT_OF_STOCK ],
 					'operator' => 'NOT IN',
 				),
-			); // WPCS: slow query ok.
+			);
 		}

 		switch ( $show ) {