load_settings(); $this->init_hooks(); } /** * Load settings from database */ private function load_settings() { $defaults = array( // Site mode 'site_mode' => 'brochure', // brochure, woocommerce, learndash, woo_learndash // Page caching 'cache_enabled' => true, 'cache_expiry' => 0, // 0 = never expires 'cache_logged_in' => false, 'cache_gzip' => true, 'cache_brotli' => false, // HTML optimization 'html_minify' => true, 'html_remove_comments' => true, // CSS optimization 'css_minify' => true, 'css_aggregate' => true, 'css_inline_aggregate' => false, 'css_defer' => false, // Disabled by default for safety // JS optimization 'js_minify' => true, 'js_aggregate' => false, // Disabled by default for safety 'js_defer' => false, 'js_exclude_jquery' => true, // Exclusions 'exclude_paths' => array(), 'exclude_cookies' => array(), 'exclude_js' => array(), 'exclude_css' => array(), // Extra 'remove_emojis' => true, 'remove_query_strings' => true, 'preconnect_domains' => array(), 'dns_prefetch' => true, // Lazy loading 'lazyload_images' => true, 'lazyload_iframes' => true, 'lazyload_exclude' => array(), // Google Fonts 'google_fonts' => 'defer', // 'leave', 'remove', 'combine', 'defer' // Local Font Optimization 'local_font_display' => true, // Add font-display: swap to all @font-face rules 'preload_fonts' => array(), // Font URLs to preload (woff2 recommended) // Plugin Compatibility 'compat_auto_detect' => false, // Auto-detect plugins (can be fragile) 'compat_plugins' => array(), // Manually selected: woocommerce, learndash, wordfence, wpforms, gutenberg_fse ); $saved = get_option( 'maple_performance_settings', array() ); $this->settings = wp_parse_args( $saved, $defaults ); // Apply site mode presets $this->apply_site_mode(); } /** * Apply safe defaults based on site mode */ private function apply_site_mode() { switch ( $this->settings['site_mode'] ) { case 'woocommerce': $this->settings['js_aggregate'] = false; $this->settings['css_defer'] = false; $this->settings['cache_logged_in'] = false; break; case 'learndash': $this->settings['js_aggregate'] = false; $this->settings['css_defer'] = false; $this->settings['cache_logged_in'] = false; break; case 'woo_learndash': $this->settings['js_aggregate'] = false; $this->settings['js_defer'] = false; $this->settings['css_aggregate'] = true; $this->settings['css_inline_aggregate'] = false; $this->settings['css_defer'] = false; $this->settings['cache_logged_in'] = false; break; } // Ensure arrays are unique $this->settings['exclude_paths'] = array_unique( array_filter( $this->settings['exclude_paths'] ) ); $this->settings['exclude_cookies'] = array_unique( array_filter( $this->settings['exclude_cookies'] ) ); $this->settings['exclude_js'] = array_unique( array_filter( $this->settings['exclude_js'] ) ); $this->settings['exclude_css'] = array_unique( array_filter( $this->settings['exclude_css'] ) ); } /** * Initialize hooks */ private function init_hooks() { // Activation/Deactivation register_activation_hook( MAPLE_PERF_FILE, array( $this, 'activate' ) ); register_deactivation_hook( MAPLE_PERF_FILE, array( $this, 'deactivate' ) ); // Load components add_action( 'plugins_loaded', array( $this, 'load_components' ) ); // Admin if ( is_admin() ) { add_action( 'plugins_loaded', array( $this, 'load_admin' ) ); } } /** * Load plugin components */ public function load_components() { // Compatibility layer (always load to detect plugins) require_once MAPLE_PERF_PATH . 'inc/class-maple-compat.php'; Maple_Performance_Compat::get_instance(); // Cache component if ( $this->settings['cache_enabled'] && ! is_admin() ) { require_once MAPLE_PERF_PATH . 'inc/class-maple-cache.php'; Maple_Performance_Cache::get_instance(); } // Optimize component (CSS/JS/HTML) if ( ! is_admin() ) { require_once MAPLE_PERF_PATH . 'inc/class-maple-optimize.php'; Maple_Performance_Optimize::get_instance(); } // Extra optimizations if ( $this->settings['remove_emojis'] ) { $this->disable_emojis(); } } /** * Load admin component */ public function load_admin() { require_once MAPLE_PERF_PATH . 'inc/class-maple-admin.php'; Maple_Performance_Admin::get_instance(); } /** * Disable WordPress emojis */ private function disable_emojis() { remove_action( 'wp_head', 'print_emoji_detection_script', 7 ); remove_action( 'admin_print_scripts', 'print_emoji_detection_script' ); remove_action( 'wp_print_styles', 'print_emoji_styles' ); remove_action( 'admin_print_styles', 'print_emoji_styles' ); remove_filter( 'the_content_feed', 'wp_staticize_emoji' ); remove_filter( 'comment_text_rss', 'wp_staticize_emoji' ); remove_filter( 'wp_mail', 'wp_staticize_emoji_for_email' ); add_filter( 'tiny_mce_plugins', function( $plugins ) { return is_array( $plugins ) ? array_diff( $plugins, array( 'wpemoji' ) ) : array(); }); add_filter( 'wp_resource_hints', function( $urls, $relation_type ) { if ( 'dns-prefetch' === $relation_type ) { $urls = array_filter( $urls, function( $url ) { return strpos( $url, 'https://s.w.org/images/core/emoji/' ) === false; }); } return $urls; }, 10, 2 ); } /** * Plugin activation */ public function activate() { // Create cache directory if ( ! file_exists( MAPLE_PERF_CACHE_DIR ) ) { wp_mkdir_p( MAPLE_PERF_CACHE_DIR ); } // Create assets cache directory if ( ! file_exists( MAPLE_PERF_CACHE_DIR . 'assets/' ) ) { wp_mkdir_p( MAPLE_PERF_CACHE_DIR . 'assets/' ); } // Add index.php for security $index_content = "detect_caching_conflicts(); if ( ! empty( $conflicts ) ) { set_transient( 'maple_perf_activation_conflict', $conflicts, 60 ); } // Clear any existing cache $this->clear_cache(); // Clear any previously dismissed conflict notice delete_transient( 'maple_perf_conflict_dismissed' ); flush_rewrite_rules(); } /** * Detect caching plugin conflicts at activation time */ private function detect_caching_conflicts() { $conflicts = array(); // Check for common caching plugins if ( defined( 'WP_ROCKET_VERSION' ) ) { $conflicts[] = 'WP Rocket'; } if ( defined( 'W3TC' ) || class_exists( 'W3_Plugin_TotalCache' ) ) { $conflicts[] = 'W3 Total Cache'; } if ( defined( 'LSCWP_V' ) || class_exists( 'LiteSpeed_Cache' ) ) { $conflicts[] = 'LiteSpeed Cache'; } if ( defined( 'WPCACHEHOME' ) || function_exists( 'wp_cache_phase2' ) ) { $conflicts[] = 'WP Super Cache'; } if ( class_exists( 'WpFastestCache' ) || defined( 'WPFC_WP_CONTENT_BASENAME' ) ) { $conflicts[] = 'WP Fastest Cache'; } if ( class_exists( 'autoptimizeMain' ) || defined( 'AUTOPTIMIZE_PLUGIN_VERSION' ) ) { $conflicts[] = 'Autoptimize'; } if ( class_exists( 'Cache_Enabler' ) || defined( 'CACHE_ENABLER_VERSION' ) ) { $conflicts[] = 'Cache Enabler'; } if ( class_exists( 'WP_Hummingbird' ) || defined( 'WPHB_VERSION' ) ) { $conflicts[] = 'Hummingbird'; } if ( class_exists( 'Breeze_Admin' ) || defined( 'BREEZE_VERSION' ) ) { $conflicts[] = 'Breeze'; } if ( class_exists( 'SiteGround_Optimizer' ) || defined( 'SG_OPTIMIZER_VERSION' ) ) { $conflicts[] = 'SG Optimizer'; } return $conflicts; } /** * Plugin deactivation */ public function deactivate() { $this->clear_cache(); flush_rewrite_rules(); } /** * Clear all caches */ public function clear_cache() { // Clear page cache if ( class_exists( 'Maple_Performance_Cache' ) ) { Maple_Performance_Cache::clear_all(); } else { $this->recursive_delete( MAPLE_PERF_CACHE_DIR ); } // Recreate directories if ( ! file_exists( MAPLE_PERF_CACHE_DIR ) ) { wp_mkdir_p( MAPLE_PERF_CACHE_DIR ); } if ( ! file_exists( MAPLE_PERF_CACHE_DIR . 'assets/' ) ) { wp_mkdir_p( MAPLE_PERF_CACHE_DIR . 'assets/' ); } } /** * Recursively delete directory contents */ private 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 setting value */ public function get_setting( $key, $default = null ) { return isset( $this->settings[ $key ] ) ? $this->settings[ $key ] : $default; } /** * Check if current request should be excluded from caching */ public function is_excluded() { // Admin requests if ( is_admin() ) { return true; } // AJAX requests if ( wp_doing_ajax() ) { return true; } // Cron requests if ( wp_doing_cron() ) { return true; } // REST API if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) { return true; } // POST requests if ( 'GET' !== $_SERVER['REQUEST_METHOD'] ) { return true; } // DONOTCACHEPAGE constant (set by compat class or other plugins) if ( defined( 'DONOTCACHEPAGE' ) && DONOTCACHEPAGE ) { return true; } // Logged-in users (unless enabled) if ( ! $this->settings['cache_logged_in'] && is_user_logged_in() ) { return true; } // Get cookie exclusions with compat filter $exclude_cookies = apply_filters( 'maple_performance_cookie_exclusions', $this->settings['exclude_cookies'] ); // Check excluded cookies foreach ( $exclude_cookies as $cookie ) { if ( ! empty( $cookie ) ) { // Support partial cookie name matching (for session cookies with dynamic suffixes) foreach ( $_COOKIE as $cookie_name => $cookie_value ) { if ( strpos( $cookie_name, $cookie ) !== false ) { return true; } } } } // Get path exclusions with compat filter $exclude_paths = apply_filters( 'maple_performance_path_exclusions', $this->settings['exclude_paths'] ); // Check excluded paths $request_uri = $_SERVER['REQUEST_URI'] ?? ''; foreach ( $exclude_paths as $path ) { if ( ! empty( $path ) && strpos( $request_uri, $path ) !== false ) { return true; } } // Allow other plugins/code to exclude if ( apply_filters( 'maple_performance_exclude_optimization', false ) ) { return true; } // WooCommerce specific if ( function_exists( 'is_cart' ) && ( is_cart() || is_checkout() || is_account_page() ) ) { return true; } // LearnDash specific - always exclude when user is enrolled if ( function_exists( 'learndash_user_get_enrolled_courses' ) && is_user_logged_in() ) { return true; } return false; } } /** * Initialize plugin */ function maple_performance() { return Maple_Performance::get_instance(); } // Start the plugin maple_performance(); /** * Template tag to clear cache */ function maple_clear_cache() { maple_performance()->clear_cache(); }