PluginProbe
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF / 2.3.3
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF v2.3.3
2.3.4 2.3.3 2.3.2 2.3.1 2.3.0 2.2.9 2.2.8 trunk 1.10 1.3.3 1.3.4 1.3.5 1.3.5.1 1.3.5.2 1.3.6 1.3.6.1 1.4 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.5 All 103 releases
imagify / inc / classes / class-imagify-abstract-options.php

class-imagify-abstract-options.php in Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF 2.3.3, at inc/classes/class-imagify-abstract-options.php

599 lines 13.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Abstract class to handle a part of the plugin options.
5 *
6 * @since 1.7
7 */
8 abstract class Imagify_Abstract_Options {
9 /**
10 * Class version.
11 *
12 * @var string
13 * @since 1.7
14 */
15 const VERSION = '1.0';
16
17 /**
18 * Suffix used in the name of the option.
19 *
20 * @var string
21 * @since 1.7
22 * @access protected
23 */
24 protected $identifier;
25
26 /**
27 * The default values for the Imagify main options.
28 * These are the "zero state" values.
29 * Don't use null as value.
30 *
31 * @var array
32 * @since 1.7
33 * @access protected
34 */
35 protected $default_values;
36
37 /**
38 * The Imagify main option values used when they are set the first time or reset.
39 * Values identical to default values are not listed.
40 *
41 * @var array
42 * @since 1.7
43 * @access protected
44 */
45 protected $reset_values = [];
46
47 /**
48 * Tell if the option should be autoloaded by WP.
49 * Possible values are 'yes' and 'no'.
50 *
51 * @var string
52 * @since 1.7
53 * @access protected
54 */
55 protected $autoload = 'yes';
56
57 /**
58 * Tell if the option should be a network option.
59 *
60 * @var bool
61 * @since 1.7
62 * @access protected
63 */
64 protected $network_option = false;
65
66 /**
67 * Identifier used in the hook names.
68 *
69 * @var string
70 * @since 1.7
71 * @access private
72 */
73 private $hook_identifier;
74
75 /**
76 * The constructor.
77 *
78 * @since 1.7
79 * @author Grégory Viguier
80 * @access protected
81 */
82 protected function __construct() {
83 $this->hook_identifier = rtrim( strtolower( str_replace( 'Imagify_', '', get_class( $this ) ) ), 's' );
84
85 if ( ! is_string( $this->autoload ) ) {
86 $this->autoload = $this->autoload ? 'yes' : 'no';
87 }
88
89 $this->default_values = array_merge(
90 [
91 'version' => '',
92 ],
93 $this->default_values
94 );
95 }
96
97 /**
98 * Launch the hooks.
99 *
100 * @since 1.7
101 * @author Grégory Viguier
102 * @access public
103 */
104 public function init() {
105 add_filter( 'sanitize_option_' . $this->get_option_name(), [ $this, 'sanitize_and_validate_on_update' ], 50 );
106 }
107
108
109 /** ----------------------------------------------------------------------------------------- */
110 /** GET/SET/DELETE OPTION(S) ================================================================ */
111 /** ----------------------------------------------------------------------------------------- */
112
113 /**
114 * Get an Imagify option.
115 *
116 * @since 1.7
117 * @author Grégory Viguier
118 * @access public
119 *
120 * @param string $key The option name.
121 * @return mixed The option value.
122 */
123 public function get( $key ) {
124 $default_values = $this->get_default_values();
125
126 if ( ! isset( $default_values[ $key ] ) ) {
127 return null;
128 }
129
130 $default = $default_values[ $key ];
131
132 /**
133 * Pre-filter any Imagify option before read.
134 *
135 * @since 1.0
136 *
137 * @param mixed $value Value to return instead of the option value. Default null to skip it.
138 * @param mixed $default The default value.
139 */
140 $value = apply_filters( 'pre_get_imagify_' . $this->get_hook_identifier() . '_' . $key, null, $default );
141
142 if ( isset( $value ) ) {
143 return $value;
144 }
145
146 // Get all values.
147 $values = $this->get_all();
148
149 // Sanitize and validate the value.
150 $value = $this->sanitize_and_validate( $key, $values[ $key ], $default );
151
152 /**
153 * Filter any Imagify option after read.
154 *
155 * @since 1.0
156 *
157 * @param mixed $value Value of the option.
158 * @param mixed $default The default value. Default false.
159 */
160 return apply_filters( 'get_imagify_' . $this->get_hook_identifier() . '_' . $key, $value, $default );
161 }
162
163 /**
164 * Get all options (no cast, no sanitization, no validation).
165 *
166 * @since 1.7
167 * @author Grégory Viguier
168 * @access public
169 *
170 * @return array The options.
171 */
172 public function get_all() {
173 $values = $this->get_raw();
174
175 if ( ! $values ) {
176 return $this->get_reset_values();
177 }
178
179 return imagify_merge_intersect( $values, $this->get_default_values() );
180 }
181
182 /**
183 * Set one or multiple options.
184 *
185 * @since 1.7
186 * @author Grégory Viguier
187 * @access public
188 *
189 * @param array $values An array of option name / option value pairs.
190 */
191 public function set( $values ) {
192 $args = func_get_args();
193
194 if ( isset( $args[1] ) && is_string( $args[0] ) ) {
195 $values = [ $args[0] => $args[1] ];
196 }
197
198 if ( ! is_array( $values ) ) {
199 // PABKAC.
200 return;
201 }
202
203 $values = array_merge( $this->get_all(), $values );
204 $values = array_intersect_key( $values, $this->get_default_values() );
205
206 $this->set_raw( $values );
207 }
208
209 /**
210 * Delete one or multiple options.
211 *
212 * @since 1.7
213 * @author Grégory Viguier
214 * @access public
215 *
216 * @param array|string $keys An array of option names or a single option name.
217 */
218 public function delete( $keys ) {
219 $values = $this->get_raw();
220
221 if ( ! $values ) {
222 if ( false !== $values ) {
223 $this->delete_raw();
224 }
225 return;
226 }
227
228 $keys = array_flip( (array) $keys );
229 $values = array_diff_key( $values, $keys );
230
231 $this->set_raw( $values );
232 }
233
234 /**
235 * Checks if the option with the given name exists or not.
236 *
237 * @since 1.7
238 * @author Grégory Viguier
239 * @access public
240 *
241 * @param string $key The option name.
242 * @return bool
243 */
244 public function has( $key ) {
245 return null !== $this->get( $key );
246 }
247
248
249 /** ----------------------------------------------------------------------------------------- */
250 /** GET / UPDATE / DELETE RAW VALUES ======================================================== */
251 /** ----------------------------------------------------------------------------------------- */
252
253 /**
254 * Get the name of the option that stores the settings.
255 *
256 * @since 1.7
257 * @author Grégory Viguier
258 * @access public
259 *
260 * @return string
261 */
262 public function get_option_name() {
263 return IMAGIFY_SLUG . '_' . $this->identifier;
264 }
265
266 /**
267 * Get the identifier used in the hook names.
268 *
269 * @since 1.7
270 * @author Grégory Viguier
271 * @access public
272 *
273 * @return string
274 */
275 public function get_hook_identifier() {
276 return $this->hook_identifier;
277 }
278
279 /**
280 * Tell if the option is autoloaded.
281 *
282 * @since 1.7
283 * @author Grégory Viguier
284 * @access public
285 *
286 * @return bool
287 */
288 public function is_autoloaded() {
289 return 'yes' === $this->autoload;
290 }
291
292 /**
293 * Tell if the option is a network option.
294 *
295 * @since 1.7
296 * @author Grégory Viguier
297 * @access public
298 *
299 * @return bool
300 */
301 public function is_network_option() {
302 return (bool) $this->network_option;
303 }
304
305 /**
306 * Get the raw value of all Imagify options.
307 *
308 * @since 1.7
309 * @author Grégory Viguier
310 * @access public
311 *
312 * @return array|bool The options. False if not set yet. An empty array if invalid.
313 */
314 public function get_raw() {
315 $values = $this->is_network_option() ? get_site_option( $this->get_option_name() ) : get_option( $this->get_option_name() );
316
317 if ( false !== $values && ! is_array( $values ) ) {
318 return [];
319 }
320
321 return $values;
322 }
323
324 /**
325 * Update the Imagify options.
326 *
327 * @since 1.7
328 * @author Grégory Viguier
329 * @access public
330 *
331 * @param array $values An array of option name / option value pairs.
332 */
333 public function set_raw( $values ) {
334 if ( ! $values ) {
335 // The option is empty: delete it.
336 $this->delete_raw();
337
338 } elseif ( $this->is_network_option() ) {
339 // Network option.
340 update_site_option( $this->get_option_name(), $values );
341
342 } elseif ( false === get_option( $this->get_option_name() ) ) {
343 // Compat' with WP < 4.2 + autoload: the option doesn't exist in the database.
344 add_option( $this->get_option_name(), $values, '', $this->autoload );
345 } else {
346 // Update the current value.
347 update_option( $this->get_option_name(), $values, $this->autoload );
348 }
349 }
350
351 /**
352 * Delete all Imagify options.
353 *
354 * @since 1.7
355 * @author Grégory Viguier
356 * @access public
357 */
358 public function delete_raw() {
359 $this->is_network_option() ? delete_site_option( $this->get_option_name() ) : delete_option( $this->get_option_name() );
360 }
361
362
363 /** ----------------------------------------------------------------------------------------- */
364 /** DEFAULT + RESET VALUES ================================================================== */
365 /** ----------------------------------------------------------------------------------------- */
366
367 /**
368 * Get default option values.
369 *
370 * @since 1.7
371 * @author Grégory Viguier
372 * @access public
373 *
374 * @return array
375 */
376 public function get_default_values() {
377 $default_values = $this->default_values;
378
379 if ( ! empty( $default_values['cached'] ) ) {
380 unset( $default_values['cached'] );
381 return $default_values;
382 }
383
384 /**
385 * Allow to add more default option values.
386 *
387 * @since 1.7
388 * @author Grégory Viguier
389 *
390 * @param array $new_values New default option values.
391 * @param array $default_values Plugin default option values.
392 */
393 $new_values = apply_filters( 'imagify_default_' . $this->get_hook_identifier() . '_values', [], $default_values );
394 $new_values = is_array( $new_values ) ? $new_values : [];
395
396 if ( $new_values ) {
397 // Don't allow new values to overwrite the plugin values.
398 $new_values = array_diff_key( $new_values, $default_values );
399 }
400
401 if ( $new_values ) {
402 $default_values = array_merge( $default_values, $new_values );
403 $this->default_values = $default_values;
404 }
405
406 $this->default_values['cached'] = 1;
407
408 return $default_values;
409 }
410
411 /**
412 * Get the values used when the option is empty.
413 *
414 * @since 1.7
415 * @author Grégory Viguier
416 * @access public
417 *
418 * @return array
419 */
420 public function get_reset_values() {
421 $reset_values = $this->reset_values;
422
423 if ( ! empty( $reset_values['cached'] ) ) {
424 unset( $reset_values['cached'] );
425 return $reset_values;
426 }
427
428 $default_values = $this->get_default_values();
429 $reset_values = array_merge( $default_values, $reset_values );
430
431 /**
432 * Allow to filter the "reset" option values.
433 *
434 * @since 1.7
435 * @author Grégory Viguier
436 *
437 * @param array $reset_values Plugin reset option values.
438 */
439 $new_values = apply_filters( 'imagify_reset_' . $this->get_hook_identifier() . '_values', $reset_values );
440
441 if ( $new_values && is_array( $new_values ) ) {
442 $reset_values = array_merge( $reset_values, $new_values );
443 }
444
445 $this->reset_values = $reset_values;
446 $this->reset_values['cached'] = 1;
447
448 return $reset_values;
449 }
450
451
452 /** ----------------------------------------------------------------------------------------- */
453 /** SANITIZATION, VALIDATION ================================================================ */
454 /** ----------------------------------------------------------------------------------------- */
455
456 /**
457 * Sanitize and validate an option value.
458 *
459 * @since 1.7
460 * @author Grégory Viguier
461 * @access public
462 *
463 * @param string $key The option key.
464 * @param mixed $value The value.
465 * @param mixed $default_value The default value.
466 * @return mixed
467 */
468 public function sanitize_and_validate( $key, $value, $default_value = null ) {
469 if ( ! isset( $default_value ) ) {
470 $default_values = $this->get_default_values();
471 $default_value = $default_values[ $key ];
472 }
473
474 // Cast the value.
475 $value = self::cast( $value, $default_value );
476
477 if ( $value === $default_value ) {
478 return $value;
479 }
480
481 // Version.
482 if ( 'version' === $key ) {
483 return sanitize_text_field( $value );
484 }
485
486 return $this->sanitize_and_validate_value( $key, $value, $default_value );
487 }
488
489 /**
490 * Sanitize and validate an option value. Basic casts have been made.
491 *
492 * @since 1.7
493 * @author Grégory Viguier
494 * @access public
495 *
496 * @param string $key The option key.
497 * @param mixed $value The value.
498 * @param mixed $default_value The default value.
499 * @return mixed
500 */
501 abstract public function sanitize_and_validate_value( $key, $value, $default_value );
502
503 /**
504 * Sanitize and validate Imagify's options before storing them.
505 *
506 * @since 1.7
507 * @author Grégory Viguier
508 * @access public
509 *
510 * @param string $values The option value.
511 * @return array
512 */
513 public function sanitize_and_validate_on_update( $values ) {
514 $values = is_array( $values ) ? $values : [];
515 $default_values = $this->get_default_values();
516
517 if ( $values ) {
518 foreach ( $default_values as $key => $default ) {
519 if ( isset( $values[ $key ] ) ) {
520 $values[ $key ] = $this->sanitize_and_validate( $key, $values[ $key ], $default );
521 }
522 }
523 }
524
525 $values = array_intersect_key( $values, $default_values );
526
527 // Version.
528 if ( empty( $values['version'] ) ) {
529 $values['version'] = IMAGIFY_VERSION;
530 }
531
532 return $this->validate_values_on_update( $values );
533 }
534
535 /**
536 * Validate Imagify's options before storing them. Basic sanitization and validation have been made, row by row.
537 *
538 * @since 1.7
539 * @author Grégory Viguier
540 * @access public
541 *
542 * @param string $values The option value.
543 * @return array
544 */
545 public function validate_values_on_update( $values ) {
546 return $values;
547 }
548
549
550 /** ----------------------------------------------------------------------------------------- */
551 /** TOOLS =================================================================================== */
552 /** ----------------------------------------------------------------------------------------- */
553
554 /**
555 * Cast a value, depending on its default value type.
556 *
557 * @since 1.7
558 * @author Grégory Viguier
559 * @access public
560 *
561 * @param mixed $value The value to cast.
562 * @param mixed $default_value The default value.
563 * @return mixed
564 */
565 public static function cast( $value, $default_value ) {
566 if ( is_array( $default_value ) ) {
567 return is_array( $value ) ? $value : [];
568 }
569
570 if ( is_int( $default_value ) ) {
571 return (int) $value;
572 }
573
574 if ( is_bool( $default_value ) ) {
575 return (bool) $value;
576 }
577
578 if ( is_float( $default_value ) ) {
579 return round( (float) $value, 3 );
580 }
581
582 return $value;
583 }
584
585 /**
586 * Cast a float like 3.000 into an integer.
587 *
588 * @since 1.7
589 * @author Grégory Viguier
590 * @access public
591 *
592 * @param float $value The value.
593 * @return float|int
594 */
595 public static function maybe_cast_float_as_int( $value ) {
596 return ( $value / (int) $value ) === (float) 1 ? (int) $value : $value;
597 }
598 }
599