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,539 @@
<?php
/**
* Plugin Name: Maple Performance WP
* Plugin URI: https://mapleopentech.ca/maple-performance-wp
* Description: A lightweight, privacy-focused WordPress performance plugin. No external dependencies, no tracking, no upsells.
* Version: 1.0.0
* Author: Maple Open Tech
* Author URI: https://mapleopentech.ca
* License: GPL-2.0-or-later
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
* Text Domain: maple-performance
* Domain Path: /languages
* Requires at least: 5.9
* Requires PHP: 7.4
*
* @package MaplePerformance
*/
// Prevent direct access
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
// Plugin constants
define( 'MAPLE_PERF_VERSION', '1.0.0' );
define( 'MAPLE_PERF_FILE', __FILE__ );
define( 'MAPLE_PERF_PATH', plugin_dir_path( __FILE__ ) );
define( 'MAPLE_PERF_URL', plugin_dir_url( __FILE__ ) );
define( 'MAPLE_PERF_CACHE_DIR', WP_CONTENT_DIR . '/cache/maple-performance/' );
define( 'MAPLE_PERF_CACHE_URL', content_url( '/cache/maple-performance/' ) );
/**
* Autoloader for plugin classes
*/
spl_autoload_register( function( $class ) {
$prefix = 'Maple_Performance_';
if ( strpos( $class, $prefix ) !== 0 ) {
return;
}
$class_name = str_replace( $prefix, '', $class );
$class_name = strtolower( str_replace( '_', '-', $class_name ) );
$file = MAPLE_PERF_PATH . 'inc/class-maple-' . $class_name . '.php';
if ( file_exists( $file ) ) {
require_once $file;
}
});
/**
* Main plugin class
*/
final class Maple_Performance {
/**
* Single instance
*/
private static $instance = null;
/**
* Plugin settings
*/
public $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->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 = "<?php\n// Silence is golden.\nif ( ! defined( 'ABSPATH' ) ) { exit; }";
if ( ! file_exists( MAPLE_PERF_CACHE_DIR . 'index.php' ) ) {
file_put_contents( MAPLE_PERF_CACHE_DIR . 'index.php', $index_content );
}
if ( ! file_exists( MAPLE_PERF_CACHE_DIR . 'assets/index.php' ) ) {
file_put_contents( MAPLE_PERF_CACHE_DIR . 'assets/index.php', $index_content );
}
// Set default settings
if ( ! get_option( 'maple_performance_settings' ) ) {
add_option( 'maple_performance_settings', array() );
}
// Check for caching conflicts and set flag to show notice
$conflicts = $this->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();
}