| 1 |
<?php |
| 2 |
/** |
| 3 |
* Main plugin bootstrap. |
| 4 |
* |
| 5 |
* @package XSpeed |
| 6 |
*/ |
| 7 |
|
| 8 |
namespace XSpeed; |
| 9 |
|
| 10 |
defined( 'ABSPATH' ) || exit; |
| 11 |
|
| 12 |
class Plugin { |
| 13 |
|
| 14 |
private static $instance = null; |
| 15 |
|
| 16 |
public static function instance() { |
| 17 |
if ( null === self::$instance ) { |
| 18 |
self::$instance = new self(); |
| 19 |
} |
| 20 |
return self::$instance; |
| 21 |
} |
| 22 |
|
| 23 |
public function init() { |
| 24 |
// v1 services that are NOT yet wrapped as Modules (Admin, Rest_Api, |
| 25 |
// Cache, Onboarding) are instantiated directly. Minifier is now |
| 26 |
// instantiated by MinifyModule::boot(); Gzip is purely static. |
| 27 |
new Admin(); |
| 28 |
new Rest_Api(); |
| 29 |
new Cache(); |
| 30 |
new Onboarding(); |
| 31 |
|
| 32 |
// Auto-heal the cache drop-in + WP_CACHE constant when state |
| 33 |
// drifts. Scoped to admin_init only: filesystem writes belong in |
| 34 |
// an authenticated admin context, never on anonymous front-end |
| 35 |
// requests. The user already opted in (cache_enabled=true) — |
| 36 |
// this is a consistency check, not a new install path. First |
| 37 |
// admin page load after a plugin upgrade restores the state; |
| 38 |
// front-end then serves from cache on the next request. |
| 39 |
add_action( 'admin_init', array( Cache::class, 'auto_heal' ) ); |
| 40 |
|
| 41 |
// Per-post cache rules (Phase 3.4) — registers postmeta with |
| 42 |
// REST + meta box on edit screens. |
| 43 |
Cache_Meta_Box::boot(); |
| 44 |
|
| 45 |
// Phase 0 architecture — managers + Free modules. v1 services |
| 46 |
// (Cache/Minifier/Gzip) are NOT yet Modules; they'll be refactored |
| 47 |
// in a follow-up PR with parity tests. |
| 48 |
Conflict_Registry::boot(); |
| 49 |
|
| 50 |
// Register Free modules via the same action xspeed-pro uses, so |
| 51 |
// the bootstrap path is symmetric across tiers. |
| 52 |
add_action( 'xspeed_register_modules', array( $this, 'register_free_modules' ) ); |
| 53 |
|
| 54 |
// Fire the registration action + boot the registry at a LATER |
| 55 |
// plugins_loaded priority so add-on plugins (xspeed-pro, in |
| 56 |
// alphabetical order so it runs at default priority 10 AFTER |
| 57 |
// us, but any add-on loaded at plugins_loaded(< 20)) have a |
| 58 |
// chance to register their `xspeed_register_modules` callback |
| 59 |
// before we fire the action. |
| 60 |
// |
| 61 |
// Bug history: previously this fired inline from init() at |
| 62 |
// priority 10. xspeed-pro's plugins_loaded(15) hook then added |
| 63 |
// its register_pro_modules callback AFTER the action had |
| 64 |
// already fired — Pro modules never appeared in the registry. |
| 65 |
// Caught by the ProStatus sentinel module's integration test. |
| 66 |
add_action( 'plugins_loaded', array( $this, 'fire_module_lifecycle' ), 20 ); |
| 67 |
} |
| 68 |
|
| 69 |
/** |
| 70 |
* Phase 2 of plugin init: fire the registration action (collecting |
| 71 |
* Free + Pro + any third-party modules hooked into |
| 72 |
* `xspeed_register_modules`) and boot the registry. |
| 73 |
* |
| 74 |
* Runs at plugins_loaded(20) so every add-on that hooks at any |
| 75 |
* priority < 20 has time to register first. |
| 76 |
*/ |
| 77 |
public function fire_module_lifecycle(): void { |
| 78 |
/** |
| 79 |
* Action: xspeed_register_modules |
| 80 |
* |
| 81 |
* Free modules register at priority 10; xspeed-pro at priority |
| 82 |
* 20; site code can hook in between to inject custom modules. |
| 83 |
* Fires exactly once per request. |
| 84 |
*/ |
| 85 |
do_action( 'xspeed_register_modules' ); |
| 86 |
|
| 87 |
Module_Registry::boot_all(); |
| 88 |
} |
| 89 |
|
| 90 |
/** |
| 91 |
* Register the Free Modules shipped in this plugin. Add new module |
| 92 |
* registrations here. Pro plugin hooks the same action separately. |
| 93 |
*/ |
| 94 |
public function register_free_modules(): void { |
| 95 |
Module_Registry::register( new \XSpeed\Modules\Cache\CacheModule() ); |
| 96 |
Module_Registry::register( new \XSpeed\Modules\Health\HealthModule() ); |
| 97 |
Module_Registry::register( new \XSpeed\Modules\Preloader\PreloaderModule() ); |
| 98 |
Module_Registry::register( new \XSpeed\Modules\Heartbeat\HeartbeatModule() ); |
| 99 |
Module_Registry::register( new \XSpeed\Modules\Minify\MinifyModule() ); |
| 100 |
Module_Registry::register( new \XSpeed\Modules\Gzip\GzipModule() ); |
| 101 |
Module_Registry::register( new \XSpeed\Modules\Lazy\LazyModule() ); |
| 102 |
Module_Registry::register( new \XSpeed\Modules\Bloat\BloatModule() ); |
| 103 |
Module_Registry::register( new \XSpeed\Modules\Database\DatabaseModule() ); |
| 104 |
Module_Registry::register( new \XSpeed\Modules\Cdn\CdnModule() ); |
| 105 |
Module_Registry::register( new \XSpeed\Modules\Cloudflare\CloudflareModule() ); |
| 106 |
Module_Registry::register( new \XSpeed\Modules\ObjectCache\ObjectCacheModule() ); |
| 107 |
Module_Registry::register( new \XSpeed\Modules\BrowserCache\BrowserCacheModule() ); |
| 108 |
Module_Registry::register( new \XSpeed\Modules\Fonts\FontsModule() ); |
| 109 |
} |
| 110 |
|
| 111 |
public static function activate() { |
| 112 |
Settings::set_defaults(); |
| 113 |
|
| 114 |
if ( ! file_exists( XSPEED_CACHE_DIR ) ) { |
| 115 |
wp_mkdir_p( XSPEED_CACHE_DIR ); |
| 116 |
} |
| 117 |
Cache::write_silence( XSPEED_CACHE_DIR ); |
| 118 |
|
| 119 |
// Drop-in installation (advanced-cache.php) and the WP_CACHE constant |
| 120 |
// edit happen only when the user explicitly enables caching from the |
| 121 |
// admin UI — never on activation. See Cache::toggle() and |
| 122 |
// Rest_Api::toggle_cache(). This is a WordPress.org review requirement. |
| 123 |
|
| 124 |
// First-run wizard: flag a one-time redirect for the activating user. |
| 125 |
// Suppressed for bulk activations / already-completed sites in |
| 126 |
// Onboarding::maybe_redirect(). |
| 127 |
Onboarding::flag_redirect(); |
| 128 |
|
| 129 |
// Propagate activation to every registered Module. Activation |
| 130 |
// happens after plugins_loaded → modules are already registered. |
| 131 |
Module_Registry::activate_all(); |
| 132 |
} |
| 133 |
|
| 134 |
public static function deactivate() { |
| 135 |
// Drop-in + WP_CACHE constant are NOT touched here. WordPress |
| 136 |
// upgrades run as deactivate → wipe files → install → activate, |
| 137 |
// so removing those artifacts on every deactivate would silently |
| 138 |
// disable caching after each plugin update. uninstall.php |
| 139 |
// handles full teardown when the user actually removes the |
| 140 |
// plugin; auto_heal() restores state on the next admin_init if |
| 141 |
// the drop-in or WP_CACHE went missing for any other reason. |
| 142 |
Cache::purge_all(); |
| 143 |
Minifier::purge_minified(); |
| 144 |
Gzip::apply( false ); |
| 145 |
|
| 146 |
Module_Registry::deactivate_all(); |
| 147 |
} |
| 148 |
} |
| 149 |
|