PluginProbe
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN / 1.0.5
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN v1.0.5
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 / modules / Minify / MinifyModule.php

MinifyModule.php in xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN 1.0.5, at includes/modules/Minify/MinifyModule.php

238 lines 8.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Minify module.
4 *
5 * Owns the minify_html / minify_css / minify_js settings. Engine work
6 * still happens in XSpeed\Minifier (filters style_loader_src and
7 * script_loader_src), but the Module is now the storage and editing
8 * authority. Settings live in xspeed_module_minify; the legacy
9 * xspeed_options blob is drained on first boot and on POST.
10 *
11 * Tier: Free. See SETTINGS.md for the contract this module satisfies.
12 *
13 * @package XSpeed
14 */
15
16 declare(strict_types=1);
17
18 namespace XSpeed\Modules\Minify;
19
20 defined( 'ABSPATH' ) || exit;
21
22 use XSpeed\Minifier as LegacyMinifier;
23 use XSpeed\Module;
24 use XSpeed\Settings_Manager;
25
26 final class MinifyModule extends Module {
27
28 public const SLUG = 'minify';
29 public const TIER = self::TIER_FREE;
30 public const VERSION = '1.2.0';
31
32 public function ui_metadata(): array {
33 return array(
34 'label' => 'Minify',
35 'icon' => 'Wand2',
36 'description' => 'Strip whitespace and rewrite enqueued CSS / JS.',
37 );
38 }
39
40 public function settings_schema(): array {
41 return array(
42 'minify_html' => array(
43 'type' => 'bool',
44 'default' => false,
45 'label' => 'Minify HTML',
46 'description' => 'Strip whitespace and comments from HTML output. Safe on most themes.',
47 ),
48 'minify_css' => array(
49 'type' => 'bool',
50 'default' => false,
51 'label' => 'Minify CSS',
52 'description' => 'Compress and rewrite enqueued local stylesheets. External CSS is left untouched.',
53 ),
54 'minify_js' => array(
55 'type' => 'bool',
56 'default' => false,
57 'label' => 'Minify JavaScript',
58 'description' => 'Compress enqueued local scripts. Disable if you hit script-loading conflicts on the frontend.',
59 ),
60 'defer_js' => array(
61 'type' => 'bool',
62 'default' => false,
63 'label' => 'Defer JavaScript',
64 'description' => 'Add defer="defer" to enqueued script tags so they execute after HTML parsing. jQuery + its hard dependencies are skipped automatically.',
65 ),
66 'delay_js' => array(
67 'type' => 'bool',
68 'default' => false,
69 'label' => 'Delay JavaScript Until Interaction',
70 'description' => 'Postpone script loading until the visitor scrolls, moves the mouse, taps, or presses a key. Drastically improves first paint on script-heavy pages; can break above-the-fold scripted UI — test before leaving on.',
71 ),
72 'async_css' => array(
73 'type' => 'bool',
74 'default' => false,
75 'label' => 'Load CSS Asynchronously',
76 'description' => 'Rewrite stylesheet link tags to load non-blocking via the print-then-all pattern. Pairs well with critical-CSS workflows; can cause a flash of unstyled content if the theme has no critical CSS.',
77 ),
78 'remove_query_strings' => array(
79 'type' => 'bool',
80 'default' => false,
81 'label' => 'Remove Asset Query Strings',
82 'description' => 'Strip ?ver=X.Y from enqueued CSS / JS URLs. Some CDN caches and proxies cache better when query strings are absent.',
83 ),
84 'defer_js_excluded' => array(
85 'type' => 'list',
86 'default' => array( 'jquery-core', 'jquery-migrate' ),
87 'item_type' => 'string',
88 'label' => 'Defer / Delay Exclusions',
89 'description' => 'Script handles OR URL substrings that skip defer + delay. Defaults exclude jQuery (most themes depend on it being available synchronously). One per line.',
90 ),
91 'combine_css' => array(
92 'type' => 'bool',
93 'default' => false,
94 'label' => 'Combine CSS Files',
95 'description' => 'Concatenate enqueued local stylesheets into a single file (with @import and url(…) paths resolved). External CSS is left alone. Pairs poorly with HTTP/2 push — only enable on HTTP/1.1 hosts.',
96 ),
97 'combine_js' => array(
98 'type' => 'bool',
99 'default' => false,
100 'label' => 'Combine JavaScript Files',
101 'description' => 'Concatenate enqueued local scripts into a single file. External scripts + scripts marked async / deferred are left alone. Disable if you hit dependency-order issues; the combiner respects WordPress enqueue order but inline scripts attached via wp_add_inline_script can shift behavior.',
102 ),
103 );
104 }
105
106 /**
107 * 1.1.0: drain minify_html / minify_css / minify_js from the legacy
108 * `xspeed_options` blob into this module's per-module option, then
109 * delete the keys from the legacy blob so duplicate sources can't
110 * re-appear. Idempotent — re-running is a no-op once the keys are
111 * gone from xspeed_options.
112 */
113 public function migrations(): array {
114 return array(
115 '1.1.0' => static function ( array $opts ): array {
116 $legacy = get_option( 'xspeed_options', array() );
117 if ( ! is_array( $legacy ) ) {
118 return $opts;
119 }
120 $dirty = false;
121 foreach ( array( 'minify_html', 'minify_css', 'minify_js' ) as $key ) {
122 if ( array_key_exists( $key, $legacy ) ) {
123 $opts[ $key ] = (bool) $legacy[ $key ];
124 unset( $legacy[ $key ] );
125 $dirty = true;
126 }
127 }
128 if ( $dirty ) {
129 update_option( 'xspeed_options', $legacy );
130 }
131 return $opts;
132 },
133 );
134 }
135
136 /**
137 * Conflict declarations. Detected automatically by Conflict_Registry,
138 * but listing them here keeps the module self-documenting.
139 */
140 public function conflicts(): array {
141 return array(
142 array(
143 'plugin' => 'autoptimize/autoptimize.php',
144 'feature' => 'minify.html',
145 'strategy' => \XSpeed\Conflict_Registry::STRATEGY_REFUSE,
146 'reason' => 'Autoptimize is active and handles minification.',
147 ),
148 array(
149 'plugin' => 'wp-rocket/wp-rocket.php',
150 'feature' => 'minify.html',
151 'strategy' => \XSpeed\Conflict_Registry::STRATEGY_REFUSE,
152 'reason' => 'WP Rocket already handles minification.',
153 ),
154 );
155 }
156
157 public function cli_commands(): array {
158 return array(
159 array(
160 'name' => 'xspeed minify',
161 'callback' => array( $this, 'cli_handler' ),
162 'shortdesc' => 'Inspect or purge xSpeed minify cache.',
163 'synopsis' => array(
164 array(
165 'type' => 'positional',
166 'name' => 'action',
167 'options' => array( 'status', 'purge' ),
168 'optional' => false,
169 ),
170 ),
171 ),
172 );
173 }
174
175 /**
176 * On boot:
177 * 1. Seed our per-module option from the legacy blob if neither
178 * our option nor the migration has run yet (covers the
179 * already-installed-before-this-module-shipped path).
180 * 2. Instantiate the v1 Minifier engine; it now reads from
181 * Settings_Manager::get('minify') via its updated read path.
182 */
183 public function boot(): void {
184 $this->seed_from_legacy_if_needed();
185 new LegacyMinifier();
186 }
187
188 public function activate(): void {
189 // Plugin activation hits all modules. Same seed logic — safe to
190 // run more than once.
191 $this->seed_from_legacy_if_needed();
192 }
193
194 private function seed_from_legacy_if_needed(): void {
195 $existing = get_option( 'xspeed_module_minify', null );
196 if ( null !== $existing ) {
197 return;
198 }
199 $legacy = get_option( 'xspeed_options', array() );
200 if ( ! is_array( $legacy ) ) {
201 return;
202 }
203 $seed = array( '_version' => self::VERSION );
204 $dirty = false;
205 foreach ( array( 'minify_html', 'minify_css', 'minify_js' ) as $key ) {
206 if ( array_key_exists( $key, $legacy ) ) {
207 $seed[ $key ] = (bool) $legacy[ $key ];
208 unset( $legacy[ $key ] );
209 $dirty = true;
210 }
211 }
212 if ( $dirty ) {
213 update_option( 'xspeed_module_minify', $seed );
214 update_option( 'xspeed_options', $legacy );
215 }
216 }
217
218 public function cli_handler( array $args, array $assoc ): void {
219 $action = $args[0] ?? 'status';
220
221 if ( 'status' === $action ) {
222 $opts = Settings_Manager::get( self::SLUG );
223 \WP_CLI::log( sprintf( 'minify_html: %s', $opts['minify_html'] ? 'on' : 'off' ) );
224 \WP_CLI::log( sprintf( 'minify_css : %s', $opts['minify_css'] ? 'on' : 'off' ) );
225 \WP_CLI::log( sprintf( 'minify_js : %s', $opts['minify_js'] ? 'on' : 'off' ) );
226 return;
227 }
228
229 if ( 'purge' === $action ) {
230 LegacyMinifier::purge_minified();
231 \WP_CLI::success( 'Minify cache purged.' );
232 return;
233 }
234
235 \WP_CLI::error( "Unknown action: $action" );
236 }
237 }
238