PluginProbe
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN / 1.0.4
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN v1.0.4
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 1.2.0 All 28 releases
xspeed / includes / class-module.php

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

275 lines 8.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Module abstract base class.
4 *
5 * Every feature in xSpeed (Free or Pro) extends this class. The contract is
6 * documented in IMPLEMENTATION.md §1.1. A Module is a self-contained unit
7 * that declares its tier, settings schema, REST routes, UI panels, CLI
8 * commands, conflicts, and lifecycle hooks in one place — so moving a
9 * feature between Free and Pro is a `git mv` + flipping the TIER constant,
10 * with no call-site changes.
11 *
12 * Concrete modules MUST:
13 * - Set the SLUG class constant.
14 * - Set the TIER class constant (TIER_FREE or TIER_PRO).
15 * - Set the VERSION class constant.
16 *
17 * @package XSpeed
18 */
19
20 namespace XSpeed;
21
22 defined( 'ABSPATH' ) || exit;
23
24 abstract class Module {
25
26 public const TIER_FREE = 'free';
27 public const TIER_PRO = 'pro';
28
29 /**
30 * Concrete modules override these three constants.
31 */
32 public const SLUG = '';
33 public const TIER = self::TIER_FREE;
34 public const VERSION = '1.0.0';
35
36 /**
37 * Other module slugs this module needs at boot. Resolved by
38 * Module_Registry via topological sort; missing deps fail loudly.
39 *
40 * @return string[]
41 */
42 public function dependencies(): array {
43 return array();
44 }
45
46 /**
47 * Typed settings schema. See Settings_Manager::validate() for the
48 * supported `type` values (bool, int, string, enum, list, url). Each
49 * field declares `default` and optional `min` / `max` / `options` /
50 * `item_type`. Storage key is `xspeed_module_<slug>`.
51 *
52 * @return array<string,array>
53 */
54 public function settings_schema(): array {
55 return array();
56 }
57
58 /**
59 * Schema migrations keyed by target version. Each value is a callable
60 * that receives the stored options array and returns the migrated
61 * array. Migrations run in version order on first load after upgrade.
62 *
63 * @return array<string,callable>
64 */
65 public function migrations(): array {
66 return array();
67 }
68
69 /**
70 * REST routes the module owns. Paths are prefixed with
71 * `/xspeed/v1/<slug>/` by Rest_Manager; declare without the prefix.
72 * `permission_callback` is wrapped automatically with a final cap
73 * check + tier gate, so modules don't need to repeat that boilerplate
74 * — but they MUST still declare a sensible callback.
75 *
76 * Default impl returns the standard GET + POST pair for modules that
77 * declare a settings_schema. Modules that need extra endpoints can
78 * extend the array. Modules with truly custom REST should override
79 * entirely and skip parent::rest_routes().
80 *
81 * Per SETTINGS.md §5.1 every module's settings live at:
82 * GET /xspeed/v1/<slug>/ → current settings
83 * POST /xspeed/v1/<slug>/ → partial patch, returns updated settings
84 *
85 * @return array[]
86 */
87 public function rest_routes(): array {
88 if ( empty( $this->settings_schema() ) ) {
89 return array();
90 }
91 return array(
92 array(
93 'path' => '/',
94 'methods' => 'GET',
95 'callback' => array( $this, 'rest_get_settings' ),
96 ),
97 array(
98 'path' => '/',
99 'methods' => 'POST',
100 'callback' => array( $this, 'rest_update_settings' ),
101 'feature' => static::SLUG,
102 ),
103 );
104 }
105
106 /**
107 * Default GET handler — returns all settings (defaults + stored)
108 * coerced against the schema. Modules can override but rarely need
109 * to.
110 */
111 public function rest_get_settings( \WP_REST_Request $request ) {
112 return rest_ensure_response( $this->get_settings() );
113 }
114
115 /**
116 * Default POST handler — validates the JSON body against the
117 * schema, persists, returns the post-update settings. Unknown keys
118 * are stripped by Settings_Manager.
119 */
120 public function rest_update_settings( \WP_REST_Request $request ) {
121 $params = $request->get_json_params();
122 if ( ! is_array( $params ) ) {
123 $params = $request->get_params();
124 }
125 return rest_ensure_response( $this->update_settings( $params ) );
126 }
127
128 /**
129 * UI panel declarations consumed by the React dashboard via the
130 * bootstrap payload. Each entry: [
131 * 'section' => 'cache' | 'performance' | 'images' | ...,
132 * 'position' => int,
133 * 'component' => 'HealthCard' | 'TogglesList' | 'StatGrid' | 'Custom',
134 * 'props' => array,
135 * ]
136 *
137 * @return array[]
138 */
139 public function ui_panels(): array {
140 return array();
141 }
142
143 /**
144 * Sidebar / dashboard metadata. The React app uses these to render the
145 * module's nav entry. Override per module to set a friendly label and
146 * a lucide-react icon name (must be in the renderer's icon whitelist —
147 * see src/components/IconResolver.tsx).
148 *
149 * @return array{label:string,icon:string,description?:string,hidden?:bool}
150 */
151 public function ui_metadata(): array {
152 return array(
153 'label' => ucfirst( str_replace( '_', ' ', static::SLUG ) ),
154 'icon' => 'Square',
155 );
156 }
157
158 /**
159 * Dynamic in-panel notices (callouts) rendered above the schema form.
160 * Computed fresh on every dashboard load. Examples: nginx GZIP
161 * snippet when the server can't be auto-configured, "drop-in
162 * missing" warning when cache_enabled but no advanced-cache.php.
163 *
164 * Each entry: [
165 * 'tone' => 'info' | 'warn' | 'danger' | 'success',
166 * 'title' => 'Short heading.',
167 * 'body' => 'One- or two-sentence explanation.',
168 * 'snippet' => 'Optional verbatim code snippet rendered in a
169 * <pre> with a Copy button.',
170 * ]
171 *
172 * @return array[]
173 */
174 public function ui_notices(): array {
175 return array();
176 }
177
178 /**
179 * WP-CLI command definitions. Each entry: [
180 * 'name' => 'xspeed cache purge',
181 * 'callback' => callable,
182 * 'synopsis' => array, // wp-cli synopsis spec
183 * ]
184 *
185 * @return array[]
186 */
187 public function cli_commands(): array {
188 return array();
189 }
190
191 /**
192 * Nginx directives this module contributes to the unified server-block
193 * snippet rendered by Cache::full_nginx_server_block(). Returning a
194 * non-null string opts the module into the consolidated "paste this
195 * once into your nginx vhost" UX on the Cache panel.
196 *
197 * The returned string should be the bare directives only — no `server
198 * { }` wrapper, no comment header (the aggregator adds one). Empty
199 * string and null are both treated as "no contribution this render".
200 *
201 * Return null (default) when the module is disabled, its current
202 * settings make the directives a no-op, or the module doesn't have
203 * nginx-side directives at all.
204 */
205 public function nginx_directives(): ?string {
206 return null;
207 }
208
209 /**
210 * Conflict declarations for this module — which other plugins clash
211 * with which sub-feature. Each entry: [
212 * 'plugin' => 'wp-rocket/wp-rocket.php',
213 * 'feature' => 'page_cache',
214 * 'strategy' => 'refuse' | 'warn' | 'allow',
215 * 'reason' => 'human-readable why',
216 * ]
217 *
218 * @return array[]
219 */
220 public function conflicts(): array {
221 return array();
222 }
223
224 /**
225 * Register WP hooks. Called by Module_Registry::boot_all() after
226 * dependencies are resolved. Modules should NOT register hooks in
227 * their constructors — only in boot() — so the registry can control
228 * load order.
229 */
230 public function boot(): void {}
231
232 /**
233 * One-time setup at plugin activation. Idempotent. Examples: create
234 * a custom table, write a silence guard, register a cron schedule.
235 */
236 public function activate(): void {}
237
238 /**
239 * Tear down at plugin deactivation. Reversible counterpart to
240 * activate(). MUST leave the site in a clean state — no orphaned
241 * cron jobs, no leftover drop-ins.
242 */
243 public function deactivate(): void {}
244
245 /**
246 * Convenience accessors. Modules read/write their own settings
247 * through these so the storage detail (one option per module under
248 * `xspeed_module_<slug>`) stays encapsulated.
249 */
250 final public function get_setting( string $key, $default = null ) {
251 $opts = Settings_Manager::get( static::SLUG );
252 return array_key_exists( $key, $opts ) ? $opts[ $key ] : $default;
253 }
254
255 final public function get_settings(): array {
256 return Settings_Manager::get( static::SLUG );
257 }
258
259 final public function update_settings( array $input ): array {
260 return Settings_Manager::update( static::SLUG, $input );
261 }
262
263 final public function slug(): string {
264 return static::SLUG;
265 }
266
267 final public function tier(): string {
268 return static::TIER;
269 }
270
271 final public function version(): string {
272 return static::VERSION;
273 }
274 }
275