PluginProbe
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN / 1.0.7
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN v1.0.7
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 / modules / Gzip / GzipModule.php

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

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