-
Notifications
You must be signed in to change notification settings - Fork 62
Add caching to database queries for performance #484
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Introduces wp_cache_get/wp_cache_set to cache results of various direct database queries across multiple classes, including MySQL version lookups, WooCommerce sales/top seller stats, attachment lookups, and PageSpeed/BackWPup/TimeCapsule metadata. This reduces redundant queries for static or infrequently changing data, improving performance and scalability. Dynamic or real-time queries (such as activity logs or backup progress) are left uncached to ensure up-to-date results. Also adds comments and improves query safety by whitelisting allowed values and clarifying phpcs:ignore reasons.
WalkthroughAdds WP object-cache reads/writes and phpcs NoCaching annotations across multiple MainWP child classes, replacing repeated direct DB queries (VERSION(), reports, pagespeed fragments, attachment lookups) with cached lookups and per-feature caching layers while preserving external APIs. Changes
Sequence Diagram(s)sequenceDiagram
participant Caller
participant Cache as WP_Object_Cache
participant DB as Database
rect rgb(235,245,255)
Note over Caller,Cache: Cache hit (fast path)
Caller->>Cache: wp_cache_get(cache_key, group)
Cache-->>Caller: cached value
Caller->>Caller: use cached value (return/compute)
end
rect rgb(255,245,235)
Note over Caller,DB: Cache miss (fallback)
Caller->>Cache: wp_cache_get(cache_key, group)
Cache-->>Caller: false
Caller->>DB: Execute query (SELECT VERSION() / reports / pagespeed)
DB-->>Caller: result
Caller->>Cache: wp_cache_set(cache_key, result, group, 3600)
Cache-->>Caller: stored
Caller->>Caller: compute aggregates / return result
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Points to review closely:
Possibly related PRs
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (4)
class/class-mainwp-child-maintenance.php (1)
263-264: LGTM! Appropriate justification for uncached query.The comment correctly explains why caching is not suitable for this query—revision IDs are fetched for immediate deletion and the data becomes stale instantly. The prepared statement is properly parameterized.
Minor formatting note: Line 264 has a double space after
FROMbefore$wpdb->posts.Apply this diff to fix the formatting:
- $results_posts = $wpdb->get_results( $wpdb->prepare( "SELECT `ID`, `post_modified` FROM $wpdb->posts WHERE `post_parent`= %d AND `post_type`='revision' ORDER BY `post_modified` ASC", $results[ $i ]->post_parent ) ); + $results_posts = $wpdb->get_results( $wpdb->prepare( "SELECT `ID`, `post_modified` FROM $wpdb->posts WHERE `post_parent`= %d AND `post_type`='revision' ORDER BY `post_modified` ASC", $results[ $i ]->post_parent ) );class/class-mainwp-child-timecapsule.php (1)
1929-1935: MySQL version caching is implemented correctly; optional TTL constant usageThe MySQL version caching via
wp_cache_get/wp_cache_setwith a dedicated group is sound and avoids repeating theSELECT VERSION()call within the cache window. The strictfalse === $mysql_versioncheck is also appropriate.If you want to align with other WordPress code, you could swap the literal
3600forHOUR_IN_SECONDS, but that’s purely stylistic.class/class-mainwp-child-pagespeed.php (1)
530-552: Pagespeed caching + filter-option hardening look good; minor refactor opportunities
- Caching the computed Pagespeed aggregates per strategy via
wp_cache_get/wp_cache_setusing a key derived from$strategyandget_filter_options( 'all' )is a solid approach that avoids re-running the heavy queries on every sync.- The
$allowed_score_columnsmapping and selection of$score_columnfrom the validated strategy prevent arbitrary column injection into the SQL.- Both
gpi_page_statsandgpi_page_reportsqueries rely on theget_filter_options()contract (fragment in[0], values in[1]with%splaceholders), and the updated docblock plus the custom‑post‑type comment make that safety argument explicit.- Caching
custom_url_typesviawp_cache_get/wp_cache_setinget_filter_options()is a nice touch to cut down on repeatedSELECT DISTINCT type FROM ...gpi_custom_urlscalls, especially on larger datasets.If you want to tighten things further without changing behavior:
- Consider calling
get_filter_options( 'all' )only once incal_pagespeed_data()and reusing the result for both$data_typestocheckand$reports_typestocheckto avoid duplicated work.- For consistency with other caches in this PR, you might also standardize TTL usage on
HOUR_IN_SECONDSeverywhere (where you aren’t already).These are polish-level suggestions; the current implementation is functionally sound.
Also applies to: 557-568, 617-620, 652-653, 680-685
class/class-mainwp-child-woocommerce-status.php (1)
134-181: Status whitelisting and cached monthly sales/top-seller queries look safe; consider normalizing the cache key time componentPositives:
- The
woocommerce_reports_order_statusesresult is intersected with an explicit$allowed_statuseslist before being used in theIN ( {$placeholders} )clause, which is a good hardening step.- Dynamic placeholders plus
array_merge( $safe_statuses, array( $month_start, $month_end ) )keep the query parameterized and avoid any injection via statuses or dates.- Caching both
salesandtop_sellerresults for the “current month” in themainwp_woocommercegroup with a 1‑hour TTL aligns with the PR’s performance goal.One nuance: the cache keys include
$month_endformatted asY-m-d H:i:s. Because that value changes every second, repeated calls tosync_data()will almost always compute a new cache key, so cache hits will be rare and you may accumulate many short‑lived entries under a persistent object cache.If you want the cache to be effective while still reflecting near‑real‑time stats, you could normalize the time part used in the key to an hour (or a day) while still using the full
$month_endin the query, for example:- $month_start = date( 'Y-m-01' ); // phpcs:ignore -- local time. - $month_end = date( 'Y-m-d H:i:s' ); // phpcs:ignore -- local time. + $month_start = date( 'Y-m-01' ); // phpcs:ignore -- local time. + $month_end = date( 'Y-m-d H:i:s' ); // phpcs:ignore -- local time. + $cache_window = date( 'Y-m-d H:00:00' ); // Normalize to hour for cache key. - $cache_key = 'wc_sales_' . md5( implode( '_', $safe_statuses ) . '_' . $month_start . '_' . $month_end ); + $cache_key = 'wc_sales_' . md5( implode( '_', $safe_statuses ) . '_' . $month_start . '_' . $cache_window ); - $cache_key_top = 'wc_top_seller_' . md5( implode( '_', $safe_statuses ) . '_' . $month_start . '_' . $month_end ); + $cache_key_top = 'wc_top_seller_' . md5( implode( '_', $safe_statuses ) . '_' . $month_start . '_' . $cache_window );That keeps the 1‑hour TTL meaningful and avoids effectively “no‑op” caching.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (8)
class/class-mainwp-child-back-wp-up.php(1 hunks)class/class-mainwp-child-maintenance.php(2 hunks)class/class-mainwp-child-pagespeed.php(6 hunks)class/class-mainwp-child-server-information-base.php(2 hunks)class/class-mainwp-child-timecapsule.php(4 hunks)class/class-mainwp-child-woocommerce-status.php(2 hunks)class/class-mainwp-clone-install.php(2 hunks)class/class-mainwp-utility.php(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Codacy Static Code Analysis
🔇 Additional comments (8)
class/class-mainwp-child-maintenance.php (1)
227-228: LGTM! Clear documentation for uncached query.The added comment appropriately explains why this revision query should not be cached—the data is transient and changes immediately after cleanup. The prepared statement correctly parameterizes the query for security.
class/class-mainwp-clone-install.php (2)
345-346: LGTM - Appropriate documentation for session configuration.The added comment correctly clarifies that this is a MySQL session setting rather than a data query, and the
NoCachingphpcs:ignore flag is justified since session configuration commands don't produce cacheable results.
431-432: LGTM - Consistent documentation pattern.The re-enabling of foreign key checks follows the same correct documentation pattern as the disabling statement, with appropriate clarification that caching is not applicable for session configuration.
class/class-mainwp-child-server-information-base.php (2)
456-466: LGTM - Effective caching for static server metadata.The MySQL version caching implementation is sound:
- Checks cache before querying
- Uses appropriate 1-hour TTL for static server metadata
- Correctly retrieves the value column from
SHOW VARIABLES
564-582: LGTM - Consistent caching implementation.The SQL mode caching follows the same effective pattern as the MySQL version caching. Static server metadata is an ideal candidate for caching with a 1-hour TTL.
class/class-mainwp-child-back-wp-up.php (1)
626-631: LGTM - Clean caching implementation for BackWPup integration.The MySQL version caching is well-implemented:
- Uses
SELECT VERSION()which is a standard approach- Properly isolated in 'mainwp_backwpup' cache group
- Includes clear documentation in the phpcs:ignore comment
class/class-mainwp-child-timecapsule.php (1)
455-463: Clarifying NoCaching annotations on dynamic WPTC queries looks correctThe added comments and
WordPress.DB.DirectDatabaseQuery.NoCachingignores clearly justify why these three queries stay uncached (backup list, lazy‑loaded activity log, and per‑row activity details are all transient / real‑time). Thewpdb::prepare()usage and placeholder types also look safe in each case.Nothing to change here from a behavior perspective.
Also applies to: 794-795, 845-847
class/class-mainwp-child-woocommerce-status.php (1)
285-363: Report-range sales/top-seller caching with status whitelist is consistent and correctFor the date‑range report path:
- The same
$allowed_statuses+array_intersect()pattern is used before building theIN ( {$placeholders} )clause, which keeps the filtered statuses constrained to known values.$start_dateand$end_dateare converted toY-m-d H:i:sand then passed throughprepare()along with the whitelisted statuses, so the query remains fully parameterized.- Caching
salesandtop_sellerinmainwp_woocommerceunder keys derived from the normalized date strings and status set should give good hit rates whenever the same range is requested repeatedly from the dashboard.This block looks solid and aligned with the intent of reducing repeated heavy queries over identical ranges.
Added NOSONAR comments to clarify that MD5 is used for cache key generation only and not for cryptographic purposes in pagespeed and WooCommerce status classes.
Added a NOSONAR comment to clarify that MD5 is used for cache key generation only and not for cryptographic purposes, addressing static analysis warnings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (1)
class/class-mainwp-child-pagespeed.php (1)
597-608: Fix undefined$datawhen no filter options are returned (still present)When
get_filter_options( 'all' )returnsnull(no types enabled), theif ( ! is_null( $reports_typestocheck ) )block is skipped,$datais never defined, andis_array( $data[0] )will emit PHP notices. This was flagged in an earlier review and remains unresolved.Consider guarding the last‑modified computation with an explicit default variable, e.g.:
- if ( ! is_null( $reports_typestocheck ) ) { - $gpi_page_stats = $wpdb->prefix . 'gpi_page_stats'; // @wordpress-security:ignore UnescapedDBParameter -- Table name is safe: wpdb->prefix + constant. - $data = $wpdb->get_results( $wpdb->prepare( "SELECT $_select FROM $gpi_page_stats WHERE ( $reports_typestocheck[0] ) AND $nullcheck", $reports_typestocheck[1] ), ARRAY_A ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,PluginCheck.Security.DirectDB.UnescapedDBParameter -- $_select is hardcoded from switch statement (lines 589, 593), $gpi_page_stats is safe constant (line 598), $reports_typestocheck[0] is validated SQL fragment (line 557), $nullcheck is hardcoded (lines 588, 592). - } - - $result = array( - 'last_modified' => is_array( $data[0] ) && isset( $data[0]['last_modified'] ) ? $data[0]['last_modified'] : 0, + $last_modified = 0; + if ( ! is_null( $reports_typestocheck ) ) { + $gpi_page_stats = $wpdb->prefix . 'gpi_page_stats'; // @wordpress-security:ignore UnescapedDBParameter -- Table name is safe: wpdb->prefix + constant. + $data = $wpdb->get_results( + $wpdb->prepare( + "SELECT $_select FROM $gpi_page_stats WHERE ( $reports_typestocheck[0] ) AND $nullcheck", + $reports_typestocheck[1] + ), + ARRAY_A + ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,PluginCheck.Security.DirectDB.UnescapedDBParameter -- $_select is hardcoded, $gpi_page_stats is safe, $reports_typestocheck is validated. + if ( ! empty( $data[0]['last_modified'] ) ) { + $last_modified = $data[0]['last_modified']; + } + } + + $result = array( + 'last_modified' => $last_modified, 'average_score' => $average_score, 'total_pages' => $total_pages, );Please rerun with a configuration where
get_filter_options( 'all' )returnsnull(e.g., all types unchecked) to confirm the notices are gone.
🧹 Nitpick comments (4)
class/class-mainwp-child-woocommerce-status.php (2)
134-216: Status whitelist + cached sales/top‑seller query look soundThe status handling (filter → intersect with
$allowed_statuses→ fallback to['completed']), dynamic IN‑clause placeholders, and cache keys tied to statuses + month range all look correct and safe; the queries remain parameterized and benefit from caching.If you want to reduce duplication and future drift, consider extracting a small helper like
get_safe_order_statuses_and_placeholders()that returns both$safe_statusesand$placeholders, and reuse it in bothsync_data()andreport_data(). Please also sanity‑check that the status slugs here match the versions of WooCommerce you still support on this legacy code path.
285-363: Report data caching mirrors sync logic correctlyThe report‑range sales and top‑seller queries reuse the same status‑whitelisting and placeholder pattern, with cache keys bound to statuses + explicit start/end dates; that’s consistent with
sync_data()and should avoid redundant DB work across identical report requests.You might want to share a tiny internal helper for building the cache key (statuses + date window) so
sync_data()andreport_data()can’t diverge subtly over time; verify via a quick test that different permutations of the same statuses (e.g.,['completed','processing']vs['processing','completed']) don’t need to share a cache entry for your use case.class/class-mainwp-child-pagespeed.php (2)
557-571: Reports query correctly scopes by strategy; consider reusing filter optionsThe updated reports query properly constrains by
r.strategy = %sand passes the strategy plus the validated filter‑options array into$wpdb->prepare, keeping the SQL safe while enabling per‑strategy aggregation.Since both
$data_typestocheckand$reports_typestocheckare derived fromget_filter_options( 'all' ), you could call it once, assign to a local variable, and reuse it for both queries to avoid duplicated option reads/DB lookups (the newcustom_url_typescaching already makes the underlying query cheap, but this would still simplify control flow).
617-619: Filter‑options contract and custom URL type caching look goodThe updated docblock for
get_filter_options()clearly documents the[0]SQL fragment /[1]parameters contract, and the newcustom_url_typescaching (viawp_cache_get/setin themainwp_pagespeedgroup) is a simple, safe way to avoid repeated DISTINCT queries againstgpi_custom_urls.If you find yourself using different
restrict_typevalues frequently, you might later extend thecustom_url_typescache key to include$restrict_typeor split caches by use‑case, but the current global key is fine given the single underlying query; a quick runtime test with/without custom URLs will verify that behavior matches expectations.Also applies to: 652-652, 680-685
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (3)
class/class-mainwp-child-pagespeed.php(6 hunks)class/class-mainwp-child-woocommerce-status.php(2 hunks)class/class-mainwp-utility.php(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Codacy Static Code Analysis
🔇 Additional comments (2)
class/class-mainwp-utility.php (1)
518-536: Attachment lookup caching looks correctCache key derivation, cache group usage, and the 1‑hour TTL are consistent and safe; query behavior on hits/misses is unchanged apart from avoiding duplicate DB work. No correctness issues spotted here.
If you want extra confidence, you can add a quick unit/integration test that calls
get_maybe_existed_attached_id()twice with the same$filename/$full_guidand asserts the second call does not hit the DB (e.g., via a stubbed$wpdbor object cache).class/class-mainwp-child-pagespeed.php (1)
530-552: Result caching and score column whitelisting are implemented safelyUsing
get_filter_options( 'all' )as part of the cache key and whitelisting the score column via$allowed_score_columnsensures both correct cache partitioning and protection against SQL injection when interpolating$score_columninto the query; the rest of the query remains parameterized via$wpdb->prepare.If you’d like, add a small test that feeds bogus
$strategyvalues intocal_pagespeed_data()and confirms they’re rejected, and that switching betweendesktop/mobileyields different cache keys and results.
Replaces persistent caching with request-scoped static caching in get_maybe_existed_attached_id to avoid unnecessary cache pollution and improve performance. Also initializes the $data variable in MainWP_Child_Pagespeed to prevent undefined variable issues.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (1)
class/class-mainwp-child-pagespeed.php (1)
597-605: Still possible undefined offset on$data[0]when no rows are returnedEven though
$datais now initialized to an empty array, the expressionis_array( $data[0] ) && isset( $data[0]['last_modified'] )will still trigger a PHP notice when$datais empty, because$data[0]is an undefined offset. This can happen if:
get_filter_options( 'all' )returnsnull(no types enabled) → the query is skipped and$datastaysarray(), or- The query runs but returns zero rows for the selected types.
You can avoid this by computing
$last_modifiedseparately and only reading$data[0]['last_modified']when it exists, as previously suggested:- $data = array(); - - if ( ! is_null( $reports_typestocheck ) ) { - $gpi_page_stats = $wpdb->prefix . 'gpi_page_stats'; // @wordpress-security:ignore UnescapedDBParameter -- Table name is safe: wpdb->prefix + constant. - $data = $wpdb->get_results( $wpdb->prepare( "SELECT $_select FROM $gpi_page_stats WHERE ( $reports_typestocheck[0] ) AND $nullcheck", $reports_typestocheck[1] ), ARRAY_A ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,PluginCheck.Security.DirectDB.UnescapedDBParameter -- $_select is hardcoded from switch statement (lines 589, 593), $gpi_page_stats is safe constant (line 598), $reports_typestocheck[0] is validated SQL fragment (line 557), $nullcheck is hardcoded (lines 588, 592). - } - - $result = array( - 'last_modified' => is_array( $data[0] ) && isset( $data[0]['last_modified'] ) ? $data[0]['last_modified'] : 0, + $last_modified = 0; + if ( ! is_null( $reports_typestocheck ) ) { + $gpi_page_stats = $wpdb->prefix . 'gpi_page_stats'; // @wordpress-security:ignore UnescapedDBParameter -- Table name is safe: wpdb->prefix + constant. + $data = $wpdb->get_results( + $wpdb->prepare( + "SELECT $_select FROM $gpi_page_stats WHERE ( $reports_typestocheck[0] ) AND $nullcheck", + $reports_typestocheck[1] + ), + ARRAY_A + ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,PluginCheck.Security.DirectDB.UnescapedDBParameter -- $_select is hardcoded, $gpi_page_stats and $reports_typestocheck are validated. + if ( ! empty( $data[0]['last_modified'] ) ) { + $last_modified = $data[0]['last_modified']; + } + } + + $result = array( + 'last_modified' => $last_modified, 'average_score' => $average_score, 'total_pages' => $total_pages, );This keeps behavior the same when data exists but removes undefined-offset notices when it does not.
🧹 Nitpick comments (2)
class/class-mainwp-child-pagespeed.php (2)
530-535: Caching strategy looks sound; consider avoiding duplicate filter computationThe new cache layer keyed by strategy and the serialized filter fragment (
$data_typestocheck) is reasonable, and using themainwp_pagespeedgroup withHOUR_IN_SECONDSTTL is consistent with the rest of the PR. The whitelisting of$score_columnvia$allowed_score_columnskeeps the dynamic column safe.You can shave some work and keep the logic more obviously consistent by reusing the first
get_filter_options( 'all' )result instead of recomputing it for$reports_typestocheck, e.g.:- $data_typestocheck = self::get_filter_options( 'all' ); + $data_typestocheck = self::get_filter_options( 'all' ); ... - $reports_typestocheck = self::get_filter_options( 'all' ); + $reports_typestocheck = $data_typestocheck;(Or rename to an appropriate shared variable.) This is optional, but reduces one DB-backed call per execution path.
Also applies to: 537-552, 557-571, 609-610
537-542: Guard against future misuse of$strategywhen indexing$allowed_score_columnsRight now
$strategyis validated earlier to be either'desktop'or'mobile', so$allowed_score_columns[ $strategy ]is safe. If this function ever gets called from another path without that guard, it could emit a notice.A small defensive tweak keeps it robust:
- $score_column = $allowed_score_columns[ $strategy ]; + $score_column = isset( $allowed_score_columns[ $strategy ] ) + ? $allowed_score_columns[ $strategy ] + : 'desktop_score'; // or return false here if you prefer strictnessThis is not required today but makes the function safer against future refactors.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
class/class-mainwp-child-pagespeed.php(6 hunks)class/class-mainwp-utility.php(3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Codacy Static Code Analysis
🔇 Additional comments (4)
class/class-mainwp-child-pagespeed.php (1)
619-621:get_filter_options()return contract and custom URL caching look goodThe clarified docblock explaining the two-element return structure (
[0]SQL fragment,[1]values array) matches the actual usage elsewhere in the file. The added comment around custom post types being validated viaget_post_types()+ whitelist is also accurate.The new
custom_url_typesobject-cache layer correctly:
- Uses a stable key in the
mainwp_pagespeedgroup.- Caches the
DISTINCT typelist for an hour.- Preserves behavior by only iterating when the cached value is non-empty.
No issues here from a correctness standpoint.
Also applies to: 654-654, 682-687
class/class-mainwp-utility.php (3)
34-39: LGTM! Per-request cache avoids stale data issues.The per-request caching approach using a static property is a solid solution. Unlike persistent caching (e.g., wp_cache_set with TTL), this approach eliminates concerns about serving stale data when attachments are modified between requests, while still providing performance benefits within a single request.
513-513: Good documentation fix.The return type documentation now correctly reflects that the function returns an array of attachment post objects from
$wpdb->get_results(), not a single integer ID.
524-540: LGTM! Caching implementation is correct.The caching logic is well-structured:
- Cache keys properly differentiate between full GUID and suffix lookups
- Both query branches store results before returning
- phpcs:ignore comments accurately explain why direct queries are acceptable here
Introduces wp_cache_get/wp_cache_set to cache results of various direct database queries across multiple classes, including MySQL version lookups, WooCommerce sales/top seller stats, attachment lookups, and PageSpeed/BackWPup/TimeCapsule metadata. This reduces redundant queries for static or infrequently changing data, improving performance and scalability. Dynamic or real-time queries (such as activity logs or backup progress) are left uncached to ensure up-to-date results. Also adds comments and improves query safety by whitelisting allowed values and clarifying phpcs:ignore reasons.
All Submissions:
Changes proposed in this Pull Request:
Closes # .
How to test the changes in this Pull Request:
Other information:
Changelog entry
Summary by CodeRabbit