PluginProbe ʕ •ᴥ•ʔ
LiteSpeed Cache / 7.6.1
LiteSpeed Cache v7.6.1
trunk 1.0.15 1.9.1.1 2.9.9.2 3.6.4 4.6 5.7.0.1 6.5.4 7.0.0.1 7.0.1 7.1 7.2 7.3 7.3.0.1 7.4 7.5 7.5.0.1 7.6 7.6.1 7.6.2 7.7 7.8 7.8.0.1 7.8.1
litespeed-cache / src / admin-settings.cls.php
litespeed-cache / src Last commit date
cdn 7 months ago data_structure 7 months ago activation.cls.php 7 months ago admin-display.cls.php 7 months ago admin-settings.cls.php 7 months ago admin.cls.php 7 months ago api.cls.php 7 months ago avatar.cls.php 7 months ago base.cls.php 7 months ago cdn.cls.php 7 months ago cloud.cls.php 7 months ago conf.cls.php 7 months ago control.cls.php 7 months ago core.cls.php 7 months ago crawler-map.cls.php 7 months ago crawler.cls.php 7 months ago css.cls.php 7 months ago data.cls.php 7 months ago data.upgrade.func.php 7 months ago db-optm.cls.php 7 months ago debug2.cls.php 7 months ago doc.cls.php 7 months ago error.cls.php 7 months ago esi.cls.php 7 months ago file.cls.php 7 months ago gui.cls.php 7 months ago health.cls.php 7 months ago htaccess.cls.php 7 months ago img-optm.cls.php 7 months ago import.cls.php 7 months ago import.preset.cls.php 7 months ago lang.cls.php 7 months ago localization.cls.php 7 months ago media.cls.php 7 months ago metabox.cls.php 7 months ago object-cache-wp.cls.php 7 months ago object-cache.cls.php 7 months ago object.lib.php 7 months ago optimize.cls.php 7 months ago optimizer.cls.php 7 months ago placeholder.cls.php 7 months ago purge.cls.php 7 months ago report.cls.php 7 months ago rest.cls.php 7 months ago root.cls.php 7 months ago router.cls.php 7 months ago str.cls.php 7 months ago tag.cls.php 7 months ago task.cls.php 7 months ago tool.cls.php 7 months ago ucss.cls.php 7 months ago utility.cls.php 7 months ago vary.cls.php 7 months ago vpi.cls.php 7 months ago
admin-settings.cls.php
403 lines
1 <?php
2 /**
3 * The admin settings handler of the plugin.
4 *
5 * Handles saving and validating settings from the admin UI and network admin.
6 *
7 * @since 1.1.0
8 * @package LiteSpeed
9 */
10
11 namespace LiteSpeed;
12
13 defined( 'WPINC' ) || exit();
14
15 /**
16 * Class Admin_Settings
17 *
18 * Saves, sanitizes, and validates LiteSpeed Cache settings.
19 */
20 class Admin_Settings extends Base {
21 const LOG_TAG = '[Settings]';
22
23 const ENROLL = '_settings-enroll';
24
25 /**
26 * Save settings (single site).
27 *
28 * Accepts data from $_POST or WP-CLI.
29 * Importers may call the Conf class directly.
30 *
31 * @since 3.0
32 *
33 * @param array<string,mixed> $raw_data Raw data from request/CLI.
34 * @return void
35 */
36 public function save( $raw_data ) {
37 self::debug( 'saving' );
38
39 if ( empty( $raw_data[ self::ENROLL ] ) ) {
40 wp_die( esc_html__( 'No fields', 'litespeed-cache' ) );
41 }
42
43 $raw_data = Admin::cleanup_text( $raw_data );
44
45 // Convert data to config format.
46 $the_matrix = [];
47 foreach ( array_unique( $raw_data[ self::ENROLL ] ) as $id ) {
48 $child = false;
49
50 // Drop array format.
51 if ( false !== strpos( $id, '[' ) ) {
52 if ( 0 === strpos( $id, self::O_CDN_MAPPING ) || 0 === strpos( $id, self::O_CRAWLER_COOKIES ) ) {
53 // CDN child | Cookie Crawler settings.
54 $child = substr( $id, strpos( $id, '[' ) + 1, strpos( $id, ']' ) - strpos( $id, '[' ) - 1 );
55 // Drop ending []; Compatible with xx[0] way from CLI.
56 $id = substr( $id, 0, strpos( $id, '[' ) );
57 } else {
58 // Drop ending [].
59 $id = substr( $id, 0, strpos( $id, '[' ) );
60 }
61 }
62
63 if ( ! array_key_exists( $id, self::$_default_options ) ) {
64 continue;
65 }
66
67 // Validate $child.
68 if ( self::O_CDN_MAPPING === $id ) {
69 if ( ! in_array( $child, [ self::CDN_MAPPING_URL, self::CDN_MAPPING_INC_IMG, self::CDN_MAPPING_INC_CSS, self::CDN_MAPPING_INC_JS, self::CDN_MAPPING_FILETYPE ], true ) ) {
70 continue;
71 }
72 }
73 if ( self::O_CRAWLER_COOKIES === $id ) {
74 if ( ! in_array( $child, [ self::CRWL_COOKIE_NAME, self::CRWL_COOKIE_VALS ], true ) ) {
75 continue;
76 }
77 }
78
79 // Pull value from request.
80 if ( $child ) {
81 // []=xxx or [0]=xxx
82 $data = ! empty( $raw_data[ $id ][ $child ] ) ? $raw_data[ $id ][ $child ] : $this->type_casting(false, $id);
83 } else {
84 $data = ! empty( $raw_data[ $id ] ) ? $raw_data[ $id ] : $this->type_casting(false, $id);
85 }
86
87 // Sanitize/normalize complex fields.
88 if ( self::O_CDN_MAPPING === $id || self::O_CRAWLER_COOKIES === $id ) {
89 // Use existing queued data if available (only when $child != false).
90 $data2 = array_key_exists( $id, $the_matrix )
91 ? $the_matrix[ $id ]
92 : ( defined( 'WP_CLI' ) && WP_CLI ? $this->conf( $id ) : [] );
93 }
94
95 switch ( $id ) {
96 // Don't allow Editor/admin to be used in crawler role simulator.
97 case self::O_CRAWLER_ROLES:
98 $data = Utility::sanitize_lines( $data );
99 if ( $data ) {
100 foreach ( $data as $k => $v ) {
101 if ( user_can( $v, 'edit_posts' ) ) {
102 /* translators: %s: user id in <code> tags */
103 $msg = sprintf(
104 esc_html__( 'The user with id %s has editor access, which is not allowed for the role simulator.', 'litespeed-cache' ),
105 '<code>' . esc_html( $v ) . '</code>'
106 );
107 Admin_Display::error( $msg );
108 unset( $data[ $k ] );
109 }
110 }
111 }
112 break;
113
114 case self::O_CDN_MAPPING:
115 /**
116 * CDN setting
117 *
118 * Raw data format:
119 * cdn-mapping[url][] = 'xxx'
120 * cdn-mapping[url][2] = 'xxx2'
121 * cdn-mapping[inc_js][] = 1
122 *
123 * Final format:
124 * cdn-mapping[0][url] = 'xxx'
125 * cdn-mapping[2][url] = 'xxx2'
126 */
127 if ( $data ) {
128 foreach ( $data as $k => $v ) {
129 if ( self::CDN_MAPPING_FILETYPE === $child ) {
130 $v = Utility::sanitize_lines( $v );
131 }
132
133 if ( self::CDN_MAPPING_URL === $child ) {
134 // If not a valid URL, turn off CDN.
135 if ( 0 !== strpos( $v, 'https://' ) ) {
136 self::debug( '❌ CDN mapping set to OFF due to invalid URL' );
137 $the_matrix[ self::O_CDN ] = false;
138 }
139 $v = trailingslashit( $v );
140 }
141
142 if ( in_array( $child, [ self::CDN_MAPPING_INC_IMG, self::CDN_MAPPING_INC_CSS, self::CDN_MAPPING_INC_JS ], true ) ) {
143 // Because these can't be auto detected in `config->update()`, need to format here.
144 $v = 'false' === $v ? 0 : (bool) $v;
145 }
146
147 if ( empty( $data2[ $k ] ) ) {
148 $data2[ $k ] = [];
149 }
150
151 $data2[ $k ][ $child ] = $v;
152 }
153 }
154
155 $data = $data2;
156 break;
157
158 case self::O_CRAWLER_COOKIES:
159 /**
160 * Cookie Crawler setting
161 * Raw Format:
162 * crawler-cookies[name][] = xxx
163 * crawler-cookies[name][2] = xxx2
164 * crawler-cookies[vals][] = xxx
165 *
166 * Final format:
167 * crawler-cookie[0][name] = 'xxx'
168 * crawler-cookie[0][vals] = 'xxx'
169 * crawler-cookie[2][name] = 'xxx2'
170 *
171 * Empty line for `vals` uses literal `_null`.
172 */
173 if ( $data ) {
174 foreach ( $data as $k => $v ) {
175 if ( self::CRWL_COOKIE_VALS === $child ) {
176 $v = Utility::sanitize_lines( $v );
177 }
178
179 if ( empty( $data2[ $k ] ) ) {
180 $data2[ $k ] = [];
181 }
182
183 $data2[ $k ][ $child ] = $v;
184 }
185 }
186
187 $data = $data2;
188 break;
189
190 // Cache exclude category.
191 case self::O_CACHE_EXC_CAT:
192 $data2 = [];
193 $data = Utility::sanitize_lines( $data );
194 foreach ( $data as $v ) {
195 $cat_id = get_cat_ID( $v );
196 if ( ! $cat_id ) {
197 continue;
198 }
199 $data2[] = $cat_id;
200 }
201 $data = $data2;
202 break;
203
204 // Cache exclude tag.
205 case self::O_CACHE_EXC_TAG:
206 $data2 = [];
207 $data = Utility::sanitize_lines( $data );
208 foreach ( $data as $v ) {
209 $term = get_term_by( 'name', $v, 'post_tag' );
210 if ( ! $term ) {
211 // Could surface an admin error here if desired.
212 continue;
213 }
214 $data2[] = $term->term_id;
215 }
216 $data = $data2;
217 break;
218
219 case self::O_IMG_OPTM_SIZES_SKIPPED: // Skip image sizes
220 $image_sizes = Utility::prepare_image_sizes_array();
221 $saved_sizes = isset( $raw_data[$id] ) ? $raw_data[$id] : [];
222 $data = array_diff( $image_sizes, $saved_sizes );
223 break;
224
225 default:
226 break;
227 }
228
229 $the_matrix[ $id ] = $data;
230 }
231
232 // Special handler for CDN/Crawler 2d list to drop empty rows.
233 foreach ( $the_matrix as $id => $data ) {
234 /**
235 * Format:
236 * cdn-mapping[0][url] = 'xxx'
237 * cdn-mapping[2][url] = 'xxx2'
238 * crawler-cookie[0][name] = 'xxx'
239 * crawler-cookie[0][vals] = 'xxx'
240 * crawler-cookie[2][name] = 'xxx2'
241 */
242 if ( self::O_CDN_MAPPING === $id || self::O_CRAWLER_COOKIES === $id ) {
243 // Drop row if all children are empty.
244 foreach ( $data as $k => $v ) {
245 foreach ( $v as $v2 ) {
246 if ( $v2 ) {
247 continue 2;
248 }
249 }
250 // All empty.
251 unset( $the_matrix[ $id ][ $k ] );
252 }
253 }
254
255 // Don't allow repeated cookie names.
256 if ( self::O_CRAWLER_COOKIES === $id ) {
257 $existed = [];
258 foreach ( $the_matrix[ $id ] as $k => $v ) {
259 if ( empty( $v[ self::CRWL_COOKIE_NAME ] ) || in_array( $v[ self::CRWL_COOKIE_NAME ], $existed, true ) ) {
260 // Filter repeated or empty name.
261 unset( $the_matrix[ $id ][ $k ] );
262 continue;
263 }
264
265 $existed[] = $v[ self::CRWL_COOKIE_NAME ];
266 }
267 }
268
269 // tmp fix the 3rd part woo update hook issue when enabling vary cookie.
270 if ( 'wc_cart_vary' === $id ) {
271 if ( $data ) {
272 add_filter(
273 'litespeed_vary_cookies',
274 function ( $arr ) {
275 $arr[] = 'woocommerce_cart_hash';
276 return array_unique( $arr );
277 }
278 );
279 } else {
280 add_filter(
281 'litespeed_vary_cookies',
282 function ( $arr ) {
283 $key = array_search( 'woocommerce_cart_hash', $arr, true );
284 if ( false !== $key ) {
285 unset( $arr[ $key ] );
286 }
287 return array_unique( $arr );
288 }
289 );
290 }
291 }
292 }
293
294 // id validation will be inside.
295 $this->cls( 'Conf' )->update_confs( $the_matrix );
296
297 $msg = __( 'Options saved.', 'litespeed-cache' );
298 Admin_Display::success( $msg );
299 }
300
301 /**
302 * Parses any changes made by the network admin on the network settings.
303 *
304 * @since 3.0
305 *
306 * @param array<string,mixed> $raw_data Raw data from request/CLI.
307 * @return void
308 */
309 public function network_save( $raw_data ) {
310 self::debug( 'network saving' );
311
312 if ( empty( $raw_data[ self::ENROLL ] ) ) {
313 wp_die( esc_html__( 'No fields', 'litespeed-cache' ) );
314 }
315
316 $raw_data = Admin::cleanup_text( $raw_data );
317
318 foreach ( array_unique( $raw_data[ self::ENROLL ] ) as $id ) {
319 // Append current field to setting save.
320 if ( ! array_key_exists( $id, self::$_default_site_options ) ) {
321 continue;
322 }
323
324 $data = ! empty( $raw_data[ $id ] ) ? $raw_data[ $id ] : false;
325
326 // id validation will be inside.
327 $this->cls( 'Conf' )->network_update( $id, $data );
328 }
329
330 // Update related files.
331 Activation::cls()->update_files();
332
333 $msg = __( 'Options saved.', 'litespeed-cache' );
334 Admin_Display::success( $msg );
335 }
336
337 /**
338 * Hooked to the wp_redirect filter when saving widgets fails validation.
339 *
340 * @since 1.1.3
341 *
342 * @param string $location The redirect location.
343 * @return string Updated location string.
344 */
345 public static function widget_save_err( $location ) {
346 return str_replace( '?message=0', '?error=0', $location );
347 }
348
349 /**
350 * Validate the LiteSpeed Cache settings on widget save.
351 *
352 * @since 1.1.3
353 *
354 * @param array $instance The new settings.
355 * @param array $new_instance The raw submitted settings.
356 * @param array $old_instance The original settings.
357 * @param \WP_Widget $widget The widget instance.
358 * @return array|false Updated settings on success, false on error.
359 */
360 public static function validate_widget_save( $instance, $new_instance, $old_instance, $widget ) {
361 if ( empty( $new_instance ) ) {
362 return $instance;
363 }
364
365 if ( ! isset( $new_instance[ ESI::WIDGET_O_ESIENABLE ], $new_instance[ ESI::WIDGET_O_TTL ] ) ) {
366 return $instance;
367 }
368
369 $esi = (int) $new_instance[ ESI::WIDGET_O_ESIENABLE ] % 3;
370 $ttl = (int) $new_instance[ ESI::WIDGET_O_TTL ];
371
372 if ( 0 !== $ttl && $ttl < 30 ) {
373 add_filter( 'wp_redirect', __CLASS__ . '::widget_save_err' );
374 return false; // Invalid ttl.
375 }
376
377 if ( empty( $instance[ Conf::OPTION_NAME ] ) ) {
378 // @todo to be removed.
379 $instance[ Conf::OPTION_NAME ] = [];
380 }
381 $instance[ Conf::OPTION_NAME ][ ESI::WIDGET_O_ESIENABLE ] = $esi;
382 $instance[ Conf::OPTION_NAME ][ ESI::WIDGET_O_TTL ] = $ttl;
383
384 $current = ! empty( $old_instance[ Conf::OPTION_NAME ] ) ? $old_instance[ Conf::OPTION_NAME ] : false;
385
386 // Avoid unsanitized superglobal usage.
387 $referrer = isset( $_SERVER['HTTP_REFERER'] ) ? esc_url_raw( wp_unslash( $_SERVER['HTTP_REFERER'] ) ) : '';
388
389 // Only purge when not in the Customizer.
390 if ( false === strpos( $referrer, '/wp-admin/customize.php' ) ) {
391 if ( ! $current || $esi !== (int) $current[ ESI::WIDGET_O_ESIENABLE ] ) {
392 Purge::purge_all( 'Widget ESI_enable changed' );
393 } elseif ( 0 !== $ttl && $ttl !== (int) $current[ ESI::WIDGET_O_TTL ] ) {
394 Purge::add( Tag::TYPE_WIDGET . $widget->id );
395 }
396
397 Purge::purge_all( 'Widget saved' );
398 }
399
400 return $instance;
401 }
402 }
403