PluginProbe
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN / 1.3.3
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN v1.3.3
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
← All changes | includes/modules/Gzip/GzipModule.php +96 -23 1.0.81.3.3 View file →
@@ -17,8 +17,9 @@
17 17 namespace XSpeed\Modules\Gzip;
18 18
19 19 defined( 'ABSPATH' ) || exit;
20 20
21 +use XSpeed\Deep_Link;
21 22 use XSpeed\Gzip as LegacyGzip;
22 23 use XSpeed\Module;
23 24 use XSpeed\Server;
24 25 use XSpeed\Settings_Manager;
@@ -30,11 +31,17 @@
30 31 public const VERSION = '1.1.0';
31 32
32 33 public function ui_metadata(): array {
33 34 return array(
34 - 'label' => 'GZIP',
35 - 'icon' => 'Layers',
36 - 'description' => 'Compress responses to reduce transfer size.',
35 + 'label' => __( 'Compression', 'xspeed' ),
36 + 'tab_label' => __( 'GZIP', 'xspeed' ), // its own tab on the Compression page
37 + 'icon' => 'Layers',
38 + 'description' => __( 'Compress responses to reduce transfer size.', 'xspeed' ),
39 + // Host panel merges GZIP (this module) + Brotli (Pro) into one
40 + // page — they are one decision with a fallback chain, not two
41 + // sidebar rows (FBS-83633). The panel renders this module's own
42 + // schema form plus a Brotli Pro section.
43 + 'custom_panel' => 'CompressionPanel',
37 44 );
38 45 }
39 46
40 47 public function settings_schema(): array {
@@ -41,15 +48,39 @@
41 48 return array(
42 49 'gzip_enabled' => array(
43 50 'type' => 'bool',
44 51 'default' => false,
45 - 'label' => 'Enable GZIP Compression',
46 - 'description' => 'On Apache / LiteSpeed we write the .htaccess rules automatically. On nginx the snippet below must be added to your server config.',
52 + 'label' => __( 'Enable GZIP Compression', 'xspeed' ),
53 + // Server-conditional. The old copy said "On nginx the snippet
54 + // below must be added to your server config" — unconditionally,
55 + // and there is no snippet below: the Compression page is a tab
56 + // strip plus this toggle, and NginxServerBlock only mounts under
57 + // the Cache panel. So Apache/LiteSpeed users read dead text
58 + // about a server they aren't on, and nginx users got a promise
59 + // the page couldn't keep. The nginx half now lives in
60 + // ui_notices(), which can link to where the snippet actually is.
61 + 'description' => self::gzip_description(),
47 62 ),
48 63 );
49 64 }
50 65
51 66 /**
67 + * Copy for the GZIP toggle, matched to the server we're actually on.
68 + *
69 + * Schema descriptions have no server-conditional rendering, so the choice
70 + * has to happen here rather than in the panel.
71 + */
72 + private static function gzip_description(): string {
73 + if ( class_exists( '\\XSpeed\\Server' ) && Server::supports_htaccess() ) {
74 + return __( 'Compress responses before sending them. We write the .htaccess rules automatically on this server.', 'xspeed' );
75 + }
76 + if ( class_exists( '\\XSpeed\\Server' ) && Server::NGINX === Server::type() ) {
77 + return __( 'Compress responses before sending them. nginx cannot be configured from WordPress, so the directives ship in the unified server-block snippet — see the notice below.', 'xspeed' );
78 + }
79 + return __( 'Compress responses before sending them. On this server the directives have to be added to your server config by hand — see the notice below.', 'xspeed' );
80 + }
81 +
82 + /**
52 83 * 1.1.0: drain gzip_enabled from the legacy xspeed_options blob into
53 84 * this module's option. Idempotent — a re-run with the legacy key
54 85 * already gone is a no-op.
55 86 */
@@ -73,8 +104,9 @@
73 104 array(
74 105 'name' => 'xspeed gzip',
75 106 'callback' => array( $this, 'cli_handler' ),
76 107 'shortdesc' => 'Show GZIP status (server type, active, mode).',
108 + 'ai_hint' => 'Is text compression (GZIP/Brotli) actually working on this site? Answers "why are my HTML/CSS/JS transfers so large" and whether the server is compressing at all. Reports the detected server, whether compression is active, and how it is applied.',
77 109 'synopsis' => array(),
78 110 ),
79 111 );
80 112 }
@@ -89,13 +121,57 @@
89 121 $opts = Settings_Manager::get( self::SLUG );
90 122 if ( empty( $opts['gzip_enabled'] ) ) {
91 123 return array();
92 124 }
93 - // Probe the live response — works regardless of server type
94 - // (Apache, nginx, anything). If gzip is actually being served,
95 - // nothing else matters: hide the notice. Mirror's BrowserCache's
125 +
126 + // Probe the live response — works regardless of server type. If gzip
127 + // is actually being served, nothing is wrong. Mirrors BrowserCache's
96 128 // probe_headers_present() pattern.
97 - if ( LegacyGzip::probe_active() ) {
129 + //
130 + // Tri-state: true = proven serving, false = proven not serving,
131 + // null = the loopback never reached the origin, which is not evidence
132 + // of anything. Telling someone their server is misconfigured because
133 + // *we* couldn't call it is the bug behind issue #18. (#18)
134 + $serving = LegacyGzip::probe_active();
135 +
136 + // nginx gets a notice EITHER WAY, because on nginx this notice is the
137 + // only route from the Compression page to the snippet — the panel body
138 + // is a tab strip plus one toggle, and NginxServerBlock mounts under
139 + // the Cache panel alone. Suppressing it on a correctly-configured box
140 + // left that user with no way to reach the directives at all (say, to
141 + // re-paste them after an nginx upgrade). Working sites get a calm
142 + // "here's where it lives"; broken ones get the warning. (#87)
143 + //
144 + // An unreachable probe (null) takes the calm wording too: we cannot
145 + // prove gzip is missing, so the notice points at the snippet without
146 + // claiming the server is misconfigured. Only a proven-false probe
147 + // warns. (#18)
148 + if ( Server::NGINX === Server::type() ) {
149 + $proven_missing = ( false === $serving );
150 + return array(
151 + array(
152 + 'tone' => $proven_missing ? 'warn' : 'info',
153 + 'title' => $proven_missing
154 + ? __( 'GZIP requires server config on nginx', 'xspeed' )
155 + : __( 'GZIP is being served by nginx', 'xspeed' ),
156 + 'body' => $proven_missing
157 + ? __( 'xSpeed can only auto-configure GZIP on Apache and LiteSpeed (via .htaccess). The directives are included in the unified server-block snippet on the Cache panel — copy + paste it once into your nginx vhost (or container nginx config) and reload nginx.', 'xspeed' )
158 + : __( 'Responses are compressed. xSpeed cannot configure nginx from WordPress, so these directives live in the unified server-block snippet on the Cache panel — that is where to re-copy them if your server config is ever rebuilt.', 'xspeed' ),
159 + // Lands on the snippet itself and auto-expands it, rather
160 + // than on the Cache page where it is one collapsed section
161 + // among several. Same call BrowserCacheModule makes.
162 + 'action' => Deep_Link::action(
163 + __( 'Go to the snippet', 'xspeed' ),
164 + 'cache',
165 + 'nginx_snippet'
166 + ),
167 + ),
168 + );
169 + }
170 +
171 + // Non-nginx: stay quiet unless the probe positively proved gzip is
172 + // missing. `null` (unreachable) must not produce a notice. (#18)
173 + if ( false !== $serving ) {
98 174 return array();
99 175 }
100 176 // Probe says NOT active and gzip is enabled — surface the right
101 177 // notice per server topology.
@@ -111,22 +187,11 @@
111 187 'body' => __( 'xSpeed wrote the .htaccess rules but the response still isn\'t gzipped. Your server may have AllowOverride disabled, another caching plugin overriding, or mod_deflate missing. Ask your host to enable GZIP on Apache.', 'xspeed' ),
112 188 ),
113 189 );
114 190 }
115 - // Manual server (nginx, IIS, unknown) — probe failed because
116 - // the user hasn't pasted the snippet (or hasn't reloaded nginx).
117 - $type = Server::type();
118 - if ( 'nginx' === $type ) {
119 - return array(
120 - array(
121 - 'tone' => 'warn',
122 - 'title' => __( 'GZIP requires server config on nginx', 'xspeed' ),
123 - 'body' => __( 'xSpeed can only auto-configure GZIP on Apache and LiteSpeed (via .htaccess). The directives are included in the unified server-block snippet on the Cache panel — copy + paste it once into your nginx vhost (or container nginx config) and reload nginx.', 'xspeed' ),
124 - ),
125 - );
126 - }
127 191 // IIS / unknown server fallback — keep the legacy "paste this"
128 - // notice until a non-nginx unified-snippet surface ships.
192 + // notice until a non-nginx unified-snippet surface ships. (nginx
193 + // returned above, whether or not gzip is currently being served.)
129 194 return array(
130 195 array(
131 196 'tone' => 'warn',
132 197 'title' => __( 'GZIP requires server config on this server', 'xspeed' ),
@@ -203,9 +268,10 @@
203 268 $opts = Settings_Manager::get( self::SLUG );
204 269 \WP_CLI::log( 'enabled ' . ( $opts['gzip_enabled'] ? 'true' : 'false' ) );
205 270 \WP_CLI::log( 'server ' . Server::type() );
206 271 \WP_CLI::log( 'mode ' . Server::gzip_mode() );
207 - \WP_CLI::log( 'active ' . ( LegacyGzip::probe_active() ? 'true' : 'false' ) );
272 + $active = LegacyGzip::probe_active();
273 + \WP_CLI::log( 'active ' . ( null === $active ? 'unknown (loopback probe failed)' : ( $active ? 'true' : 'false' ) ) );
208 274 \WP_CLI::log( 'nginx_snippet ' . LegacyGzip::nginx_snippet() );
209 275 }
210 276
211 277 /**
@@ -218,6 +284,13 @@
218 284 return null;
219 285 }
220 286 $snippet = LegacyGzip::nginx_snippet();
221 287 return is_string( $snippet ) && '' !== $snippet ? $snippet : null;
288 + }
289 +
290 + /**
291 + * Gzip stores its switch as `gzip_enabled`, not `enabled`. (#363)
292 + */
293 + public function is_active(): ?bool {
294 + return $this->any_bool_flag_on();
222 295 }
223 296 }