init_modules(); $this->init_hooks(); } /** * Initialize modules. */ private function init_modules() { // Core modules (always loaded). $this->modules['conflict_detector'] = new WPSCO_Conflict_Detector(); $this->modules['cache_manager'] = new WPSCO_Cache_Manager(); $this->modules['cache_purger'] = new WPSCO_Cache_Purger(); $this->modules['security'] = new WPSCO_Security(); $this->modules['settings'] = new WPSCO_Settings(); // Frontend optimization modules (loaded conditionally). if ( ! is_admin() && ! wpsco_get_setting( 'emergency_disable', false ) ) { if ( wpsco_get_setting( 'page_cache_enabled', false ) ) { $this->modules['page_cache'] = new WPSCO_Page_Cache(); } if ( wpsco_get_setting( 'css_minify', false ) || wpsco_get_setting( 'css_combine', false ) || wpsco_get_setting( 'css_critical', false ) || wpsco_get_setting( 'css_async', false ) ) { $this->modules['css_optimizer'] = new WPSCO_CSS_Optimizer(); } if ( wpsco_get_setting( 'js_minify', false ) || wpsco_get_setting( 'js_defer', false ) || wpsco_get_setting( 'js_delay', false ) ) { $this->modules['js_optimizer'] = new WPSCO_JS_Optimizer(); } $this->modules['html_optimizer'] = new WPSCO_HTML_Optimizer(); if ( wpsco_get_setting( 'lazy_load_images', false ) || wpsco_get_setting( 'add_missing_dimensions', false ) || wpsco_get_setting( 'lcp_image_preload', false ) ) { $this->modules['image_optimizer'] = new WPSCO_Image_Optimizer(); } if ( wpsco_get_setting( 'font_display_swap', false ) || wpsco_get_setting( 'google_fonts_local', false ) || wpsco_get_setting( 'preload_fonts', false ) ) { $this->modules['font_optimizer'] = new WPSCO_Font_Optimizer(); } } // WordPress optimizations (always check even on admin). $this->modules['wordpress_optimizer'] = new WPSCO_WordPress_Optimizer(); // Integrations. if ( wpsco_is_woocommerce_active() ) { $this->modules['woocommerce'] = new WPSCO_WooCommerce(); } if ( wpsco_is_elementor_active() ) { $this->modules['elementor'] = new WPSCO_Elementor(); } if ( wpsco_get_setting( 'cloudflare_enabled', false ) ) { $this->modules['cloudflare'] = new WPSCO_Cloudflare(); } if ( wpsco_get_setting( 'cdn_enabled', false ) ) { $this->modules['cdn'] = new WPSCO_CDN(); } // Admin. if ( is_admin() ) { $this->modules['admin'] = new WPSCO_Admin(); } // Diagnostics (loaded on demand via admin). // Database cleaner (loaded on demand via admin). } /** * Initialize hooks. */ private function init_hooks() { // Admin bar. add_action( 'admin_bar_menu', array( $this, 'admin_bar_menu' ), 100 ); // Auto cache purge hooks. add_action( 'save_post', array( $this, 'on_content_change' ), 10, 2 ); add_action( 'delete_post', array( $this, 'on_content_change' ), 10 ); add_action( 'wp_update_nav_menu', array( $this, 'on_menu_update' ) ); add_action( 'update_option_sidebars_widgets', array( $this, 'on_widget_update' ) ); add_action( 'switch_theme', array( $this, 'on_theme_change' ) ); add_action( 'customize_save_after', array( $this, 'on_theme_change' ) ); add_action( 'activated_plugin', array( $this, 'on_plugin_change' ) ); add_action( 'deactivated_plugin', array( $this, 'on_plugin_change' ) ); // Cron handlers. add_action( 'wpsco_cache_cleanup', array( $this, 'cron_cache_cleanup' ) ); add_action( 'wpsco_expired_transients_cleanup', array( $this, 'cron_expired_transients' ) ); add_action( 'wpsco_cache_warming', array( $this, 'cron_cache_warming' ) ); // Plugin action links. add_filter( 'plugin_action_links_' . WPSCO_PLUGIN_BASENAME, array( $this, 'plugin_action_links' ) ); } /** * Get a module instance. * * @param string $name Module name. * @return object|null */ public function get_module( $name ) { return isset( $this->modules[ $name ] ) ? $this->modules[ $name ] : null; } /** * Admin bar menu. * * @param WP_Admin_Bar $admin_bar Admin bar instance. */ public function admin_bar_menu( $admin_bar ) { if ( ! wpsco_current_user_can() ) { return; } $admin_bar->add_node( array( 'id' => 'wpsco', 'title' => ' ' . esc_html__( 'WP Speed', 'speednest' ), 'href' => admin_url( 'admin.php?page=wpsco-dashboard' ), ) ); $items = array( 'clear-cache' => array( 'title' => __( 'Clear Cache', 'speednest' ), 'href' => wp_nonce_url( admin_url( 'admin.php?page=wpsco-cache&action=clear_all' ), 'wpsco_cache_action' ), ), 'clear-all' => array( 'title' => __( 'Purge Everything Possible', 'speednest' ), 'href' => wp_nonce_url( admin_url( 'admin.php?page=wpsco-cache&action=purge_everything' ), 'wpsco_cache_action' ), ), 'clear-css' => array( 'title' => __( 'Clear CSS Cache', 'speednest' ), 'href' => wp_nonce_url( admin_url( 'admin.php?page=wpsco-cache&action=clear_css' ), 'wpsco_cache_action' ), ), 'clear-js' => array( 'title' => __( 'Clear JS Cache', 'speednest' ), 'href' => wp_nonce_url( admin_url( 'admin.php?page=wpsco-cache&action=clear_js' ), 'wpsco_cache_action' ), ), 'clear-html' => array( 'title' => __( 'Clear HTML Cache', 'speednest' ), 'href' => wp_nonce_url( admin_url( 'admin.php?page=wpsco-cache&action=clear_html' ), 'wpsco_cache_action' ), ), 'clear-object' => array( 'title' => __( 'Clear Object Cache', 'speednest' ), 'href' => wp_nonce_url( admin_url( 'admin.php?page=wpsco-cache&action=clear_object' ), 'wpsco_cache_action' ), ), 'reset-opcache' => array( 'title' => __( 'Reset OPcache', 'speednest' ), 'href' => wp_nonce_url( admin_url( 'admin.php?page=wpsco-cache&action=reset_opcache' ), 'wpsco_cache_action' ), ), 'diagnostics' => array( 'title' => __( 'Run Diagnostics', 'speednest' ), 'href' => admin_url( 'admin.php?page=wpsco-diagnostics' ), ), ); foreach ( $items as $id => $item ) { $admin_bar->add_node( array( 'parent' => 'wpsco', 'id' => 'wpsco-' . $id, 'title' => esc_html( $item['title'] ), 'href' => esc_url( $item['href'] ), ) ); } } /** * Handle content changes — targeted cache purge. * * @param int $post_id Post ID. * @param WP_Post $post Post object (optional). */ public function on_content_change( $post_id, $post = null ) { if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { return; } if ( wp_is_post_revision( $post_id ) ) { return; } $purger = $this->get_module( 'cache_purger' ); if ( ! $purger ) { return; } $post_obj = $post ? $post : get_post( $post_id ); $post_type = $post_obj ? $post_obj->post_type : ''; // If a template/header/footer is updated, clear all page HTML & CSS caches. $is_template = in_array( $post_type, array( 'elementor_library', 'elementor-hf', 'shopengine-template', 'wp_template', 'wp_template_part', 'custom_css', ), true ); if ( $is_template ) { $purger->purge_html_cache(); $purger->purge_css_cache(); wpsco_increment_cache_version(); wpsco_log( sprintf( 'Global template updated (ID %d, type %s) — purged all HTML & CSS cache.', $post_id, $post_type ) ); return; } // Purge specific post URL. $url = get_permalink( $post_id ); if ( $url ) { $purger->purge_url( $url ); } // Purge homepage. $purger->purge_url( home_url( '/' ) ); // Purge category/archive pages. if ( $post_obj ) { $taxonomies = get_object_taxonomies( $post_obj->post_type ); foreach ( $taxonomies as $taxonomy ) { $terms = get_the_terms( $post_id, $taxonomy ); if ( $terms && ! is_wp_error( $terms ) ) { foreach ( $terms as $term ) { $term_link = get_term_link( $term ); if ( ! is_wp_error( $term_link ) ) { $purger->purge_url( $term_link ); } } } } } // Purge CSS/JS cache if settings changed. wpsco_log( sprintf( 'Cache purged for post ID %d', $post_id ) ); } /** * Handle menu updates. */ public function on_menu_update() { $purger = $this->get_module( 'cache_purger' ); if ( $purger ) { $purger->purge_all_html(); } wpsco_log( 'Cache purged: menu update' ); } /** * Handle widget updates. */ public function on_widget_update() { $purger = $this->get_module( 'cache_purger' ); if ( $purger ) { $purger->purge_all_html(); } wpsco_log( 'Cache purged: widget update' ); } /** * Handle theme changes. */ public function on_theme_change() { $purger = $this->get_module( 'cache_purger' ); if ( $purger ) { $purger->purge_all(); } wpsco_increment_cache_version(); wpsco_log( 'Full cache purge: theme change' ); } /** * Handle plugin changes. */ public function on_plugin_change() { $purger = $this->get_module( 'cache_purger' ); if ( $purger ) { $purger->purge_all(); } // Re-detect conflicts. $detector = $this->get_module( 'conflict_detector' ); if ( $detector ) { $detector->detect_all(); } wpsco_log( 'Full cache purge: plugin change' ); } /** * Cron: cache cleanup. */ public function cron_cache_cleanup() { $purger = $this->get_module( 'cache_purger' ); if ( $purger ) { $purger->cleanup_expired_html_cache(); } } /** * Cron: expired transients. */ public function cron_expired_transients() { if ( function_exists( 'delete_expired_transients' ) ) { delete_expired_transients( true ); return; } global $wpdb; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Fallback direct query required for bulk deletion of expired transients. $wpdb->query( $wpdb->prepare( "DELETE a, b FROM {$wpdb->options} a INNER JOIN {$wpdb->options} b ON b.option_name = CONCAT('_transient_timeout_', SUBSTRING(a.option_name, 12)) WHERE a.option_name LIKE %s AND b.option_value < %d", $wpdb->esc_like( '_transient_' ) . '%', time() ) ); } /** * Cron: cache warming. */ public function cron_cache_warming() { if ( ! wpsco_get_setting( 'cache_warming_enabled', false ) ) { return; } $urls = $this->get_cache_warming_urls(); $concurrency = (int) wpsco_get_setting( 'cache_warming_concurrency', 2 ); $concurrency = max( 1, min( 5, $concurrency ) ); $batch_size = $concurrency; $batches = array_chunk( $urls, $batch_size ); foreach ( $batches as $batch ) { foreach ( $batch as $url ) { wp_remote_get( $url, array( 'timeout' => 10, 'blocking' => false, 'user-agent' => 'WPSpeedCacheOptimizer/CacheWarmer', ) ); } // Small delay between batches. usleep( 500000 ); } } /** * Get URLs for cache warming. * * @return array */ private function get_cache_warming_urls() { $urls = array( home_url( '/' ) ); // Get recent pages. $pages = get_posts( array( 'post_type' => 'page', 'post_status' => 'publish', 'posts_per_page' => 20, 'fields' => 'ids', ) ); foreach ( $pages as $page_id ) { $urls[] = get_permalink( $page_id ); } // Get recent posts. $posts = get_posts( array( 'post_type' => 'post', 'post_status' => 'publish', 'posts_per_page' => 20, 'fields' => 'ids', ) ); foreach ( $posts as $post_id ) { $urls[] = get_permalink( $post_id ); } // WooCommerce products. if ( wpsco_is_woocommerce_active() ) { $products = get_posts( array( 'post_type' => 'product', 'post_status' => 'publish', 'posts_per_page' => 30, 'fields' => 'ids', ) ); foreach ( $products as $product_id ) { $urls[] = get_permalink( $product_id ); } } return array_unique( $urls ); } /** * Plugin action links. * * @param array $links Existing links. * @return array */ public function plugin_action_links( $links ) { $custom = array( '' . esc_html__( 'Dashboard', 'speednest' ) . '', '' . esc_html__( 'Settings', 'speednest' ) . '', ); return array_merge( $custom, $links ); } }