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.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 / Gzip / GzipModule.php

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

222 lines 7.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * GZIP module.
4 *
5 * Owns the gzip_enabled setting. On flip → writes / removes Apache /
6 * LiteSpeed `.htaccess` rules via the static XSpeed\Gzip helper. On
7 * nginx (or any server xSpeed can't auto-configure), the panel shows
8 * the manual snippet via ui_notices().
9 *
10 * Tier: Free. See SETTINGS.md for the contract this module satisfies.
11 *
12 * @package XSpeed
13 */
14
15 declare(strict_types=1);
16
17 namespace XSpeed\Modules\Gzip;
18
19 use XSpeed\Gzip as LegacyGzip;
20 use XSpeed\Module;
21 use XSpeed\Server;
22 use XSpeed\Settings_Manager;
23
24 final class GzipModule extends Module {
25
26 public const SLUG = 'gzip';
27 public const TIER = self::TIER_FREE;
28 public const VERSION = '1.1.0';
29
30 public function ui_metadata(): array {
31 return array(
32 'label' => 'GZIP',
33 'icon' => 'Layers',
34 'description' => 'Compress responses to reduce transfer size.',
35 );
36 }
37
38 public function settings_schema(): array {
39 return array(
40 'gzip_enabled' => array(
41 'type' => 'bool',
42 'default' => false,
43 'label' => 'Enable GZIP Compression',
44 'description' => 'On Apache / LiteSpeed we write the .htaccess rules automatically. On nginx the snippet below must be added to your server config.',
45 ),
46 );
47 }
48
49 /**
50 * 1.1.0: drain gzip_enabled from the legacy xspeed_options blob into
51 * this module's option. Idempotent — a re-run with the legacy key
52 * already gone is a no-op.
53 */
54 public function migrations(): array {
55 return array(
56 '1.1.0' => static function ( array $opts ): array {
57 $legacy = get_option( 'xspeed_options', array() );
58 if ( ! is_array( $legacy ) || ! array_key_exists( 'gzip_enabled', $legacy ) ) {
59 return $opts;
60 }
61 $opts['gzip_enabled'] = (bool) $legacy['gzip_enabled'];
62 unset( $legacy['gzip_enabled'] );
63 update_option( 'xspeed_options', $legacy );
64 return $opts;
65 },
66 );
67 }
68
69 public function cli_commands(): array {
70 return array(
71 array(
72 'name' => 'xspeed gzip',
73 'callback' => array( $this, 'cli_handler' ),
74 'shortdesc' => 'Show GZIP status (server type, active, mode).',
75 'synopsis' => array(),
76 ),
77 );
78 }
79
80 /**
81 * Conditional notice published to the module's panel via
82 * Module::ui_notices(). When gzip is enabled and the server can't
83 * auto-configure (nginx / unknown / IIS), we surface a copy-able
84 * snippet so the user can wire it up.
85 */
86 public function ui_notices(): array {
87 $opts = Settings_Manager::get( self::SLUG );
88 if ( empty( $opts['gzip_enabled'] ) ) {
89 return array();
90 }
91 // Probe the live response — works regardless of server type
92 // (Apache, nginx, anything). If gzip is actually being served,
93 // nothing else matters: hide the notice. Mirror's BrowserCache's
94 // probe_headers_present() pattern.
95 if ( LegacyGzip::probe_active() ) {
96 return array();
97 }
98 // Probe says NOT active and gzip is enabled — surface the right
99 // notice per server topology.
100 if ( 'auto' === Server::gzip_mode() ) {
101 // Apache/LiteSpeed but probe failed — .htaccess write must
102 // have been blocked, or another plugin is overriding. Tell
103 // the user something is wrong, no snippet (we can't fix it
104 // without their server access).
105 return array(
106 array(
107 'tone' => 'warn',
108 'title' => __( 'GZIP enabled but not active on the server', 'xspeed' ),
109 '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' ),
110 ),
111 );
112 }
113 // Manual server (nginx, IIS, unknown) — probe failed because
114 // the user hasn't pasted the snippet (or hasn't reloaded nginx).
115 $type = Server::type();
116 if ( 'nginx' === $type ) {
117 return array(
118 array(
119 'tone' => 'warn',
120 'title' => __( 'GZIP requires server config on nginx', 'xspeed' ),
121 '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' ),
122 ),
123 );
124 }
125 // IIS / unknown server fallback — keep the legacy "paste this"
126 // notice until a non-nginx unified-snippet surface ships.
127 return array(
128 array(
129 'tone' => 'warn',
130 'title' => __( 'GZIP requires server config on this server', 'xspeed' ),
131 'body' => __( 'xSpeed can only auto-configure GZIP on Apache and LiteSpeed (via .htaccess). Paste the snippet below into your server config and reload.', 'xspeed' ),
132 'snippet' => LegacyGzip::nginx_snippet(),
133 ),
134 );
135 }
136
137 /**
138 * Module booting: seed per-module option from legacy if needed,
139 * then register the change hook that flips Apache / LiteSpeed
140 * .htaccess rules whenever gzip_enabled is toggled. This handler
141 * fires on writes to xspeed_module_gzip (not the legacy blob).
142 */
143 public function boot(): void {
144 $this->seed_from_legacy_if_needed();
145 add_action( 'update_option_xspeed_module_gzip', array( __CLASS__, 'on_settings_change' ), 10, 2 );
146 add_action( 'add_option_xspeed_module_gzip', array( __CLASS__, 'on_settings_added' ), 10, 2 );
147 }
148
149 /**
150 * Detect the gzip_enabled flip on write and apply / remove the
151 * .htaccess rules. Idempotent — re-running with the same state is a
152 * no-op inside LegacyGzip::apply().
153 */
154 public static function on_settings_change( $old, $new ): void {
155 $old_gzip = is_array( $old ) ? ! empty( $old['gzip_enabled'] ) : false;
156 $new_gzip = is_array( $new ) ? ! empty( $new['gzip_enabled'] ) : false;
157 if ( $old_gzip !== $new_gzip ) {
158 LegacyGzip::apply( $new_gzip );
159 // Settings just changed — current "probe says active" answer
160 // is stale. Drop the transient so the next dashboard load
161 // re-checks the live response.
162 delete_transient( 'xspeed_gzip_active' );
163 }
164 }
165
166 /**
167 * The very first write (option doesn't exist yet) hits add_option
168 * instead of update_option. Treat it as old=false → new=current.
169 */
170 public static function on_settings_added( $name, $value ): void {
171 $enabled = is_array( $value ) ? ! empty( $value['gzip_enabled'] ) : false;
172 if ( $enabled ) {
173 LegacyGzip::apply( true );
174 }
175 }
176
177 public function activate(): void {
178 $this->seed_from_legacy_if_needed();
179 }
180
181 private function seed_from_legacy_if_needed(): void {
182 if ( null !== get_option( 'xspeed_module_gzip', null ) ) {
183 return;
184 }
185 $legacy = get_option( 'xspeed_options', array() );
186 if ( ! is_array( $legacy ) || ! array_key_exists( 'gzip_enabled', $legacy ) ) {
187 return;
188 }
189 update_option(
190 'xspeed_module_gzip',
191 array(
192 '_version' => self::VERSION,
193 'gzip_enabled' => (bool) $legacy['gzip_enabled'],
194 )
195 );
196 unset( $legacy['gzip_enabled'] );
197 update_option( 'xspeed_options', $legacy );
198 }
199
200 public function cli_handler( array $args, array $assoc ): void {
201 $opts = Settings_Manager::get( self::SLUG );
202 \WP_CLI::log( 'enabled ' . ( $opts['gzip_enabled'] ? 'true' : 'false' ) );
203 \WP_CLI::log( 'server ' . Server::type() );
204 \WP_CLI::log( 'mode ' . Server::gzip_mode() );
205 \WP_CLI::log( 'active ' . ( LegacyGzip::probe_active() ? 'true' : 'false' ) );
206 \WP_CLI::log( 'nginx_snippet ' . LegacyGzip::nginx_snippet() );
207 }
208
209 /**
210 * GZIP directives for the unified nginx server-block snippet. Null
211 * when the module is disabled.
212 */
213 public function nginx_directives(): ?string {
214 $opts = Settings_Manager::get( self::SLUG );
215 if ( empty( $opts['gzip_enabled'] ) ) {
216 return null;
217 }
218 $snippet = LegacyGzip::nginx_snippet();
219 return is_string( $snippet ) && '' !== $snippet ? $snippet : null;
220 }
221 }
222