added maple performance caching plugin
This commit is contained in:
parent
c44c49a836
commit
e468202f95
12 changed files with 4501 additions and 0 deletions
539
native/wordpress/maple-performance-wp/maple-performance.php
Normal file
539
native/wordpress/maple-performance-wp/maple-performance.php
Normal 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();
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue