PluginProbe
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN / 1.0.9
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN v1.0.9
1.3.3 1.3.2 1.3.1 1.3.0 1.2.4 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 1.1.8 All 29 releases
xspeed / includes / class-plugin.php

class-plugin.php in xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN 1.0.9, at includes/class-plugin.php

215 lines 9.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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 /** @var Usage_Tracker|null */
17 private $usage_tracker = null;
18
19 public static function instance() {
20 if ( null === self::$instance ) {
21 self::$instance = new self();
22 }
23 return self::$instance;
24 }
25
26 public function init() {
27 // v1 services that are NOT yet wrapped as Modules (Admin, Rest_Api,
28 // Cache, Onboarding) are instantiated directly. Minifier is now
29 // instantiated by MinifyModule::boot(); Gzip is purely static.
30 new Admin();
31 new Rest_Api();
32 new Cache();
33 new Rest_Cache();
34 new Onboarding();
35
36 // Opt-in usage analytics. Instantiating + init() only registers the
37 // cron callback; NOTHING is collected or sent until the admin opts in
38 // from the setup wizard (Onboarding wires the consent toggle to
39 // Usage_Tracker::opt_in()). See class-usage-tracker.php privacy contract.
40 $this->start_plugin_tracking();
41
42 // Auto-heal the cache drop-in + WP_CACHE constant when state
43 // drifts. Scoped to admin_init only: filesystem writes belong in
44 // an authenticated admin context, never on anonymous front-end
45 // requests. The user already opted in (cache_enabled=true) —
46 // this is a consistency check, not a new install path. First
47 // admin page load after a plugin upgrade restores the state;
48 // front-end then serves from cache on the next request.
49 add_action( 'admin_init', array( Cache::class, 'auto_heal' ) );
50
51 // Per-post cache rules (Phase 3.4) — registers postmeta with
52 // REST + meta box on edit screens.
53 Cache_Meta_Box::boot();
54
55 // Phase 0 architecture — managers + Free modules. v1 services
56 // (Cache/Minifier/Gzip) are NOT yet Modules; they'll be refactored
57 // in a follow-up PR with parity tests.
58 Conflict_Registry::boot();
59
60 // Register Free modules via the same action xspeed-pro uses, so
61 // the bootstrap path is symmetric across tiers.
62 add_action( 'xspeed_register_modules', array( $this, 'register_free_modules' ) );
63
64 // Fire the registration action + boot the registry at a LATER
65 // plugins_loaded priority so add-on plugins (xspeed-pro, in
66 // alphabetical order so it runs at default priority 10 AFTER
67 // us, but any add-on loaded at plugins_loaded(< 20)) have a
68 // chance to register their `xspeed_register_modules` callback
69 // before we fire the action.
70 //
71 // Bug history: previously this fired inline from init() at
72 // priority 10. xspeed-pro's plugins_loaded(15) hook then added
73 // its register_pro_modules callback AFTER the action had
74 // already fired — Pro modules never appeared in the registry.
75 // Caught by the ProStatus sentinel module's integration test.
76 add_action( 'plugins_loaded', array( $this, 'fire_module_lifecycle' ), 20 );
77 }
78
79 /**
80 * Phase 2 of plugin init: fire the registration action (collecting
81 * Free + Pro + any third-party modules hooked into
82 * `xspeed_register_modules`) and boot the registry.
83 *
84 * Runs at plugins_loaded(20) so every add-on that hooks at any
85 * priority < 20 has time to register first.
86 */
87 public function fire_module_lifecycle(): void {
88 /**
89 * Action: xspeed_register_modules
90 *
91 * Free modules register at priority 10; xspeed-pro at priority
92 * 20; site code can hook in between to inject custom modules.
93 * Fires exactly once per request.
94 */
95 do_action( 'xspeed_register_modules' );
96
97 Module_Registry::boot_all();
98 }
99
100 /**
101 * Register the Free Modules shipped in this plugin. Add new module
102 * registrations here. Pro plugin hooks the same action separately.
103 */
104 public function register_free_modules(): void {
105 Module_Registry::register( new \XSpeed\Modules\Cache\CacheModule() );
106 Module_Registry::register( new \XSpeed\Modules\Health\HealthModule() );
107 Module_Registry::register( new \XSpeed\Modules\Preloader\PreloaderModule() );
108 Module_Registry::register( new \XSpeed\Modules\Heartbeat\HeartbeatModule() );
109 Module_Registry::register( new \XSpeed\Modules\Minify\MinifyModule() );
110 Module_Registry::register( new \XSpeed\Modules\Gzip\GzipModule() );
111 Module_Registry::register( new \XSpeed\Modules\Lazy\LazyModule() );
112 Module_Registry::register( new \XSpeed\Modules\Bloat\BloatModule() );
113 Module_Registry::register( new \XSpeed\Modules\Database\DatabaseModule() );
114 Module_Registry::register( new \XSpeed\Modules\Cdn\CdnModule() );
115 Module_Registry::register( new \XSpeed\Modules\Cloudflare\CloudflareModule() );
116 Module_Registry::register( new \XSpeed\Modules\ObjectCache\ObjectCacheModule() );
117 Module_Registry::register( new \XSpeed\Modules\BrowserCache\BrowserCacheModule() );
118 // Advanced Cache — a Free container row that gathers the Pro
119 // cache-coverage features (404 / search / feed / REST / rules /
120 // maintenance) into one sidebar sub-item (FBS-83633).
121 Module_Registry::register( new \XSpeed\Modules\CacheCoverage\CacheCoverageModule() );
122 Module_Registry::register( new \XSpeed\Modules\Fonts\FontsModule() );
123 Module_Registry::register( new \XSpeed\Modules\ResourceHints\ResourceHintsModule() );
124 // AI Privacy (GDPR off-switch) ships in Free even though every AI
125 // *feature* is Pro — privacy is a right, not a paid tier. FEATURES.md
126 // §AI row 6 mandates it. Without this registration the module was dead
127 // code: no REST/settings surface, the promised off-switch unreachable
128 // (FBS-83633 Bug 1). It carries its own cli_commands() so it satisfies
129 // the CLI/MCP coverage guard once registered.
130 Module_Registry::register( new \XSpeed\Modules\AIPrivacy\AIPrivacyModule() );
131 // Migration moved Pro → Free: it's an acquisition/onboarding feature
132 // (detect a competing caching plugin, import its settings, switch over),
133 // so it must work without a Pro license. Agency-scale extras (profiles,
134 // bulk multisite, host presets) remain Pro.
135 Module_Registry::register( new \XSpeed\Modules\Migration\MigrationModule() );
136 // Help & Support moved Pro → Free: a ticket link + read-only system
137 // snapshot is onboarding/diagnostics, not a paid value-add, so every
138 // user gets it. The snapshot degrades gracefully without Pro (Pro
139 // version/license fields fall back to defaults via defined()/get_option).
140 Module_Registry::register( new \XSpeed\Modules\Support\SupportModule() );
141 // MCP remote control (AI assistants) — Free. The plugin serves the
142 // MCP protocol at the site's own /xspeed/mcp URL; the only gate is
143 // the per-site connection token an admin mints via Connect. No
144 // license, no hosted infra. See IMPLEMENTATION.md §17.
145 Module_Registry::register( new \XSpeed\Modules\Mcp\McpModule() );
146 // Settings host page (FBS-83633): a Free container whose tabs embed
147 // AI Provider / AI Privacy / MCP / License / Branding / Multisite.
148 // Folds the old AI + Pro Add-ons sidebar groups into one Settings
149 // destination and gives the Free AI-Privacy off-switch a stable home.
150 Module_Registry::register( new \XSpeed\Modules\Settings\SettingsModule() );
151 }
152
153 /**
154 * Boot the opt-in usage tracker. Registers the cron sender only; the send
155 * itself is consent-gated inside Usage_Tracker. The instance is held so the
156 * onboarding REST handler can flip consent via usage_tracker()->opt_in().
157 */
158 public function start_plugin_tracking(): void {
159 $this->usage_tracker = Usage_Tracker::get_instance(
160 XSPEED_FILE,
161 array(
162 'opt_in' => true,
163 'item_id' => defined( 'XSPEED_INSIGHTS_ITEM_ID' ) ? XSPEED_INSIGHTS_ITEM_ID : false,
164 )
165 );
166 $this->usage_tracker->init();
167 }
168
169 /**
170 * The shared Usage_Tracker singleton (or null if tracking wasn't booted,
171 * e.g. on the activation hook before init() runs).
172 */
173 public function usage_tracker(): ?Usage_Tracker {
174 return $this->usage_tracker;
175 }
176
177 public static function activate() {
178 Settings::set_defaults();
179
180 if ( ! file_exists( XSPEED_CACHE_DIR ) ) {
181 wp_mkdir_p( XSPEED_CACHE_DIR );
182 }
183 Cache::write_silence( XSPEED_CACHE_DIR );
184
185 // Drop-in installation (advanced-cache.php) and the WP_CACHE constant
186 // edit happen only when the user explicitly enables caching from the
187 // admin UI — never on activation. See Cache::toggle() and
188 // Rest_Api::toggle_cache(). This is a WordPress.org review requirement.
189
190 // First-run wizard: flag a one-time redirect for the activating user.
191 // Suppressed for bulk activations / already-completed sites in
192 // Onboarding::maybe_redirect().
193 Onboarding::flag_redirect();
194
195 // Propagate activation to every registered Module. Activation
196 // happens after plugins_loaded → modules are already registered.
197 Module_Registry::activate_all();
198 }
199
200 public static function deactivate() {
201 // Drop-in + WP_CACHE constant are NOT touched here. WordPress
202 // upgrades run as deactivate → wipe files → install → activate,
203 // so removing those artifacts on every deactivate would silently
204 // disable caching after each plugin update. uninstall.php
205 // handles full teardown when the user actually removes the
206 // plugin; auto_heal() restores state on the next admin_init if
207 // the drop-in or WP_CACHE went missing for any other reason.
208 Cache::purge_all();
209 Minifier::purge_minified();
210 Gzip::apply( false );
211
212 Module_Registry::deactivate_all();
213 }
214 }
215