added maple performance caching plugin

This commit is contained in:
rodolfomartinez 2026-02-02 12:35:28 -05:00
parent c44c49a836
commit e468202f95
12 changed files with 4501 additions and 0 deletions

View file

@ -0,0 +1,837 @@
<?php
/**
* Admin Settings Class
*
* Handles plugin settings page and admin functionality.
*
* @package MaplePerformance
*/
// Prevent direct access
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Admin class
*/
class Maple_Performance_Admin {
/**
* Single instance
*/
private static $instance = null;
/**
* Get instance
*/
public static function get_instance() {
if ( null === self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Constructor
*/
private function __construct() {
$this->init_hooks();
}
/**
* Initialize hooks
*/
private function init_hooks() {
// Add admin menu
add_action( 'admin_menu', array( $this, 'add_menu' ) );
// Register settings
add_action( 'admin_init', array( $this, 'register_settings' ) );
// Admin bar cache clear button
add_action( 'admin_bar_menu', array( $this, 'add_admin_bar_button' ), 100 );
// Handle cache clear action
add_action( 'admin_init', array( $this, 'handle_cache_clear' ) );
// Admin notices
add_action( 'admin_notices', array( $this, 'admin_notices' ) );
// Admin styles
add_action( 'admin_enqueue_scripts', array( $this, 'admin_styles' ) );
// Plugin action links
add_filter( 'plugin_action_links_' . plugin_basename( MAPLE_PERF_FILE ), array( $this, 'action_links' ) );
// Privacy policy
add_action( 'admin_init', array( $this, 'add_privacy_policy_content' ) );
}
/**
* Add privacy policy content
*/
public function add_privacy_policy_content() {
if ( ! function_exists( 'wp_add_privacy_policy_content' ) ) {
return;
}
$content = sprintf(
'<h2>%s</h2><p>%s</p><h3>%s</h3><p>%s</p><h3>%s</h3><p>%s</p><h3>%s</h3><p>%s</p>',
__( 'Maple Performance WP', 'maple-performance' ),
__( 'This site uses the Maple Performance WP plugin to improve page load times through caching and asset optimization.', 'maple-performance' ),
__( 'What personal data we collect and why', 'maple-performance' ),
__( 'Maple Performance WP does not collect, store, or process any personal data. The plugin caches publicly-visible page content to improve performance. By default, pages are not cached for logged-in users, ensuring that no user-specific content is stored in the cache.', 'maple-performance' ),
__( 'Cookies', 'maple-performance' ),
__( 'Maple Performance WP does not set any cookies. The plugin may read existing cookies (such as WordPress login cookies or WooCommerce cart cookies) solely to determine whether to serve a cached page or bypass the cache for dynamic content. No cookie data is stored or transmitted.', 'maple-performance' ),
__( 'Third-party services', 'maple-performance' ),
__( 'Maple Performance WP does not connect to any external services or transmit any data to third parties. All caching and optimization is performed locally on your web server.', 'maple-performance' )
);
wp_add_privacy_policy_content(
'Maple Performance WP',
wp_kses_post( $content )
);
}
/**
* Add admin menu
*/
public function add_menu() {
add_options_page(
__( 'Maple Performance', 'maple-performance' ),
__( 'Maple Performance', 'maple-performance' ),
'manage_options',
'maple-performance',
array( $this, 'settings_page' )
);
}
/**
* Register settings
*/
public function register_settings() {
register_setting(
'maple_performance_settings',
'maple_performance_settings',
array( $this, 'sanitize_settings' )
);
}
/**
* Sanitize settings
*/
public function sanitize_settings( $input ) {
$sanitized = array();
// Site mode
$sanitized['site_mode'] = sanitize_text_field( $input['site_mode'] ?? 'brochure' );
// Google Fonts mode
$valid_font_modes = array( 'leave', 'remove', 'combine', 'defer' );
$sanitized['google_fonts'] = in_array( $input['google_fonts'] ?? 'defer', $valid_font_modes )
? $input['google_fonts']
: 'defer';
// Plugin compatibility
$sanitized['compat_auto_detect'] = ! empty( $input['compat_auto_detect'] );
// Manual plugin selection
$valid_compat_plugins = array( 'woocommerce', 'learndash', 'wordfence', 'wpforms', 'gutenberg_fse' );
$sanitized['compat_plugins'] = array();
if ( ! empty( $input['compat_plugins'] ) && is_array( $input['compat_plugins'] ) ) {
foreach ( $input['compat_plugins'] as $plugin ) {
if ( in_array( $plugin, $valid_compat_plugins ) ) {
$sanitized['compat_plugins'][] = $plugin;
}
}
}
// Boolean settings
$booleans = array(
'cache_enabled', 'cache_logged_in', 'cache_gzip', 'cache_brotli',
'html_minify', 'html_remove_comments',
'css_minify', 'css_aggregate', 'css_inline_aggregate', 'css_defer',
'js_minify', 'js_aggregate', 'js_defer', 'js_exclude_jquery',
'remove_emojis', 'remove_query_strings', 'dns_prefetch',
'lazyload_images', 'lazyload_iframes',
'local_font_display',
);
foreach ( $booleans as $key ) {
$sanitized[ $key ] = ! empty( $input[ $key ] );
}
// Integer settings
$sanitized['cache_expiry'] = absint( $input['cache_expiry'] ?? 0 );
// Array settings (textarea, one per line)
$arrays = array(
'exclude_paths', 'exclude_cookies', 'exclude_js', 'exclude_css',
'preconnect_domains', 'lazyload_exclude', 'preload_fonts',
);
foreach ( $arrays as $key ) {
$value = $input[ $key ] ?? '';
if ( is_string( $value ) ) {
$lines = explode( "\n", $value );
$sanitized[ $key ] = array_filter( array_map( function( $line ) {
// Sanitize each line - remove potentially dangerous characters
$line = trim( $line );
$line = str_replace( array( "\0", "\r" ), '', $line );
// For URLs/paths, use esc_url_raw for domains, sanitize_text_field for others
if ( strpos( $line, 'http' ) === 0 || strpos( $line, '//' ) === 0 ) {
return esc_url_raw( $line );
}
return sanitize_text_field( $line );
}, $lines ) );
} else {
$sanitized[ $key ] = array();
}
}
// Clear cache when settings change
maple_performance()->clear_cache();
return $sanitized;
}
/**
* Add admin bar button
*/
public function add_admin_bar_button( $wp_admin_bar ) {
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
$wp_admin_bar->add_node( array(
'id' => 'maple-performance',
'title' => '<span class="ab-icon dashicons dashicons-performance" style="font-family: dashicons; font-size: 20px; padding-top: 4px;"></span>' . __( 'Maple Cache', 'maple-performance' ),
'href' => '#',
) );
$wp_admin_bar->add_node( array(
'parent' => 'maple-performance',
'id' => 'maple-clear-cache',
'title' => __( 'Clear All Cache', 'maple-performance' ),
'href' => wp_nonce_url( admin_url( 'admin.php?action=maple_clear_cache' ), 'maple_clear_cache' ),
) );
$wp_admin_bar->add_node( array(
'parent' => 'maple-performance',
'id' => 'maple-settings',
'title' => __( 'Settings', 'maple-performance' ),
'href' => admin_url( 'options-general.php?page=maple-performance' ),
) );
// Show cache stats - use transient to avoid filesystem scan on every page load
$stats = get_transient( 'maple_perf_cache_stats' );
if ( false === $stats ) {
// Only calculate if not cached, with a 5-minute expiry
$stats = array(
'count' => Maple_Performance_Cache::get_cache_count(),
'size' => Maple_Performance_Cache::get_cache_size(),
);
set_transient( 'maple_perf_cache_stats', $stats, 5 * MINUTE_IN_SECONDS );
}
$wp_admin_bar->add_node( array(
'parent' => 'maple-performance',
'id' => 'maple-cache-stats',
'title' => sprintf( __( 'Cache: %d pages (%s)', 'maple-performance' ), $stats['count'], size_format( $stats['size'] ) ),
'href' => admin_url( 'options-general.php?page=maple-performance' ),
) );
}
/**
* Handle cache clear action
*/
public function handle_cache_clear() {
if ( ! isset( $_GET['action'] ) || $_GET['action'] !== 'maple_clear_cache' ) {
return;
}
if ( ! current_user_can( 'manage_options' ) ) {
wp_die( __( 'Unauthorized', 'maple-performance' ) );
}
if ( ! wp_verify_nonce( $_GET['_wpnonce'] ?? '', 'maple_clear_cache' ) ) {
wp_die( __( 'Invalid nonce', 'maple-performance' ) );
}
maple_performance()->clear_cache();
// Redirect back with notice
$redirect = remove_query_arg( array( 'action', '_wpnonce' ), wp_get_referer() );
$redirect = add_query_arg( 'maple_cache_cleared', '1', $redirect );
wp_safe_redirect( $redirect );
exit;
}
/**
* Admin notices
*/
public function admin_notices() {
// Show activation conflict notice (only once, immediately after activation)
$activation_conflicts = get_transient( 'maple_perf_activation_conflict' );
if ( false !== $activation_conflicts && is_array( $activation_conflicts ) ) {
delete_transient( 'maple_perf_activation_conflict' );
echo '<div class="notice notice-error">';
echo '<p><strong>' . esc_html__( '⚠️ Maple Performance WP - Important!', 'maple-performance' ) . '</strong></p>';
echo '<p>' . sprintf(
esc_html__( 'Another caching plugin is already active: %s', 'maple-performance' ),
'<strong>' . esc_html( implode( ', ', $activation_conflicts ) ) . '</strong>'
) . '</p>';
echo '<p>' . esc_html__( 'Running multiple caching plugins can cause site errors, blank pages, or performance issues.', 'maple-performance' ) . '</p>';
echo '<p>';
echo '<a href="' . esc_url( admin_url( 'plugins.php' ) ) . '" class="button button-primary">' . esc_html__( 'Choose which plugin to keep', 'maple-performance' ) . '</a> ';
echo '<a href="' . esc_url( admin_url( 'options-general.php?page=maple-performance' ) ) . '" class="button">' . esc_html__( 'Go to Settings', 'maple-performance' ) . '</a>';
echo '</p>';
echo '</div>';
}
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only display check
if ( isset( $_GET['maple_cache_cleared'] ) ) {
echo '<div class="notice notice-success is-dismissible">';
echo '<p>' . esc_html__( 'Maple Performance: Cache cleared successfully.', 'maple-performance' ) . '</p>';
echo '</div>';
}
// Check if settings were saved
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only display check
$page = isset( $_GET['page'] ) ? sanitize_text_field( wp_unslash( $_GET['page'] ) ) : '';
if ( isset( $_GET['settings-updated'] ) && $page === 'maple-performance' ) {
echo '<div class="notice notice-success is-dismissible">';
echo '<p>' . esc_html__( 'Maple Performance: Settings saved and cache cleared.', 'maple-performance' ) . '</p>';
echo '</div>';
}
}
/**
* Admin styles
*/
public function admin_styles( $hook ) {
if ( $hook !== 'settings_page_maple-performance' ) {
return;
}
wp_add_inline_style( 'common', '
.maple-settings { max-width: 900px; }
.maple-settings h2 { border-bottom: 1px solid #ccc; padding-bottom: 10px; margin-top: 30px; }
.maple-settings h2:first-of-type { margin-top: 10px; }
.maple-settings table { margin-bottom: 20px; }
.maple-settings .description { color: #666; font-style: italic; }
.maple-settings textarea { width: 100%; max-width: 400px; }
.maple-settings .site-mode-card {
border: 2px solid #ddd;
padding: 15px;
margin: 10px 0;
border-radius: 5px;
cursor: pointer;
}
.maple-settings .site-mode-card:hover { border-color: #2271b1; }
.maple-settings .site-mode-card.selected { border-color: #2271b1; background: #f0f7fc; }
.maple-settings .site-mode-card h4 { margin: 0 0 5px; }
.maple-settings .warning { color: #d63638; }
.maple-settings .safe { color: #00a32a; }
.maple-cache-stats {
background: #f0f0f1;
padding: 15px;
border-radius: 5px;
margin-bottom: 20px;
}
.maple-cache-stats strong { font-size: 1.2em; }
.maple-detected-plugins {
background: #f9f9f9;
border: 1px solid #ddd;
border-radius: 5px;
padding: 15px 20px;
margin-bottom: 25px;
}
.maple-detected-plugins h3 { margin-top: 0; }
.maple-detected-plugins table { margin-top: 15px; }
.maple-detected-plugins ul li { font-size: 13px; color: #555; }
' );
}
/**
* Plugin action links
*/
public function action_links( $links ) {
$settings_link = '<a href="' . admin_url( 'options-general.php?page=maple-performance' ) . '">' . __( 'Settings', 'maple-performance' ) . '</a>';
$clear_link = '<a href="' . wp_nonce_url( admin_url( 'admin.php?action=maple_clear_cache' ), 'maple_clear_cache' ) . '">' . __( 'Clear Cache', 'maple-performance' ) . '</a>';
array_unshift( $links, $settings_link, $clear_link );
return $links;
}
/**
* Settings page
*/
public function settings_page() {
$settings = maple_performance()->settings;
// Get cache stats
$cache_count = Maple_Performance_Cache::get_cache_count();
$cache_size = Maple_Performance_Cache::get_cache_size();
?>
<div class="wrap maple-settings">
<h1><?php _e( 'Maple Performance WP', 'maple-performance' ); ?></h1>
<p><?php _e( 'A lightweight, privacy-focused performance plugin. No external dependencies, no tracking, no upsells.', 'maple-performance' ); ?></p>
<div class="maple-cache-stats">
<strong><?php _e( 'Cache Status:', 'maple-performance' ); ?></strong>
<?php printf( __( '%d cached pages (%s)', 'maple-performance' ), $cache_count, size_format( $cache_size ) ); ?>
&nbsp;&nbsp;
<a href="<?php echo wp_nonce_url( admin_url( 'admin.php?action=maple_clear_cache' ), 'maple_clear_cache' ); ?>" class="button button-secondary">
<?php _e( 'Clear All Cache', 'maple-performance' ); ?>
</a>
</div>
<?php
// Get compat class for detection info
$compat = class_exists( 'Maple_Performance_Compat' ) ? Maple_Performance_Compat::get_instance() : null;
$detected_plugins = $compat ? $compat->get_detected() : array();
?>
<form method="post" action="options.php">
<?php settings_fields( 'maple_performance_settings' ); ?>
<h2><?php _e( 'Plugin Compatibility', 'maple-performance' ); ?></h2>
<p class="description"><?php _e( 'Configure compatibility rules for common plugins. These rules protect critical functionality like checkout, form submissions, and course tracking.', 'maple-performance' ); ?></p>
<table class="form-table">
<tr>
<th scope="row"><?php _e( 'Detection Mode', 'maple-performance' ); ?></th>
<td>
<label>
<input type="checkbox" name="maple_performance_settings[compat_auto_detect]" value="1" <?php checked( $settings['compat_auto_detect'] ); ?> id="compat_auto_detect">
<?php _e( 'Auto-detect installed plugins', 'maple-performance' ); ?>
</label>
<p class="description">
<?php _e( 'When enabled, Maple Performance will attempt to detect plugins automatically. This can be fragile if plugins change their file structure. <strong>Manual selection below is recommended for reliability.</strong>', 'maple-performance' ); ?>
</p>
</td>
</tr>
<tr id="manual_compat_row">
<th scope="row"><?php _e( 'Enable Compatibility For', 'maple-performance' ); ?></th>
<td>
<fieldset>
<label style="display: block; margin-bottom: 12px;">
<input type="checkbox" name="maple_performance_settings[compat_plugins][]" value="woocommerce" <?php checked( in_array( 'woocommerce', $settings['compat_plugins'] ?? array() ) ); ?>>
<strong>WooCommerce</strong>
<?php if ( ! empty( $detected_plugins['woocommerce'] ) ) : ?>
<span class="safe" style="margin-left: 5px;"> Detected</span>
<?php endif; ?>
<br>
<span class="description" style="margin-left: 24px;">Excludes cart, checkout, account pages from cache. Protects payment gateway scripts. Bypasses cache when cart has items.</span>
</label>
<label style="display: block; margin-bottom: 12px;">
<input type="checkbox" name="maple_performance_settings[compat_plugins][]" value="learndash" <?php checked( in_array( 'learndash', $settings['compat_plugins'] ?? array() ) ); ?>>
<strong>LearnDash</strong>
<?php if ( ! empty( $detected_plugins['learndash'] ) ) : ?>
<span class="safe" style="margin-left: 5px;"> Detected</span>
<?php endif; ?>
<br>
<span class="description" style="margin-left: 24px;">Excludes lesson, topic, quiz pages from cache. Protects progress tracking and quiz submission scripts.</span>
</label>
<label style="display: block; margin-bottom: 12px;">
<input type="checkbox" name="maple_performance_settings[compat_plugins][]" value="wpforms" <?php checked( in_array( 'wpforms', $settings['compat_plugins'] ?? array() ) ); ?>>
<strong>WPForms</strong>
<?php if ( ! empty( $detected_plugins['wpforms'] ) ) : ?>
<span class="safe" style="margin-left: 5px;"> Detected</span>
<?php endif; ?>
<br>
<span class="description" style="margin-left: 24px;">Protects form validation and submission scripts. Ensures AJAX form submissions work correctly.</span>
</label>
<label style="display: block; margin-bottom: 12px;">
<input type="checkbox" name="maple_performance_settings[compat_plugins][]" value="wordfence" <?php checked( in_array( 'wordfence', $settings['compat_plugins'] ?? array() ) ); ?>>
<strong>Wordfence</strong>
<?php if ( ! empty( $detected_plugins['wordfence'] ) ) : ?>
<span class="safe" style="margin-left: 5px;"> Detected</span>
<?php endif; ?>
<br>
<span class="description" style="margin-left: 24px;">Excludes login pages from cache. Respects firewall bypass cookies. Protects security scripts.</span>
</label>
<label style="display: block; margin-bottom: 12px;">
<input type="checkbox" name="maple_performance_settings[compat_plugins][]" value="gutenberg_fse" <?php checked( in_array( 'gutenberg_fse', $settings['compat_plugins'] ?? array() ) ); ?>>
<strong>Gutenberg / Block Themes (FSE)</strong>
<?php if ( ! empty( $detected_plugins['gutenberg_fse'] ) ) : ?>
<span class="safe" style="margin-left: 5px;"> Detected</span>
<?php endif; ?>
<br>
<span class="description" style="margin-left: 24px;">Protects global styles and block CSS. Safe optimization for Full Site Editing themes (Twenty Twenty-Two, etc.).</span>
</label>
</fieldset>
<p class="description" style="margin-top: 10px;">
<?php _e( '<strong>Tip:</strong> Select plugins you have installed even if not detected. Detection can miss plugins due to naming changes between versions.', 'maple-performance' ); ?>
</p>
</td>
</tr>
</table>
<script>
(function() {
var autoDetect = document.getElementById('compat_auto_detect');
var manualRow = document.getElementById('manual_compat_row');
function toggleManual() {
if (autoDetect.checked) {
manualRow.style.opacity = '0.5';
manualRow.querySelectorAll('input').forEach(function(el) {
el.disabled = true;
});
} else {
manualRow.style.opacity = '1';
manualRow.querySelectorAll('input').forEach(function(el) {
el.disabled = false;
});
}
}
autoDetect.addEventListener('change', toggleManual);
toggleManual();
})();
</script>
<h2><?php _e( 'Site Mode', 'maple-performance' ); ?></h2>
<p class="description"><?php _e( 'Select your site type to apply safe default settings. This affects which optimizations are enabled.', 'maple-performance' ); ?></p>
<table class="form-table">
<tr>
<th scope="row"><?php _e( 'Site Type', 'maple-performance' ); ?></th>
<td>
<select name="maple_performance_settings[site_mode]" id="site_mode">
<option value="brochure" <?php selected( $settings['site_mode'], 'brochure' ); ?>>
<?php _e( 'Brochure / Blog (No ecommerce or LMS)', 'maple-performance' ); ?>
</option>
<option value="woocommerce" <?php selected( $settings['site_mode'], 'woocommerce' ); ?>>
<?php _e( 'WooCommerce Store', 'maple-performance' ); ?>
</option>
<option value="learndash" <?php selected( $settings['site_mode'], 'learndash' ); ?>>
<?php _e( 'LearnDash LMS', 'maple-performance' ); ?>
</option>
<option value="woo_learndash" <?php selected( $settings['site_mode'], 'woo_learndash' ); ?>>
<?php _e( 'WooCommerce + LearnDash', 'maple-performance' ); ?>
</option>
</select>
<p class="description">
<?php _e( '<strong>Brochure:</strong> Full optimization enabled. <strong>WooCommerce/LearnDash:</strong> Conservative settings to protect checkout and lesson tracking.', 'maple-performance' ); ?>
</p>
</td>
</tr>
</table>
<h2><?php esc_html_e( 'Page Caching', 'maple-performance' ); ?></h2>
<?php
// Check for caching conflicts
$compat = Maple_Performance_Compat::get_instance();
if ( method_exists( $compat, 'get_caching_conflicts' ) ) {
$caching_conflicts = $compat->get_caching_conflicts();
if ( ! empty( $caching_conflicts ) ) {
echo '<div class="notice notice-warning inline" style="margin: 10px 0;">';
echo '<p><strong>' . esc_html__( '⚠️ Another caching plugin detected:', 'maple-performance' ) . '</strong> ';
echo esc_html( implode( ', ', $caching_conflicts ) ) . '</p>';
echo '<p>' . esc_html__( 'Consider disabling Page Cache below and using only the CSS/JS/HTML optimizations to avoid conflicts.', 'maple-performance' ) . '</p>';
echo '</div>';
}
}
?>
<table class="form-table">
<tr>
<th scope="row"><?php esc_html_e( 'Enable Page Cache', 'maple-performance' ); ?></th>
<td>
<label>
<input type="checkbox" name="maple_performance_settings[cache_enabled]" value="1" <?php checked( $settings['cache_enabled'] ); ?>>
<?php esc_html_e( 'Create static HTML files for faster delivery', 'maple-performance' ); ?>
</label>
</td>
</tr>
<tr>
<th scope="row"><?php _e( 'Cache Expiry', 'maple-performance' ); ?></th>
<td>
<input type="number" name="maple_performance_settings[cache_expiry]" value="<?php echo esc_attr( $settings['cache_expiry'] ); ?>" min="0" step="1" class="small-text">
<?php _e( 'hours (0 = never expires, cleared on content updates)', 'maple-performance' ); ?>
</td>
</tr>
<tr>
<th scope="row"><?php _e( 'Pre-compression', 'maple-performance' ); ?></th>
<td>
<label>
<input type="checkbox" name="maple_performance_settings[cache_gzip]" value="1" <?php checked( $settings['cache_gzip'] ); ?>>
<?php _e( 'Create Gzip compressed versions', 'maple-performance' ); ?>
</label>
<br>
<label>
<input type="checkbox" name="maple_performance_settings[cache_brotli]" value="1" <?php checked( $settings['cache_brotli'] ); ?> <?php disabled( ! function_exists( 'brotli_compress' ) ); ?>>
<?php _e( 'Create Brotli compressed versions', 'maple-performance' ); ?>
<?php if ( ! function_exists( 'brotli_compress' ) ) : ?>
<span class="description">(<?php _e( 'PHP Brotli extension not installed', 'maple-performance' ); ?>)</span>
<?php endif; ?>
</label>
</td>
</tr>
</table>
<h2><?php _e( 'HTML Optimization', 'maple-performance' ); ?></h2>
<table class="form-table">
<tr>
<th scope="row"><?php _e( 'Minify HTML', 'maple-performance' ); ?></th>
<td>
<label>
<input type="checkbox" name="maple_performance_settings[html_minify]" value="1" <?php checked( $settings['html_minify'] ); ?>>
<?php _e( 'Remove unnecessary whitespace from HTML', 'maple-performance' ); ?>
</label>
</td>
</tr>
<tr>
<th scope="row"><?php _e( 'Remove Comments', 'maple-performance' ); ?></th>
<td>
<label>
<input type="checkbox" name="maple_performance_settings[html_remove_comments]" value="1" <?php checked( $settings['html_remove_comments'] ); ?>>
<?php _e( 'Remove HTML comments (except IE conditionals)', 'maple-performance' ); ?>
</label>
</td>
</tr>
</table>
<h2><?php _e( 'CSS Optimization', 'maple-performance' ); ?></h2>
<table class="form-table">
<tr>
<th scope="row"><?php _e( 'Minify CSS', 'maple-performance' ); ?></th>
<td>
<label>
<input type="checkbox" name="maple_performance_settings[css_minify]" value="1" <?php checked( $settings['css_minify'] ); ?>>
<?php _e( 'Remove whitespace and comments from CSS', 'maple-performance' ); ?>
</label>
</td>
</tr>
<tr>
<th scope="row"><?php _e( 'Aggregate CSS', 'maple-performance' ); ?></th>
<td>
<label>
<input type="checkbox" name="maple_performance_settings[css_aggregate]" value="1" <?php checked( $settings['css_aggregate'] ); ?>>
<?php _e( 'Combine CSS files into one', 'maple-performance' ); ?>
</label>
</td>
</tr>
<tr>
<th scope="row"><?php _e( 'Defer CSS', 'maple-performance' ); ?></th>
<td>
<label>
<input type="checkbox" name="maple_performance_settings[css_defer]" value="1" <?php checked( $settings['css_defer'] ); ?>>
<?php _e( 'Load CSS asynchronously (may cause flash of unstyled content)', 'maple-performance' ); ?>
</label>
<p class="description warning"><?php _e( '⚠️ Not recommended for WooCommerce/LearnDash sites', 'maple-performance' ); ?></p>
</td>
</tr>
<tr>
<th scope="row"><?php _e( 'Exclude CSS', 'maple-performance' ); ?></th>
<td>
<textarea name="maple_performance_settings[exclude_css]" rows="4" placeholder="handle-name&#10;another-handle"><?php echo esc_textarea( implode( "\n", $settings['exclude_css'] ) ); ?></textarea>
<p class="description"><?php _e( 'CSS handles to exclude from optimization (one per line)', 'maple-performance' ); ?></p>
</td>
</tr>
</table>
<h2><?php _e( 'JavaScript Optimization', 'maple-performance' ); ?></h2>
<table class="form-table">
<tr>
<th scope="row"><?php _e( 'Minify JavaScript', 'maple-performance' ); ?></th>
<td>
<label>
<input type="checkbox" name="maple_performance_settings[js_minify]" value="1" <?php checked( $settings['js_minify'] ); ?>>
<?php _e( 'Remove whitespace and comments from JavaScript', 'maple-performance' ); ?>
</label>
</td>
</tr>
<tr>
<th scope="row"><?php _e( 'Aggregate JavaScript', 'maple-performance' ); ?></th>
<td>
<label>
<input type="checkbox" name="maple_performance_settings[js_aggregate]" value="1" <?php checked( $settings['js_aggregate'] ); ?>>
<?php _e( 'Combine JavaScript files into one', 'maple-performance' ); ?>
</label>
<p class="description warning"><?php _e( '⚠️ Can break WooCommerce checkout and LearnDash tracking. Test thoroughly!', 'maple-performance' ); ?></p>
</td>
</tr>
<tr>
<th scope="row"><?php _e( 'Exclude jQuery', 'maple-performance' ); ?></th>
<td>
<label>
<input type="checkbox" name="maple_performance_settings[js_exclude_jquery]" value="1" <?php checked( $settings['js_exclude_jquery'] ); ?>>
<?php _e( 'Keep jQuery loading separately (recommended)', 'maple-performance' ); ?>
</label>
</td>
</tr>
<tr>
<th scope="row"><?php _e( 'Exclude JavaScript', 'maple-performance' ); ?></th>
<td>
<textarea name="maple_performance_settings[exclude_js]" rows="4" placeholder="handle-name&#10;woocommerce&#10;learndash"><?php echo esc_textarea( implode( "\n", $settings['exclude_js'] ) ); ?></textarea>
<p class="description"><?php _e( 'JavaScript handles/strings to exclude from optimization (one per line)', 'maple-performance' ); ?></p>
</td>
</tr>
</table>
<h2><?php _e( 'Lazy Loading', 'maple-performance' ); ?></h2>
<table class="form-table">
<tr>
<th scope="row"><?php _e( 'Lazy Load Images', 'maple-performance' ); ?></th>
<td>
<label>
<input type="checkbox" name="maple_performance_settings[lazyload_images]" value="1" <?php checked( $settings['lazyload_images'] ); ?>>
<?php _e( 'Add loading="lazy" to images', 'maple-performance' ); ?>
</label>
</td>
</tr>
<tr>
<th scope="row"><?php _e( 'Lazy Load Iframes', 'maple-performance' ); ?></th>
<td>
<label>
<input type="checkbox" name="maple_performance_settings[lazyload_iframes]" value="1" <?php checked( $settings['lazyload_iframes'] ); ?>>
<?php _e( 'Add loading="lazy" to iframes (YouTube, maps, etc.)', 'maple-performance' ); ?>
</label>
</td>
</tr>
<tr>
<th scope="row"><?php _e( 'Exclude from Lazy Load', 'maple-performance' ); ?></th>
<td>
<textarea name="maple_performance_settings[lazyload_exclude]" rows="4" placeholder="wp-image-12345&#10;hero-image&#10;logo"><?php echo esc_textarea( implode( "\n", $settings['lazyload_exclude'] ) ); ?></textarea>
<p class="description"><?php _e( 'Image class names or strings to exclude from lazy loading (one per line). Exclude your LCP/hero image.', 'maple-performance' ); ?></p>
</td>
</tr>
</table>
<h2><?php _e( 'Extra Optimizations', 'maple-performance' ); ?></h2>
<table class="form-table">
<tr>
<th scope="row"><?php _e( 'Google Fonts', 'maple-performance' ); ?></th>
<td>
<select name="maple_performance_settings[google_fonts]" id="google_fonts">
<option value="leave" <?php selected( $settings['google_fonts'], 'leave' ); ?>>
<?php _e( 'Leave as is', 'maple-performance' ); ?>
</option>
<option value="remove" <?php selected( $settings['google_fonts'], 'remove' ); ?>>
<?php _e( 'Remove Google Fonts', 'maple-performance' ); ?>
</option>
<option value="combine" <?php selected( $settings['google_fonts'], 'combine' ); ?>>
<?php _e( 'Combine and link in head (with display:swap)', 'maple-performance' ); ?>
</option>
<option value="defer" <?php selected( $settings['google_fonts'], 'defer' ); ?>>
<?php _e( 'Combine and load deferred (non-render-blocking, with display:swap)', 'maple-performance' ); ?>
</option>
</select>
<p class="description">
<?php _e( '<strong>Defer</strong> is recommended - loads fonts without blocking page render.', 'maple-performance' ); ?>
</p>
</td>
</tr>
<tr>
<th scope="row"><?php _e( 'Local Font Display', 'maple-performance' ); ?></th>
<td>
<label>
<input type="checkbox" name="maple_performance_settings[local_font_display]" value="1" <?php checked( $settings['local_font_display'] ?? true ); ?>>
<?php _e( 'Add font-display: swap to theme/plugin fonts', 'maple-performance' ); ?>
</label>
<p class="description">
<?php _e( 'Ensures text remains visible while local fonts load. Fixes "Ensure text remains visible during webfont load" warning.', 'maple-performance' ); ?>
</p>
</td>
</tr>
<tr>
<th scope="row"><?php _e( 'Preload Fonts', 'maple-performance' ); ?></th>
<td>
<textarea name="maple_performance_settings[preload_fonts]" rows="3" class="large-text code"><?php
echo esc_textarea( implode( "\n", $settings['preload_fonts'] ?? array() ) );
?></textarea>
<p class="description">
<?php _e( 'Enter font URLs to preload (one per line). This breaks the CSS → Font chain and improves LCP. Use .woff2 files for best results.', 'maple-performance' ); ?>
<br>
<?php _e( '<strong>Example:</strong> /wp-content/themes/yourtheme/fonts/font-name.woff2', 'maple-performance' ); ?>
</p>
</td>
</tr>
<tr>
<th scope="row"><?php _e( 'Remove Emojis', 'maple-performance' ); ?></th>
<td>
<label>
<input type="checkbox" name="maple_performance_settings[remove_emojis]" value="1" <?php checked( $settings['remove_emojis'] ); ?>>
<?php _e( 'Remove WordPress emoji CSS and JavaScript', 'maple-performance' ); ?>
</label>
</td>
</tr>
<tr>
<th scope="row"><?php _e( 'Remove Query Strings', 'maple-performance' ); ?></th>
<td>
<label>
<input type="checkbox" name="maple_performance_settings[remove_query_strings]" value="1" <?php checked( $settings['remove_query_strings'] ); ?>>
<?php _e( 'Remove ?ver= from static resource URLs', 'maple-performance' ); ?>
</label>
</td>
</tr>
<tr>
<th scope="row"><?php _e( 'DNS Prefetch', 'maple-performance' ); ?></th>
<td>
<label>
<input type="checkbox" name="maple_performance_settings[dns_prefetch]" value="1" <?php checked( $settings['dns_prefetch'] ); ?>>
<?php _e( 'Add DNS prefetch hints for external domains', 'maple-performance' ); ?>
</label>
</td>
</tr>
<tr>
<th scope="row"><?php _e( 'Preconnect Domains', 'maple-performance' ); ?></th>
<td>
<textarea name="maple_performance_settings[preconnect_domains]" rows="4" placeholder="https://www.googletagmanager.com&#10;https://fonts.googleapis.com"><?php echo esc_textarea( implode( "\n", $settings['preconnect_domains'] ) ); ?></textarea>
<p class="description"><?php _e( 'Third-party domains to preconnect to (one per line, include https://)', 'maple-performance' ); ?></p>
</td>
</tr>
</table>
<h2><?php _e( 'Cache Exclusions', 'maple-performance' ); ?></h2>
<table class="form-table">
<tr>
<th scope="row"><?php _e( 'Exclude Paths', 'maple-performance' ); ?></th>
<td>
<textarea name="maple_performance_settings[exclude_paths]" rows="4" placeholder="/cart/&#10;/checkout/&#10;/my-account/"><?php echo esc_textarea( implode( "\n", $settings['exclude_paths'] ) ); ?></textarea>
<p class="description"><?php _e( 'URL paths to exclude from caching (one per line)', 'maple-performance' ); ?></p>
</td>
</tr>
<tr>
<th scope="row"><?php _e( 'Exclude Cookies', 'maple-performance' ); ?></th>
<td>
<textarea name="maple_performance_settings[exclude_cookies]" rows="4" placeholder="woocommerce_items_in_cart&#10;woocommerce_cart_hash"><?php echo esc_textarea( implode( "\n", $settings['exclude_cookies'] ) ); ?></textarea>
<p class="description"><?php _e( 'Cookie names that bypass cache when present (one per line)', 'maple-performance' ); ?></p>
</td>
</tr>
</table>
<?php submit_button( __( 'Save Settings & Clear Cache', 'maple-performance' ) ); ?>
</form>
<hr>
<p>
<strong><?php _e( 'Maple Performance WP', 'maple-performance' ); ?></strong> v<?php echo MAPLE_PERF_VERSION; ?> |
<?php _e( 'by', 'maple-performance' ); ?> <a href="https://mapleopentech.ca" target="_blank">Maple Open Tech</a> |
<?php _e( 'No tracking. No external dependencies. No upsells.', 'maple-performance' ); ?> 🍁
</p>
</div>
<?php
}
}

View file

@ -0,0 +1,608 @@
<?php
/**
* Page Caching Class
*
* Handles static HTML file generation and serving.
*
* @package MaplePerformance
*/
// Prevent direct access
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Cache class
*/
class Maple_Performance_Cache {
/**
* Single instance
*/
private static $instance = null;
/**
* Plugin settings
*/
private $settings;
/**
* Get instance
*/
public static function get_instance() {
if ( null === self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Constructor
*/
private function __construct() {
$this->settings = maple_performance()->settings;
$this->init_hooks();
}
/**
* Initialize hooks
*/
private function init_hooks() {
// Start output buffering early
add_action( 'template_redirect', array( $this, 'start_buffering' ), -999 );
// Cache clearing hooks
add_action( 'save_post', array( $this, 'clear_post_cache' ), 10, 1 );
add_action( 'delete_post', array( $this, 'clear_post_cache' ), 10, 1 );
add_action( 'wp_trash_post', array( $this, 'clear_post_cache' ), 10, 1 );
add_action( 'comment_post', array( $this, 'clear_post_cache_by_comment' ), 10, 2 );
add_action( 'edit_comment', array( $this, 'clear_post_cache_by_comment' ), 10, 1 );
add_action( 'switch_theme', array( __CLASS__, 'clear_all' ) );
add_action( 'activated_plugin', array( __CLASS__, 'clear_all' ) );
add_action( 'deactivated_plugin', array( __CLASS__, 'clear_all' ) );
add_action( 'update_option_permalink_structure', array( __CLASS__, 'clear_all' ) );
// WooCommerce hooks
if ( class_exists( 'WooCommerce' ) ) {
add_action( 'woocommerce_product_set_stock', array( $this, 'clear_product_cache' ) );
add_action( 'woocommerce_variation_set_stock', array( $this, 'clear_product_cache' ) );
}
}
/**
* Start output buffering
*/
public function start_buffering() {
// Check if we should cache this request
if ( maple_performance()->is_excluded() ) {
return;
}
// Check if cached version exists
$cache_file = $this->get_cache_file_path();
if ( $this->serve_cache( $cache_file ) ) {
exit;
}
// Start buffering for cache creation
ob_start( array( $this, 'process_buffer' ) );
}
/**
* Process output buffer and create cache
*/
public function process_buffer( $buffer ) {
// Don't cache empty or error pages
if ( empty( $buffer ) || http_response_code() !== 200 ) {
return $buffer;
}
// Don't cache if contains certain markers
if ( $this->should_skip_caching( $buffer ) ) {
return $buffer;
}
// Write cache file
$this->write_cache( $buffer );
return $buffer;
}
/**
* Check if page should skip caching based on content
*/
private function should_skip_caching( $buffer ) {
// Skip if contains no-cache markers
$skip_markers = array(
'<!-- no-cache -->',
'<!-- maple-no-cache -->',
'woocommerce-cart',
'woocommerce-checkout',
);
foreach ( $skip_markers as $marker ) {
if ( strpos( $buffer, $marker ) !== false ) {
return true;
}
}
return false;
}
/**
* Get cache file path for current request
*/
private function get_cache_file_path( $url = null ) {
if ( null === $url ) {
$url = $this->get_current_url();
}
$parsed = parse_url( $url );
$host = $parsed['host'] ?? '';
$path = $parsed['path'] ?? '/';
// Sanitize host - only allow alphanumeric, dots, and hyphens
$host = preg_replace( '/[^a-zA-Z0-9.-]/', '', $host );
// Sanitize path - remove any path traversal attempts
$path = str_replace( array( '..', "\0" ), '', $path );
$path = preg_replace( '/[^a-zA-Z0-9\/_-]/', '', $path );
$path = preg_replace( '#/+#', '/', $path ); // Collapse multiple slashes
$path = rtrim( $path, '/' );
if ( empty( $path ) ) {
$path = '/index';
}
// Limit path depth to prevent excessive directory creation
$path_parts = explode( '/', trim( $path, '/' ) );
if ( count( $path_parts ) > 10 ) {
$path_parts = array_slice( $path_parts, 0, 10 );
$path = '/' . implode( '/', $path_parts );
}
// Build cache directory structure
$cache_dir = MAPLE_PERF_CACHE_DIR . $host . $path . '/';
// Verify the resolved path is within cache directory
$real_cache_base = realpath( MAPLE_PERF_CACHE_DIR );
if ( $real_cache_base ) {
// Use dirname to check parent since $cache_dir may not exist yet
$parent_dir = dirname( $cache_dir );
while ( ! is_dir( $parent_dir ) && $parent_dir !== dirname( $parent_dir ) ) {
$parent_dir = dirname( $parent_dir );
}
if ( is_dir( $parent_dir ) ) {
$real_parent = realpath( $parent_dir );
if ( $real_parent && strpos( $real_parent, $real_cache_base ) !== 0 ) {
// Path escapes cache directory - return safe fallback
return MAPLE_PERF_CACHE_DIR . 'fallback/https-index.html';
}
}
}
// Determine cache file name
$is_https = is_ssl() ? 'https' : 'http';
$cache_file = $cache_dir . $is_https . '-index.html';
return $cache_file;
}
/**
* Get current URL
*/
private function get_current_url() {
$scheme = is_ssl() ? 'https' : 'http';
// Sanitize HTTP_HOST
$host = isset( $_SERVER['HTTP_HOST'] ) ? $_SERVER['HTTP_HOST'] : '';
$host = preg_replace( '/[^a-zA-Z0-9.-]/', '', $host );
// Sanitize REQUEST_URI
$uri = isset( $_SERVER['REQUEST_URI'] ) ? $_SERVER['REQUEST_URI'] : '/';
$uri = filter_var( $uri, FILTER_SANITIZE_URL );
// Remove query strings for cache key
$uri = strtok( $uri, '?' );
// Remove any null bytes or path traversal
$uri = str_replace( array( "\0", '..' ), '', $uri );
return $scheme . '://' . $host . $uri;
}
/**
* Serve cached file if exists
*/
private function serve_cache( $cache_file ) {
// Check for gzipped version first
if ( $this->settings['cache_gzip'] && $this->client_accepts_gzip() ) {
$gzip_file = $cache_file . '.gz';
if ( file_exists( $gzip_file ) && $this->is_cache_valid( $gzip_file ) ) {
header( 'Content-Encoding: gzip' );
header( 'Content-Type: text/html; charset=UTF-8' );
header( 'X-Maple-Cache: HIT (gzip)' );
readfile( $gzip_file );
return true;
}
}
// Check for brotli version
if ( $this->settings['cache_brotli'] && $this->client_accepts_brotli() ) {
$br_file = $cache_file . '.br';
if ( file_exists( $br_file ) && $this->is_cache_valid( $br_file ) ) {
header( 'Content-Encoding: br' );
header( 'Content-Type: text/html; charset=UTF-8' );
header( 'X-Maple-Cache: HIT (brotli)' );
readfile( $br_file );
return true;
}
}
// Serve uncompressed
if ( file_exists( $cache_file ) && $this->is_cache_valid( $cache_file ) ) {
header( 'Content-Type: text/html; charset=UTF-8' );
header( 'X-Maple-Cache: HIT' );
readfile( $cache_file );
return true;
}
return false;
}
/**
* Check if cache file is still valid
*/
private function is_cache_valid( $cache_file ) {
// No expiry set
if ( empty( $this->settings['cache_expiry'] ) || $this->settings['cache_expiry'] === 0 ) {
return true;
}
$file_age = time() - filemtime( $cache_file );
$max_age = $this->settings['cache_expiry'] * HOUR_IN_SECONDS;
return $file_age < $max_age;
}
/**
* Check if client accepts gzip
*/
private function client_accepts_gzip() {
$encoding = $_SERVER['HTTP_ACCEPT_ENCODING'] ?? '';
return strpos( $encoding, 'gzip' ) !== false;
}
/**
* Check if client accepts brotli
*/
private function client_accepts_brotli() {
$encoding = $_SERVER['HTTP_ACCEPT_ENCODING'] ?? '';
return strpos( $encoding, 'br' ) !== false;
}
/**
* Write cache file
*/
private function write_cache( $content ) {
$cache_file = $this->get_cache_file_path();
$cache_dir = dirname( $cache_file );
// Create directory if needed
if ( ! file_exists( $cache_dir ) ) {
wp_mkdir_p( $cache_dir );
}
// Verify we're still within cache directory (paranoid check)
$real_cache_base = realpath( MAPLE_PERF_CACHE_DIR );
$real_cache_dir = realpath( $cache_dir );
if ( false === $real_cache_base || false === $real_cache_dir ) {
return;
}
if ( strpos( $real_cache_dir, $real_cache_base ) !== 0 ) {
return;
}
// Add cache signature
$timestamp = gmdate( 'D, d M Y H:i:s' ) . ' GMT';
$signature = "\n<!-- Maple Performance WP @ {$timestamp} -->";
$content .= $signature;
// Write HTML file with proper permissions
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents
file_put_contents( $cache_file, $content );
if ( file_exists( $cache_file ) ) {
chmod( $cache_file, 0644 );
}
// Create gzipped version
if ( $this->settings['cache_gzip'] && function_exists( 'gzencode' ) ) {
$gzip_content = gzencode( $content, 9 );
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents
file_put_contents( $cache_file . '.gz', $gzip_content );
if ( file_exists( $cache_file . '.gz' ) ) {
chmod( $cache_file . '.gz', 0644 );
}
}
// Create brotli version
if ( $this->settings['cache_brotli'] && function_exists( 'brotli_compress' ) ) {
$br_content = brotli_compress( $content );
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents
file_put_contents( $cache_file . '.br', $br_content );
if ( file_exists( $cache_file . '.br' ) ) {
chmod( $cache_file . '.br', 0644 );
}
}
}
/**
* Clear cache for a specific post
*/
public function clear_post_cache( $post_id ) {
// Don't clear for autosaves or revisions
if ( wp_is_post_autosave( $post_id ) || wp_is_post_revision( $post_id ) ) {
return;
}
$post = get_post( $post_id );
if ( ! $post || $post->post_status !== 'publish' ) {
return;
}
// Clear post URL
$url = get_permalink( $post_id );
$this->clear_url_cache( $url );
// Clear home page
$this->clear_url_cache( home_url( '/' ) );
// Clear archive pages
$post_type = get_post_type( $post_id );
$archive_url = get_post_type_archive_link( $post_type );
if ( $archive_url ) {
$this->clear_url_cache( $archive_url );
}
// Clear category/tag archives - limit to prevent performance issues
$taxonomies = get_object_taxonomies( $post_type );
$terms_cleared = 0;
$max_terms = 50; // Limit term cache clearing to prevent slowdown
foreach ( $taxonomies as $taxonomy ) {
if ( $terms_cleared >= $max_terms ) {
break;
}
$terms = get_the_terms( $post_id, $taxonomy );
if ( $terms && ! is_wp_error( $terms ) ) {
foreach ( $terms as $term ) {
if ( $terms_cleared >= $max_terms ) {
break;
}
$term_url = get_term_link( $term );
if ( ! is_wp_error( $term_url ) ) {
$this->clear_url_cache( $term_url );
$terms_cleared++;
}
}
}
}
// Clear stats transient since cache changed
delete_transient( 'maple_perf_cache_stats' );
}
/**
* Clear cache by comment
*/
public function clear_post_cache_by_comment( $comment_id, $comment_approved = null ) {
$comment = get_comment( $comment_id );
if ( $comment ) {
$this->clear_post_cache( $comment->comment_post_ID );
}
}
/**
* Clear product cache (WooCommerce)
*/
public function clear_product_cache( $product ) {
if ( is_numeric( $product ) ) {
$product_id = $product;
} else {
$product_id = $product->get_id();
}
$this->clear_post_cache( $product_id );
}
/**
* Clear cache for a specific URL
*/
public function clear_url_cache( $url ) {
$cache_file = $this->get_cache_file_path( $url );
if ( file_exists( $cache_file ) ) {
@unlink( $cache_file );
}
if ( file_exists( $cache_file . '.gz' ) ) {
@unlink( $cache_file . '.gz' );
}
if ( file_exists( $cache_file . '.br' ) ) {
@unlink( $cache_file . '.br' );
}
// Also clear https version if http was cleared and vice versa
$alt_file = str_replace(
array( '/http-index.html', '/https-index.html' ),
array( '/https-index.html', '/http-index.html' ),
$cache_file
);
if ( file_exists( $alt_file ) ) {
@unlink( $alt_file );
}
if ( file_exists( $alt_file . '.gz' ) ) {
@unlink( $alt_file . '.gz' );
}
if ( file_exists( $alt_file . '.br' ) ) {
@unlink( $alt_file . '.br' );
}
}
/**
* Clear all cache
*/
public static function clear_all() {
self::recursive_delete( MAPLE_PERF_CACHE_DIR );
// Recreate directories
wp_mkdir_p( MAPLE_PERF_CACHE_DIR );
wp_mkdir_p( MAPLE_PERF_CACHE_DIR . 'assets/' );
// Add index.php files with ABSPATH check
$index = "<?php\n// Silence is golden.\nif ( ! defined( 'ABSPATH' ) ) { exit; }";
file_put_contents( MAPLE_PERF_CACHE_DIR . 'index.php', $index );
file_put_contents( MAPLE_PERF_CACHE_DIR . 'assets/index.php', $index );
// Clear stats transient
delete_transient( 'maple_perf_cache_stats' );
}
/**
* Recursively delete directory
*/
private static function recursive_delete( $dir ) {
if ( ! is_dir( $dir ) ) {
return;
}
// Safety check - only delete within wp-content/cache/maple-performance
$real_dir = realpath( $dir );
$allowed_base = realpath( WP_CONTENT_DIR );
if ( false === $real_dir || false === $allowed_base ) {
return;
}
if ( strpos( $real_dir, $allowed_base ) !== 0 ) {
return;
}
// Additional safety - must contain 'maple-performance' in path
if ( strpos( $real_dir, 'maple-performance' ) === false ) {
return;
}
// Limit iterations to prevent runaway deletion
$max_iterations = 50000;
$iterations = 0;
try {
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator( $dir, RecursiveDirectoryIterator::SKIP_DOTS ),
RecursiveIteratorIterator::CHILD_FIRST
);
foreach ( $files as $file ) {
if ( ++$iterations > $max_iterations ) {
break; // Safety limit reached
}
$file_path = $file->getRealPath();
// Verify each file is within allowed directory
if ( strpos( $file_path, $real_dir ) !== 0 ) {
continue;
}
if ( $file->isDir() ) {
@rmdir( $file_path );
} else {
@unlink( $file_path );
}
}
} catch ( Exception $e ) {
// Handle iterator exceptions gracefully
return;
}
}
/**
* Get cache size
*/
public static function get_cache_size() {
$size = 0;
if ( ! is_dir( MAPLE_PERF_CACHE_DIR ) ) {
return $size;
}
// Limit iterations to prevent runaway on huge cache directories
$max_iterations = 10000;
$iterations = 0;
try {
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator( MAPLE_PERF_CACHE_DIR, RecursiveDirectoryIterator::SKIP_DOTS )
);
foreach ( $files as $file ) {
if ( ++$iterations > $max_iterations ) {
break; // Safety limit reached
}
if ( $file->isFile() ) {
$size += $file->getSize();
}
}
} catch ( Exception $e ) {
// Handle iterator exceptions gracefully
return $size;
}
return $size;
}
/**
* Get number of cached files
*/
public static function get_cache_count() {
$count = 0;
if ( ! is_dir( MAPLE_PERF_CACHE_DIR ) ) {
return $count;
}
// Limit iterations to prevent runaway on huge cache directories
$max_iterations = 10000;
$iterations = 0;
try {
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator( MAPLE_PERF_CACHE_DIR, RecursiveDirectoryIterator::SKIP_DOTS )
);
foreach ( $files as $file ) {
if ( ++$iterations > $max_iterations ) {
break; // Safety limit reached
}
if ( $file->isFile() && pathinfo( $file, PATHINFO_EXTENSION ) === 'html' ) {
$count++;
}
}
} catch ( Exception $e ) {
// Handle iterator exceptions gracefully
return $count;
}
return $count;
}
}

View file

@ -0,0 +1,801 @@
<?php
/**
* Plugin Compatibility Class
*
* Handles automatic detection and safe defaults for common plugins.
*
* @package MaplePerformance
*/
// Prevent direct access
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Compatibility class
*/
class Maple_Performance_Compat {
/**
* Single instance
*/
private static $instance = null;
/**
* Detected plugins (via auto-detect)
*/
private $detected = array();
/**
* Enabled plugins (manual selection or auto-detected)
*/
private $enabled = array();
/**
* Plugin settings
*/
private $settings = array();
/**
* Get instance
*/
public static function get_instance() {
if ( null === self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Constructor
*/
private function __construct() {
$this->settings = maple_performance()->settings;
$this->determine_enabled_plugins();
$this->init_hooks();
}
/**
* Determine which plugins should have compatibility rules applied
*/
private function determine_enabled_plugins() {
// If auto-detect is enabled, detect plugins
if ( ! empty( $this->settings['compat_auto_detect'] ) ) {
$this->detect_plugins();
$this->enabled = $this->detected;
} else {
// Use manual selection
$manual = $this->settings['compat_plugins'] ?? array();
foreach ( $manual as $plugin ) {
$this->enabled[ $plugin ] = true;
}
}
}
/**
* Detect active plugins (used when auto-detect is enabled)
*/
private function detect_plugins() {
// WooCommerce
if ( class_exists( 'WooCommerce' ) || defined( 'WC_PLUGIN_FILE' ) ) {
$this->detected['woocommerce'] = true;
}
// LearnDash
if ( defined( 'LEARNDASH_VERSION' ) || class_exists( 'SFWD_LMS' ) ) {
$this->detected['learndash'] = true;
}
// WPForms
if ( defined( 'WPFORMS_VERSION' ) || class_exists( 'WPForms' ) ) {
$this->detected['wpforms'] = true;
}
// Wordfence
if ( defined( 'WORDFENCE_VERSION' ) || class_exists( 'wordfence' ) ) {
$this->detected['wordfence'] = true;
}
// Gutenberg FSE / Block Themes
if ( function_exists( 'wp_is_block_theme' ) && wp_is_block_theme() ) {
$this->detected['gutenberg_fse'] = true;
}
// Gravity Forms
if ( class_exists( 'GFForms' ) || defined( 'GF_MIN_WP_VERSION' ) ) {
$this->detected['gravityforms'] = true;
}
// Contact Form 7
if ( defined( 'WPCF7_VERSION' ) || class_exists( 'WPCF7' ) ) {
$this->detected['cf7'] = true;
}
// Elementor
if ( defined( 'ELEMENTOR_VERSION' ) ) {
$this->detected['elementor'] = true;
}
// === Caching plugin conflict detection ===
// WP Rocket
if ( defined( 'WP_ROCKET_VERSION' ) ) {
$this->detected['wp_rocket'] = true;
}
// W3 Total Cache
if ( defined( 'W3TC' ) || class_exists( 'W3_Plugin_TotalCache' ) ) {
$this->detected['w3tc'] = true;
}
// LiteSpeed Cache
if ( defined( 'LSCWP_V' ) || class_exists( 'LiteSpeed_Cache' ) ) {
$this->detected['litespeed'] = true;
}
// WP Super Cache
if ( defined( 'WPCACHEHOME' ) || function_exists( 'wp_cache_phase2' ) ) {
$this->detected['wp_super_cache'] = true;
}
// WP Fastest Cache
if ( class_exists( 'WpFastestCache' ) || defined( 'WPFC_WP_CONTENT_BASENAME' ) ) {
$this->detected['wp_fastest_cache'] = true;
}
// Autoptimize
if ( class_exists( 'autoptimizeMain' ) || defined( 'AUTOPTIMIZE_PLUGIN_VERSION' ) ) {
$this->detected['autoptimize'] = true;
}
// Cache Enabler
if ( class_exists( 'Cache_Enabler' ) || defined( 'CACHE_ENABLER_VERSION' ) ) {
$this->detected['cache_enabler'] = true;
}
// Hummingbird
if ( class_exists( 'WP_Hummingbird' ) || defined( 'WPHB_VERSION' ) ) {
$this->detected['hummingbird'] = true;
}
// Breeze (Cloudways)
if ( class_exists( 'Breeze_Admin' ) || defined( 'BREEZE_VERSION' ) ) {
$this->detected['breeze'] = true;
}
// SG Optimizer (SiteGround)
if ( class_exists( 'SiteGround_Optimizer' ) || defined( 'SG_OPTIMIZER_VERSION' ) ) {
$this->detected['sg_optimizer'] = true;
}
// Swift Performance
if ( class_exists( 'Swift_Performance' ) || defined( 'FLAVOR_FLAVOR' ) ) {
$this->detected['swift_performance'] = true;
}
// Comet Cache
if ( class_exists( 'WebSharks\\CometCache\\Classes\\Plugin' ) || defined( 'COMET_CACHE_VERSION' ) ) {
$this->detected['comet_cache'] = true;
}
// Powered Cache
if ( defined( 'POWERED_CACHE_VERSION' ) ) {
$this->detected['powered_cache'] = true;
}
// Perfmatters
if ( class_exists( 'Perfmatters' ) || defined( 'PERFMATTERS_VERSION' ) ) {
$this->detected['perfmatters'] = true;
}
}
/**
* Initialize hooks
*/
private function init_hooks() {
// Only apply filters if at least one plugin is enabled
if ( empty( $this->enabled ) ) {
// Still check for caching conflicts in admin
add_action( 'admin_notices', array( $this, 'conflict_notices' ) );
return;
}
// Filter exclusions based on enabled plugins
add_filter( 'maple_performance_js_exclusions', array( $this, 'add_js_exclusions' ) );
add_filter( 'maple_performance_css_exclusions', array( $this, 'add_css_exclusions' ) );
add_filter( 'maple_performance_path_exclusions', array( $this, 'add_path_exclusions' ) );
add_filter( 'maple_performance_cookie_exclusions', array( $this, 'add_cookie_exclusions' ) );
// Add admin notice for conflicts
add_action( 'admin_notices', array( $this, 'conflict_notices' ) );
// WooCommerce specific hooks
if ( $this->is_enabled( 'woocommerce' ) ) {
$this->init_woocommerce_compat();
}
// LearnDash specific hooks
if ( $this->is_enabled( 'learndash' ) ) {
$this->init_learndash_compat();
}
// WPForms specific hooks
if ( $this->is_enabled( 'wpforms' ) ) {
$this->init_wpforms_compat();
}
// Wordfence specific hooks
if ( $this->is_enabled( 'wordfence' ) ) {
$this->init_wordfence_compat();
}
// Gutenberg FSE specific hooks
if ( $this->is_enabled( 'gutenberg_fse' ) ) {
$this->init_gutenberg_fse_compat();
}
}
/**
* Check if plugin compatibility is enabled (either via auto-detect or manual)
*/
public function is_enabled( $plugin ) {
return ! empty( $this->enabled[ $plugin ] );
}
/**
* Check if plugin was detected (for display purposes)
*/
public function is_detected( $plugin ) {
return ! empty( $this->detected[ $plugin ] );
}
/**
* Get all detected plugins
*/
public function get_detected() {
// Always run detection for display in admin
if ( empty( $this->detected ) ) {
$this->detect_plugins();
}
return $this->detected;
}
/**
* Get all enabled plugins
*/
public function get_enabled() {
return $this->enabled;
}
/**
* Add JS exclusions for enabled plugins
*/
public function add_js_exclusions( $exclusions ) {
// WooCommerce
if ( $this->is_enabled( 'woocommerce' ) ) {
$exclusions = array_merge( $exclusions, array(
'woocommerce',
'wc-',
'wc_',
'jquery-blockui',
'selectWoo',
'select2',
'js-cookie',
'cart-fragments',
'checkout',
'add-to-cart',
'payment',
'stripe',
'paypal',
'square',
'braintree',
) );
}
// LearnDash
if ( $this->is_enabled( 'learndash' ) ) {
$exclusions = array_merge( $exclusions, array(
'learndash',
'sfwd-',
'sfwd_',
'ld-',
'ld_',
'ldlms',
'quiz',
'wpProQuiz',
) );
}
// WPForms
if ( $this->is_enabled( 'wpforms' ) ) {
$exclusions = array_merge( $exclusions, array(
'wpforms',
'wpforms-',
'jquery-validation',
'mailcheck',
'inputmask',
) );
}
// Wordfence
if ( $this->is_enabled( 'wordfence' ) ) {
$exclusions = array_merge( $exclusions, array(
'wordfence',
'wf-',
'wfls-',
) );
}
// Gutenberg FSE / Block Themes
if ( $this->is_enabled( 'gutenberg_fse' ) ) {
$exclusions = array_merge( $exclusions, array(
'wp-block-',
'wp-edit-',
) );
}
// Gravity Forms
if ( $this->is_enabled( 'gravityforms' ) ) {
$exclusions = array_merge( $exclusions, array(
'gform',
'gravityforms',
'gf_',
) );
}
// Contact Form 7
if ( $this->is_enabled( 'cf7' ) ) {
$exclusions = array_merge( $exclusions, array(
'contact-form-7',
'wpcf7',
) );
}
// Elementor
if ( $this->is_enabled( 'elementor' ) ) {
$exclusions = array_merge( $exclusions, array(
'elementor-',
'elementor_',
) );
}
return array_unique( $exclusions );
}
/**
* Add CSS exclusions for enabled plugins
*/
public function add_css_exclusions( $exclusions ) {
// LearnDash - Focus Mode CSS should load normally
if ( $this->is_enabled( 'learndash' ) ) {
$exclusions = array_merge( $exclusions, array(
'learndash-front',
'sfwd-',
) );
}
// WPForms
if ( $this->is_enabled( 'wpforms' ) ) {
$exclusions = array_merge( $exclusions, array(
'wpforms',
) );
}
// Gutenberg FSE / Block Themes - protect global styles
if ( $this->is_enabled( 'gutenberg_fse' ) ) {
$exclusions = array_merge( $exclusions, array(
'global-styles',
'wp-block-',
'core-block-',
) );
}
return array_unique( $exclusions );
}
/**
* Add path exclusions for enabled plugins
*/
public function add_path_exclusions( $exclusions ) {
// WooCommerce
if ( $this->is_enabled( 'woocommerce' ) ) {
$exclusions = array_merge( $exclusions, array(
'/cart/',
'/cart',
'/checkout/',
'/checkout',
'/my-account/',
'/my-account',
'/wc-api/',
'/order-received/',
'/order-pay/',
'/view-order/',
'/add-to-cart=',
'?add-to-cart=',
'?remove_item=',
'?removed_item=',
) );
}
// LearnDash
if ( $this->is_enabled( 'learndash' ) ) {
$exclusions = array_merge( $exclusions, array(
'/lessons/',
'/topic/',
'/quiz/',
'/quizzes/',
'/certificates/',
'/sfwd-',
) );
}
// Wordfence
if ( $this->is_enabled( 'wordfence' ) ) {
$exclusions = array_merge( $exclusions, array(
'/wp-login.php',
'?wfls-',
) );
}
return array_unique( $exclusions );
}
/**
* Add cookie exclusions for enabled plugins
*/
public function add_cookie_exclusions( $exclusions ) {
// WooCommerce
if ( $this->is_enabled( 'woocommerce' ) ) {
$exclusions = array_merge( $exclusions, array(
'woocommerce_items_in_cart',
'woocommerce_cart_hash',
'wp_woocommerce_session_',
'woocommerce_recently_viewed',
) );
}
// Wordfence
if ( $this->is_enabled( 'wordfence' ) ) {
$exclusions = array_merge( $exclusions, array(
'wfCBLBypass',
'wf_loginalerted_',
) );
}
return array_unique( $exclusions );
}
/**
* Initialize WooCommerce compatibility
*/
private function init_woocommerce_compat() {
// Don't cache cart fragments AJAX
add_action( 'wc_ajax_get_refreshed_fragments', array( $this, 'disable_caching' ), 1 );
add_action( 'wc_ajax_add_to_cart', array( $this, 'disable_caching' ), 1 );
add_action( 'wc_ajax_remove_from_cart', array( $this, 'disable_caching' ), 1 );
add_action( 'wc_ajax_apply_coupon', array( $this, 'disable_caching' ), 1 );
add_action( 'wc_ajax_remove_coupon', array( $this, 'disable_caching' ), 1 );
add_action( 'wc_ajax_update_shipping_method', array( $this, 'disable_caching' ), 1 );
add_action( 'wc_ajax_checkout', array( $this, 'disable_caching' ), 1 );
// Clear cache on stock changes
add_action( 'woocommerce_product_set_stock', array( $this, 'clear_product_cache' ) );
add_action( 'woocommerce_variation_set_stock', array( $this, 'clear_product_cache' ) );
// Clear cache on order status changes (affects stock)
add_action( 'woocommerce_order_status_changed', array( $this, 'clear_on_order_status' ), 10, 3 );
// Exclude WooCommerce pages from optimization
add_filter( 'maple_performance_exclude_optimization', array( $this, 'exclude_woo_pages' ) );
}
/**
* Initialize LearnDash compatibility
*/
private function init_learndash_compat() {
// Don't cache any LearnDash AJAX
add_action( 'wp_ajax_learndash_mark_complete', array( $this, 'disable_caching' ), 1 );
add_action( 'wp_ajax_ld_adv_quiz_result', array( $this, 'disable_caching' ), 1 );
add_action( 'wp_ajax_wpProQuiz_admin_ajax', array( $this, 'disable_caching' ), 1 );
// Clear cache on course enrollment/completion
add_action( 'learndash_course_completed', array( $this, 'clear_learndash_user_cache' ), 10, 1 );
add_action( 'learndash_lesson_completed', array( $this, 'clear_learndash_user_cache' ), 10, 1 );
add_action( 'learndash_topic_completed', array( $this, 'clear_learndash_user_cache' ), 10, 1 );
add_action( 'learndash_quiz_completed', array( $this, 'clear_learndash_user_cache' ), 10, 2 );
// Clear cache when user is enrolled
add_action( 'learndash_update_course_access', array( $this, 'clear_course_cache' ), 10, 4 );
}
/**
* Initialize WPForms compatibility
*/
private function init_wpforms_compat() {
// WPForms AJAX is generally fine, but ensure it's not cached
add_action( 'wp_ajax_wpforms_submit', array( $this, 'disable_caching' ), 1 );
add_action( 'wp_ajax_nopriv_wpforms_submit', array( $this, 'disable_caching' ), 1 );
add_action( 'wp_ajax_wpforms_file_upload_speed_test', array( $this, 'disable_caching' ), 1 );
add_action( 'wp_ajax_nopriv_wpforms_file_upload_speed_test', array( $this, 'disable_caching' ), 1 );
add_action( 'wp_ajax_wpforms_restricted_email', array( $this, 'disable_caching' ), 1 );
add_action( 'wp_ajax_nopriv_wpforms_restricted_email', array( $this, 'disable_caching' ), 1 );
}
/**
* Initialize Wordfence compatibility
*/
private function init_wordfence_compat() {
// Don't interfere with Wordfence login security
add_action( 'wp_ajax_nopriv_wordfence_ls_authenticate', array( $this, 'disable_caching' ), 1 );
add_action( 'wp_ajax_wordfence_ls_authenticate', array( $this, 'disable_caching' ), 1 );
// Don't cache Wordfence blocked pages
if ( defined( 'WORDFENCE_BLOCKED' ) && WORDFENCE_BLOCKED ) {
add_filter( 'maple_performance_should_cache', '__return_false' );
}
// Respect Wordfence's caching headers
add_filter( 'maple_performance_should_cache', array( $this, 'check_wordfence_bypass' ) );
}
/**
* Initialize Gutenberg FSE / Block Theme compatibility
*/
private function init_gutenberg_fse_compat() {
// Don't aggregate global styles inline CSS
add_filter( 'maple_performance_aggregate_inline_css', '__return_false' );
// Protect block editor assets on frontend
add_filter( 'maple_performance_exclude_optimization', array( $this, 'exclude_block_editor_frontend' ) );
}
/**
* Exclude block editor frontend from aggressive optimization
*/
public function exclude_block_editor_frontend( $exclude ) {
// If viewing a page with blocks that need JS interaction
// This is conservative - most block content is static
return $exclude;
}
/**
* Disable caching for current request
*/
public function disable_caching() {
if ( ! defined( 'DONOTCACHEPAGE' ) ) {
define( 'DONOTCACHEPAGE', true );
}
}
/**
* Clear product cache
*/
public function clear_product_cache( $product ) {
if ( ! class_exists( 'Maple_Performance_Cache' ) ) {
return;
}
$product_id = is_numeric( $product ) ? $product : $product->get_id();
$url = get_permalink( $product_id );
if ( $url ) {
$cache = Maple_Performance_Cache::get_instance();
$cache->clear_url_cache( $url );
}
// Also clear shop page
$shop_url = function_exists( 'wc_get_page_permalink' ) ? wc_get_page_permalink( 'shop' ) : '';
if ( $shop_url ) {
$cache->clear_url_cache( $shop_url );
}
}
/**
* Clear cache on order status change
*/
public function clear_on_order_status( $order_id, $old_status, $new_status ) {
// Only clear when status changes might affect stock
$stock_statuses = array( 'completed', 'processing', 'cancelled', 'refunded' );
if ( in_array( $new_status, $stock_statuses ) || in_array( $old_status, $stock_statuses ) ) {
$order = wc_get_order( $order_id );
if ( $order ) {
foreach ( $order->get_items() as $item ) {
$product_id = $item->get_product_id();
$this->clear_product_cache( $product_id );
}
}
}
}
/**
* Exclude WooCommerce pages from JS/CSS optimization
*/
public function exclude_woo_pages( $exclude ) {
if ( function_exists( 'is_cart' ) && is_cart() ) {
return true;
}
if ( function_exists( 'is_checkout' ) && is_checkout() ) {
return true;
}
if ( function_exists( 'is_account_page' ) && is_account_page() ) {
return true;
}
return $exclude;
}
/**
* Clear LearnDash user-related cache
*/
public function clear_learndash_user_cache( $data ) {
// LearnDash pages are user-specific and already excluded for logged-in users
// But we can clear the course archive pages
if ( ! class_exists( 'Maple_Performance_Cache' ) ) {
return;
}
$cache = Maple_Performance_Cache::get_instance();
// Clear course archive
$course_archive = get_post_type_archive_link( 'sfwd-courses' );
if ( $course_archive ) {
$cache->clear_url_cache( $course_archive );
}
// Clear home page (might show course counts)
$cache->clear_url_cache( home_url( '/' ) );
}
/**
* Clear course cache when enrollment changes
*/
public function clear_course_cache( $user_id, $course_id, $access_list, $remove ) {
if ( ! class_exists( 'Maple_Performance_Cache' ) ) {
return;
}
$cache = Maple_Performance_Cache::get_instance();
// Clear the course page (might show enrollment count)
$course_url = get_permalink( $course_id );
if ( $course_url ) {
$cache->clear_url_cache( $course_url );
}
}
/**
* Check Wordfence bypass
*/
public function check_wordfence_bypass( $should_cache ) {
// If Wordfence has set bypass cookie, don't cache
if ( isset( $_COOKIE['wfCBLBypass'] ) ) {
return false;
}
return $should_cache;
}
/**
* Show admin notices for plugin conflicts
*/
public function conflict_notices() {
// Build list of conflicting caching plugins
$caching_conflicts = $this->get_caching_conflicts();
// Show caching conflict warning on all admin pages (dismissible)
if ( ! empty( $caching_conflicts ) && ! get_transient( 'maple_perf_conflict_dismissed' ) ) {
$this->show_conflict_warning( $caching_conflicts );
}
// Show detailed info only on Maple Performance settings page
$screen = get_current_screen();
if ( ! $screen || $screen->id !== 'settings_page_maple-performance' ) {
return;
}
// If conflicts exist but were dismissed, show a subtle reminder on settings page
if ( ! empty( $caching_conflicts ) && get_transient( 'maple_perf_conflict_dismissed' ) ) {
echo '<div class="notice notice-warning">';
echo '<p><strong>' . esc_html__( 'Reminder:', 'maple-performance' ) . '</strong> ';
echo sprintf(
esc_html__( 'Other caching plugin(s) detected: %s. For best results, use only one caching solution.', 'maple-performance' ),
'<code>' . implode( '</code>, <code>', array_map( 'esc_html', $caching_conflicts ) ) . '</code>'
);
echo '</p></div>';
}
// Show detected compatible plugins (info)
$compatible = array();
if ( $this->is_detected( 'woocommerce' ) ) $compatible[] = 'WooCommerce';
if ( $this->is_detected( 'learndash' ) ) $compatible[] = 'LearnDash';
if ( $this->is_detected( 'wpforms' ) ) $compatible[] = 'WPForms';
if ( $this->is_detected( 'wordfence' ) ) $compatible[] = 'Wordfence';
if ( $this->is_detected( 'gravityforms' ) ) $compatible[] = 'Gravity Forms';
if ( $this->is_detected( 'cf7' ) ) $compatible[] = 'Contact Form 7';
if ( $this->is_detected( 'elementor' ) ) $compatible[] = 'Elementor';
if ( ! empty( $compatible ) ) {
echo '<div class="notice notice-info">';
echo '<p><strong>' . esc_html__( 'Maple Performance - Detected Plugins:', 'maple-performance' ) . '</strong> ';
echo sprintf(
esc_html__( 'Compatibility rules available for: %s. Enable them in the Plugin Compatibility section below.', 'maple-performance' ),
'<code>' . implode( '</code>, <code>', array_map( 'esc_html', $compatible ) ) . '</code>'
);
echo '</p></div>';
}
}
/**
* Get list of detected caching plugin conflicts
*/
public function get_caching_conflicts() {
$conflicts = array();
$caching_plugins = array(
'wp_rocket' => 'WP Rocket',
'w3tc' => 'W3 Total Cache',
'litespeed' => 'LiteSpeed Cache',
'wp_super_cache' => 'WP Super Cache',
'wp_fastest_cache' => 'WP Fastest Cache',
'autoptimize' => 'Autoptimize',
'cache_enabler' => 'Cache Enabler',
'hummingbird' => 'Hummingbird',
'breeze' => 'Breeze',
'sg_optimizer' => 'SG Optimizer',
'swift_performance'=> 'Swift Performance',
'comet_cache' => 'Comet Cache',
'powered_cache' => 'Powered Cache',
'perfmatters' => 'Perfmatters',
);
foreach ( $caching_plugins as $key => $name ) {
if ( $this->is_detected( $key ) ) {
$conflicts[] = $name;
}
}
return $conflicts;
}
/**
* Check if any caching conflicts exist
*/
public function has_caching_conflicts() {
return ! empty( $this->get_caching_conflicts() );
}
/**
* Show conflict warning notice
*/
private function show_conflict_warning( $conflicts ) {
// Handle dismiss action
if ( isset( $_GET['maple_dismiss_conflict'] ) && wp_verify_nonce( $_GET['_wpnonce'] ?? '', 'maple_dismiss_conflict' ) ) {
set_transient( 'maple_perf_conflict_dismissed', true, 7 * DAY_IN_SECONDS );
return;
}
$dismiss_url = wp_nonce_url( add_query_arg( 'maple_dismiss_conflict', '1' ), 'maple_dismiss_conflict' );
echo '<div class="notice notice-error">';
echo '<p><strong>' . esc_html__( '⚠️ Maple Performance - Caching Conflict Detected', 'maple-performance' ) . '</strong></p>';
echo '<p>' . sprintf(
esc_html__( 'The following caching/optimization plugin(s) are also active: %s', 'maple-performance' ),
'<strong>' . implode( ', ', array_map( 'esc_html', $conflicts ) ) . '</strong>'
) . '</p>';
echo '<p>' . esc_html__( 'Running multiple caching plugins simultaneously can cause:', 'maple-performance' ) . '</p>';
echo '<ul style="list-style: disc; margin-left: 20px;">';
echo '<li>' . esc_html__( 'Blank pages or site errors', 'maple-performance' ) . '</li>';
echo '<li>' . esc_html__( 'Stale/outdated content being served', 'maple-performance' ) . '</li>';
echo '<li>' . esc_html__( 'Slower performance (double processing)', 'maple-performance' ) . '</li>';
echo '<li>' . esc_html__( 'Cache invalidation failures', 'maple-performance' ) . '</li>';
echo '</ul>';
echo '<p><strong>' . esc_html__( 'Recommended action:', 'maple-performance' ) . '</strong> ';
echo esc_html__( 'Deactivate the other caching plugin(s), or deactivate Maple Performance if you prefer to keep your existing solution.', 'maple-performance' );
echo '</p>';
echo '<p>';
echo '<a href="' . esc_url( admin_url( 'plugins.php' ) ) . '" class="button button-primary">' . esc_html__( 'Go to Plugins', 'maple-performance' ) . '</a> ';
echo '<a href="' . esc_url( $dismiss_url ) . '" class="button">' . esc_html__( 'Dismiss for 7 days', 'maple-performance' ) . '</a>';
echo '</p>';
echo '</div>';
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,11 @@
<?php
/**
* Silence is golden.
*
* @package MaplePerformance
*/
// Prevent direct access
if ( ! defined( 'ABSPATH' ) ) {
exit;
}