Skip to content

Fix and improve caching controls#155

Merged
ryanwelcher merged 6 commits intotrunkfrom
fix/only-cache-when-control-is-enabled
Feb 25, 2026
Merged

Fix and improve caching controls#155
ryanwelcher merged 6 commits intotrunkfrom
fix/only-cache-when-control-is-enabled

Conversation

@ryanwelcher
Copy link
Owner

@ryanwelcher ryanwelcher commented Feb 23, 2026

Summary

  • Fixes a bug where posts_pre_query and the_posts filters would attempt to cache/serve results for any AQL query, even when the Enable Caching toggle was off
  • Adds an allowedControls guard to PerformanceControls so the panel respects the aql_allowed_controls filter like every other control
  • Fixes resetAll in the Performance Controls panel — it was a no-op and now correctly sets enable_caching to false
  • Fixes enable_caching being left set when switching orderBy to Random — switching to Random now atomically clears the attribute
  • Adds missing 'advanced-query-loop' text domain to i18n strings in performance-controls.js
  • Fixes minor whitespace inconsistency in the PHP filter conditions
  • Updates both readme.md and readme.txt to document the Enable Caching and Disable Pagination controls and complete the control identifier lists (exclude_posts and enable_caching were missing from readme.txt)
  • Adds PHP unit tests explicitly documenting the strict true === filter contract
  • Adds 10 e2e tests covering editor UI behaviour and frontend rendering with caching enabled

Test plan

  • Enable caching toggle appears in the AQL: Performance Controls panel (via the panel options menu)
  • Toggling on sets enable_caching: true in block attributes; toggling off sets it to false
  • Reset All in the Performance Controls panel clears enable_caching
  • Selecting Random order disables the caching toggle
  • Switching to Random order clears an existing enable_caching: true attribute
  • Switching away from Random re-enables the toggle
  • Frontend renders correctly with caching enabled; repeated loads return consistent results
  • npm run test:unit passes
  • npm run test:e2e passes (or run npx playwright test tests/e2e/tests/enable-caching.spec.ts to run only the new tests)

@ryanwelcher ryanwelcher requested a review from Copilot February 23, 2026 19:50
@ryanwelcher ryanwelcher added the enhancement New feature or request label Feb 23, 2026
@ryanwelcher ryanwelcher added this to the 4.4.0 milestone Feb 23, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes critical bugs in the caching functionality and improves the PerformanceControls component to align with established patterns. The main bug fixed is that the posts_pre_query and the_posts filters would attempt to cache results for ANY AQL query (where is_aql is set), even when the Enable Caching toggle was off. The fix adds strict boolean checks to ensure caching only happens when explicitly enabled.

Changes:

  • Adds isset() and true === checks to caching filters to prevent unintended caching
  • Implements allowedControls pattern in PerformanceControls to respect the aql_allowed_controls filter
  • Fixes resetAll callback (was no-op, now properly clears enable_caching)
  • Atomically clears enable_caching when switching orderBy to Random
  • Adds missing 'advanced-query-loop' text domain to i18n strings
  • Updates documentation with comprehensive control lists and caching behavior description
  • Adds comprehensive PHP unit tests and e2e tests

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated no comments.

Show a summary per file
File Description
includes/query-loop.php Adds strict boolean checks to prevent caching when not explicitly enabled
src/components/performance-controls.js Implements allowedControls guard, fixes resetAll, adds text domains
src/components/post-order-controls.js Clears enable_caching when switching to Random order
tests/unit/Enable_Caching_Tests.php Adds tests documenting strict boolean contract for caching filters
tests/e2e/tests/enable-caching.spec.ts Adds 10 e2e tests covering editor UI and frontend rendering
readme.txt Documents Enable Caching control and completes control identifier list
readme.md Documents Enable Caching functionality and adds to control list
Comments suppressed due to low confidence (1)

includes/query-loop.php:216

  • The caching implementation in posts_pre_query/the_posts caches the entire WP_Query object globally in a transient keyed only by $query->query_vars_hash, without taking the current user or capabilities into account. If a privileged user (e.g., an admin or editor) triggers an AQL query that returns posts they are allowed to see (such as private or otherwise restricted content), that WP_Query is stored and then transparently reused for later visitors whose requests produce the same query vars hash, bypassing WP_Query’s normal capability-based filtering because posts_pre_query short-circuits the database query. This can leak private or otherwise access-controlled posts to unauthenticated or lower-privilege users; to fix this, the cache key or caching conditions need to incorporate user-specific context (or be limited to queries that are guaranteed to be user-agnostic, e.g., post_status strictly publish).
add_filter(
	'posts_pre_query',
	function ( $null_return, $query ) {

		if ( ! $query->is_admin &&
			isset( $query->query['is_aql'] ) &&
			isset( $query->query['enable_caching'] ) &&
			true === $query->query['enable_caching'] &&
			! isset( $_GET['context'] ) && // phpcs:ignore
			! isset( $_GET['canvas'] ) // phpcs:ignore
		) {
			$cached_query = get_transient( $query->query_vars_hash );
			if ( $cached_query ) {
				$query->found_posts   = $cached_query->found_posts;
				$query->max_num_pages = $cached_query->max_num_pages;
				return $cached_query->posts;
			}
		}

		return $null_return;
	},
	10,
	2
);

/**
 * Create a cache for the Query generated by the AQL instance.
 */
add_filter(
	'the_posts',
	function ( $posts, $query ) {
		if ( ! $query->is_admin &&
			isset( $query->query['is_aql'] ) &&
			isset( $query->query['enable_caching'] ) &&
			true === $query->query['enable_caching'] &&
			! isset( $_GET['context'] ) && // phpcs:ignore
			! isset( $_GET['canvas'] ) // phpcs:ignore
		) {
			if ( ! get_transient( $query->query_vars_hash ) ) {
				set_transient( $query->query_vars_hash, $query, HOUR_IN_SECONDS );
			}

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@ryanwelcher ryanwelcher merged commit 8e40311 into trunk Feb 25, 2026
7 checks passed
@ryanwelcher ryanwelcher deleted the fix/only-cache-when-control-is-enabled branch February 25, 2026 15:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants