Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 264
0.00% covered (danger)
0.00%
0 / 18
CRAP
0.00% covered (danger)
0.00%
0 / 1
ImageSettings
0.00% covered (danger)
0.00%
0 / 264
0.00% covered (danger)
0.00%
0 / 18
4422
0.00% covered (danger)
0.00%
0 / 1
 get_default_settings
0.00% covered (danger)
0.00%
0 / 33
0.00% covered (danger)
0.00%
0 / 1
12
 __construct
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 register_settings
0.00% covered (danger)
0.00%
0 / 117
0.00% covered (danger)
0.00%
0 / 1
2
 initialize_settings
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
12
 sanitize_settings
0.00% covered (danger)
0.00%
0 / 38
0.00% covered (danger)
0.00%
0 / 1
306
 maybe_refresh_with_capabilities
0.00% covered (danger)
0.00%
0 / 27
0.00% covered (danger)
0.00%
0 / 1
420
 is_optimization_enabled
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 is_auto_optimization_enabled
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 is_auto_delete_enabled
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 is_lazy_loading_enabled
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 is_bulk_optimization_enabled
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 is_webp_preference_enabled
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 is_banned
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 get_monthly_usage
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
12
 get
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
72
 update
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 is_cloudflare_polish_enabled
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 is_cloudflare_mirage_enabled
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace NewfoldLabs\WP\Module\Performance\Images;
4
5/**
6 * Manages the registration and sanitization of image optimization settings.
7 */
8class ImageSettings {
9    /**
10     * The setting key for image optimization.
11     */
12    private const SETTING_KEY = 'nfd_image_optimization';
13
14    /**
15     * Stores default image optimization settings.
16     *
17     * @var array
18     */
19    private $default_settings = array();
20
21    /**
22     * Constructor to initialize the settings and the listener.
23     *
24     * @param \NewfoldLabs\WP\Container\Container $container Dependency injection container.
25     */
26    private static function get_default_settings( $container = null ) {
27        $default = array(
28            'enabled'                            => true,
29            'bulk_optimization'                  => true,
30            'prefer_optimized_image_when_exists' => true,
31            'auto_optimized_uploaded_images'     => array(
32                'enabled'                    => true,
33                'auto_delete_original_image' => false,
34            ),
35            'lazy_loading'                       => array(
36                'enabled' => true,
37            ),
38            'banned_status'                      => false,
39            'monthly_usage'                      => array(
40                'monthlyRequestCount' => 0,
41                'maxRequestsPerMonth' => 100000,
42            ),
43            'cloudflare'                         => array(
44                'polish' => array(
45                    'value'    => false,
46                    'user_set' => false,
47                ),
48                'mirage' => array(
49                    'value'    => false,
50                    'user_set' => false,
51                ),
52            ),
53        );
54
55        // Override with capability-aware defaults
56        if (
57        null !== $container &&
58        $container->has( 'capabilities' )
59        ) {
60            $capabilities = $container->get( 'capabilities' );
61
62            $default['cloudflare']['polish']['value'] = (bool) $capabilities->get( 'hasCloudflarePolish' );
63            $default['cloudflare']['mirage']['value'] = (bool) $capabilities->get( 'hasCloudflareMirage' );
64        }
65
66        return $default;
67    }
68
69    /**
70     * Constructor to initialize the settings and the listener.
71     *
72     * @param \NewfoldLabs\WP\Container\Container $container Dependency injection container.
73     */
74    public function __construct( $container ) {
75        $this->default_settings = self::get_default_settings( $container );
76        $this->register_settings( $container );
77        $this->initialize_settings();
78    }
79
80    /**
81     * Registers the `nfd_image_optimization` setting in WordPress.
82     */
83    private function register_settings() {
84        register_setting(
85            'general',
86            self::SETTING_KEY,
87            array(
88                'type'              => 'object',
89                'description'       => __( 'Settings for NFD Image Optimization.', 'wp-module-performance' ),
90                'sanitize_callback' => array( $this, 'sanitize_settings' ),
91                'default'           => $this->default_settings,
92                'show_in_rest'      => array(
93                    'schema' => array(
94                        'type'                 => 'object',
95                        'properties'           => array(
96                            'enabled'           => array(
97                                'type'        => 'boolean',
98                                'description' => __( 'Enable image optimization.', 'wp-module-performance' ),
99                                'default'     => $this->default_settings['enabled'],
100                            ),
101                            'prefer_optimized_image_when_exists' => array(
102                                'type'        => 'boolean',
103                                'description' => __( 'Prefer WebP format when it exists.', 'wp-module-performance' ),
104                                'default'     => $this->default_settings['prefer_optimized_image_when_exists'],
105                            ),
106                            'auto_optimized_uploaded_images' => array(
107                                'type'        => 'object',
108                                'description' => __( 'Auto-optimized uploaded images settings.', 'wp-module-performance' ),
109                                'properties'  => array(
110                                    'enabled' => array(
111                                        'type'        => 'boolean',
112                                        'description' => __( 'Automatically optimize uploaded images.', 'wp-module-performance' ),
113                                        'default'     => $this->default_settings['auto_optimized_uploaded_images']['enabled'],
114                                    ),
115                                    'auto_delete_original_image' => array(
116                                        'type'        => 'boolean',
117                                        'description' => __( 'Delete the original uploaded image after optimization.', 'wp-module-performance' ),
118                                        'default'     => $this->default_settings['auto_optimized_uploaded_images']['auto_delete_original_image'],
119                                    ),
120                                ),
121                            ),
122                            'lazy_loading'      => array(
123                                'type'        => 'object',
124                                'description' => __( 'Settings for lazy loading.', 'wp-module-performance' ),
125                                'properties'  => array(
126                                    'enabled' => array(
127                                        'type'        => 'boolean',
128                                        'description' => __( 'Enable lazy loading.', 'wp-module-performance' ),
129                                        'default'     => $this->default_settings['lazy_loading']['enabled'],
130                                    ),
131                                ),
132                            ),
133                            'bulk_optimization' => array(
134                                'type'        => 'boolean',
135                                'description' => __( 'Enable bulk optimization of images.', 'wp-module-performance' ),
136                                'default'     => $this->default_settings['bulk_optimization'],
137                            ),
138                            'banned_status'     => array(
139                                'type'        => 'boolean',
140                                'description' => __( 'Indicates if the site is banned from image optimization.', 'wp-module-performance' ),
141                                'default'     => $this->default_settings['banned_status'],
142                            ),
143                            'monthly_usage'     => array(
144                                'type'        => 'object',
145                                'description' => __( 'Monthly usage statistics for image optimization.', 'wp-module-performance' ),
146                                'properties'  => array(
147                                    'monthlyRequestCount' => array(
148                                        'type'        => 'integer',
149                                        'description' => __( 'Number of requests made this month.', 'wp-module-performance' ),
150                                        'default'     => $this->default_settings['monthly_usage']['monthlyRequestCount'],
151                                    ),
152                                    'maxRequestsPerMonth' => array(
153                                        'type'        => 'integer',
154                                        'description' => __( 'Maximum allowed requests per month.', 'wp-module-performance' ),
155                                        'default'     => $this->default_settings['monthly_usage']['maxRequestsPerMonth'],
156                                    ),
157                                ),
158                            ),
159                            'cloudflare'        => array(
160                                'type'        => 'object',
161                                'description' => __( 'Cloudflare-related image optimization options.', 'wp-module-performance' ),
162                                'properties'  => array(
163                                    'polish' => array(
164                                        'type'        => 'object',
165                                        'description' => __( 'Enable Cloudflare Polish optimization.', 'wp-module-performance' ),
166                                        'properties'  => array(
167                                            'value'    => array(
168                                                'type'    => 'boolean',
169                                                'default' => $this->default_settings['cloudflare']['polish']['value'],
170                                            ),
171                                            'user_set' => array(
172                                                'type'    => 'boolean',
173                                                'default' => false,
174                                                'description' => 'Whether the value was explicitly set by the user.',
175                                            ),
176                                        ),
177                                    ),
178                                    'mirage' => array(
179                                        'type'        => 'object',
180                                        'description' => __( 'Enable Cloudflare Mirage optimization.', 'wp-module-performance' ),
181                                        'properties'  => array(
182                                            'value'    => array(
183                                                'type'    => 'boolean',
184                                                'default' => $this->default_settings['cloudflare']['mirage']['value'],
185                                            ),
186                                            'user_set' => array(
187                                                'type'    => 'boolean',
188                                                'default' => false,
189                                                'description' => 'Whether the value was explicitly set by the user.',
190                                            ),
191                                        ),
192                                    ),
193                                ),
194                            ),
195                        ),
196                        'additionalProperties' => false,
197                    ),
198                ),
199            )
200        );
201    }
202
203    /**
204     * Initializes the setting if it does not exist.
205     */
206    private function initialize_settings() {
207        $current_settings = get_option( self::SETTING_KEY, false );
208
209        if ( false === $current_settings || ! is_array( $current_settings ) ) {
210            add_option( self::SETTING_KEY, $this->default_settings );
211        }
212    }
213
214    /**
215     * Sanitizes the `nfd_image_optimization` settings.
216     *
217     * @param array $settings The input settings.
218     * @return array The sanitized settings.
219     */
220    public function sanitize_settings( $settings ) {
221        $existing_settings = get_option( self::SETTING_KEY, array() );
222
223        if ( ! is_array( $settings ) ) {
224            $settings = array();
225        }
226
227        return array(
228            'enabled'                            => isset( $settings['enabled'] ) ? ! empty( $settings['enabled'] ) : ( ! empty( $existing_settings['enabled'] ) ),
229            'prefer_optimized_image_when_exists' => isset( $settings['prefer_optimized_image_when_exists'] ) ? ! empty( $settings['prefer_optimized_image_when_exists'] ) : ( ! empty( $existing_settings['prefer_optimized_image_when_exists'] ) ),
230            'auto_optimized_uploaded_images'     => array(
231                'enabled'                    => isset( $settings['auto_optimized_uploaded_images']['enabled'] ) ? ! empty( $settings['auto_optimized_uploaded_images']['enabled'] ) : ( ! empty( $existing_settings['auto_optimized_uploaded_images']['enabled'] ) ),
232                'auto_delete_original_image' => isset( $settings['auto_optimized_uploaded_images']['auto_delete_original_image'] ) ? ! empty( $settings['auto_optimized_uploaded_images']['auto_delete_original_image'] ) : ( ! empty( $existing_settings['auto_optimized_uploaded_images']['auto_delete_original_image'] ) ),
233            ),
234            'lazy_loading'                       => array(
235                'enabled' => isset( $settings['lazy_loading']['enabled'] ) ? ! empty( $settings['lazy_loading']['enabled'] ) : ( ! empty( $existing_settings['lazy_loading']['enabled'] ) ),
236            ),
237            'bulk_optimization'                  => isset( $settings['bulk_optimization'] ) ? ! empty( $settings['bulk_optimization'] ) : ( ! empty( $existing_settings['bulk_optimization'] ) ),
238            'banned_status'                      => isset( $settings['banned_status'] ) ? ! empty( $settings['banned_status'] ) : ( ! empty( $existing_settings['banned_status'] ) ),
239            'monthly_usage'                      => array(
240                'monthlyRequestCount' => isset( $settings['monthly_usage']['monthlyRequestCount'] ) ? (int) $settings['monthly_usage']['monthlyRequestCount'] : ( isset( $existing_settings['monthly_usage']['monthlyRequestCount'] ) ? (int) $existing_settings['monthly_usage']['monthlyRequestCount'] : 0 ),
241                'maxRequestsPerMonth' => isset( $settings['monthly_usage']['maxRequestsPerMonth'] ) ? (int) $settings['monthly_usage']['maxRequestsPerMonth'] : ( isset( $existing_settings['monthly_usage']['maxRequestsPerMonth'] ) ? (int) $existing_settings['monthly_usage']['maxRequestsPerMonth'] : 100000 ),
242            ),
243            'cloudflare'                         => array(
244                'polish' => array(
245                    'value'    => isset( $settings['cloudflare']['polish']['value'] )
246                        ? (bool) $settings['cloudflare']['polish']['value']
247                        : (bool) ( $existing_settings['cloudflare']['polish']['value'] ?? false ),
248                    'user_set' => isset( $settings['cloudflare']['mirage']['user_set'] )
249                    ? (bool) $settings['cloudflare']['mirage']['user_set']
250                    : (bool) ( $existing_settings['cloudflare']['mirage']['user_set'] ?? false ),
251                ),
252                'mirage' => array(
253                    'value'    => isset( $settings['cloudflare']['mirage']['value'] )
254                    ? (bool) $settings['cloudflare']['mirage']['value']
255                    : (bool) ( $existing_settings['cloudflare']['mirage']['value'] ?? false ),
256                    'user_set' => isset( $settings['cloudflare']['mirage']['user_set'] )
257                    ? (bool) $settings['cloudflare']['mirage']['user_set']
258                    : (bool) ( $existing_settings['cloudflare']['mirage']['user_set'] ?? false ),
259                ),
260            ),
261        );
262    }
263
264    /**
265     * Refreshes legacy settings with Cloudflare feature flags based on capabilities.
266     *
267     * @param object|null $capabilities Capabilities object (optional).
268     */
269    public static function maybe_refresh_with_capabilities( $capabilities ) {
270        $settings = get_option( self::SETTING_KEY, array() );
271
272        // If the option doesn't exist or is empty, use default settings
273        if ( empty( $settings ) || ! is_array( $settings ) ) {
274            $settings = self::get_default_settings();
275        }
276
277        if ( ! isset( $settings['cloudflare']['polish'] ) || ! is_array( $settings['cloudflare']['polish'] ) ) {
278            $settings['cloudflare']['polish'] = array(
279                'value'    => false,
280                'user_set' => false,
281            );
282        }
283        if ( ! isset( $settings['cloudflare']['mirage'] ) || ! is_array( $settings['cloudflare']['mirage'] ) ) {
284            $settings['cloudflare']['mirage'] = array(
285                'value'    => false,
286                'user_set' => false,
287            );
288        }
289
290        if ( is_array( $capabilities ) ) {
291            $has_polish = isset( $capabilities['hasCloudflarePolish'] ) ? (bool) $capabilities['hasCloudflarePolish'] : false;
292            $has_mirage = isset( $capabilities['hasCloudflareMirage'] ) ? (bool) $capabilities['hasCloudflareMirage'] : false;
293
294            // Polish
295            if ( $settings['cloudflare']['polish']['user_set'] ) {
296                if ( $settings['cloudflare']['polish']['value'] && ! $has_polish ) {
297                    // User enabled but capability is gone â€” disable it
298                    $settings['cloudflare']['polish']['value'] = false;
299                }
300            } elseif ( ! $settings['cloudflare']['polish']['value'] && $has_polish ) {
301                    // Not user set â€” follow capability
302                $settings['cloudflare']['polish']['value'] = true;
303
304            }
305
306            // Mirage
307            if ( $settings['cloudflare']['mirage']['user_set'] ) {
308                if ( $settings['cloudflare']['mirage']['value'] && ! $has_mirage ) {
309                    $settings['cloudflare']['mirage']['value'] = false;
310                }
311            } elseif ( ! $settings['cloudflare']['mirage']['value'] && $has_mirage ) {
312                    $settings['cloudflare']['mirage']['value'] = true;
313            }
314        }
315
316        update_option( self::SETTING_KEY, $settings );
317    }
318
319
320    /**
321     * Checks if image optimization is enabled.
322     *
323     * @return bool True if optimization is enabled, false otherwise.
324     */
325    public static function is_optimization_enabled() {
326        $settings = get_option( self::SETTING_KEY, self::get_default_settings() );
327        return ! empty( $settings['enabled'] );
328    }
329
330    /**
331     * Checks if auto-optimization for uploaded images is enabled.
332     *
333     * @return bool True if auto-optimization is enabled, false otherwise.
334     */
335    public static function is_auto_optimization_enabled() {
336        $settings = get_option( self::SETTING_KEY, self::get_default_settings() );
337        return ! empty( $settings['auto_optimized_uploaded_images']['enabled'] );
338    }
339
340    /**
341     * Checks if auto-deletion of the original image is enabled.
342     *
343     * @return bool True if auto-deletion is enabled, false otherwise.
344     */
345    public static function is_auto_delete_enabled() {
346        $settings = get_option( self::SETTING_KEY, self::get_default_settings() );
347        return ! empty( $settings['auto_optimized_uploaded_images']['auto_delete_original_image'] );
348    }
349
350    /**
351     * Checks if lazy loading is enabled.
352     *
353     * @return bool True if lazy loading is enabled, false otherwise.
354     */
355    public static function is_lazy_loading_enabled() {
356        $settings = get_option( self::SETTING_KEY, self::get_default_settings() );
357        return ! empty( $settings['lazy_loading']['enabled'] );
358    }
359
360    /**
361     * Checks if bulk optimization is enabled.
362     *
363     * @return bool True if bulk optimization is enabled, false otherwise.
364     */
365    public static function is_bulk_optimization_enabled() {
366        $settings = get_option( self::SETTING_KEY, self::get_default_settings() );
367        return ! empty( $settings['bulk_optimization'] );
368    }
369
370    /**
371     * Checks if WebP preference is enabled.
372     *
373     * @return bool True if WebP preference is enabled, false otherwise.
374     */
375    public static function is_webp_preference_enabled() {
376        $settings = get_option( self::SETTING_KEY, self::get_default_settings() );
377        return ! empty( $settings['prefer_optimized_image_when_exists'] );
378    }
379
380    /**
381     * Checks if the site is banned from image optimization.
382     *
383     * @return bool True if the site is banned, false otherwise.
384     */
385    public static function is_banned() {
386        $settings = get_option( self::SETTING_KEY, self::get_default_settings() );
387        return ! empty( $settings['banned_status'] );
388    }
389
390    /**
391     * Retrieves the monthly usage statistics for image optimization.
392     *
393     * @return array An array containing `monthlyRequestCount` and `maxRequestsPerMonth`.
394     */
395    public static function get_monthly_usage() {
396        $settings = get_option( self::SETTING_KEY, self::get_default_settings() );
397
398        // Ensure monthly_usage exists and return default values if not set
399        return isset( $settings['monthly_usage'] ) && is_array( $settings['monthly_usage'] )
400        ? $settings['monthly_usage']
401        : array(
402            'monthlyRequestCount' => 0,
403            'maxRequestsPerMonth' => 100000,
404        );
405    }
406
407    /**
408     * Retrieves the image optimization settings.
409     *
410     * @param \NewfoldLabs\WP\Container\Container|null $container   Dependency injection container (optional).
411     * @param bool                                     $call_worker Whether to fetch the latest monthly usage from the worker. Default is true.
412     *
413     * @return array The current image optimization settings, including monthly usage and banned status.
414     */
415    public static function get( $container, $call_worker = true ) {
416        $settings = get_option( self::SETTING_KEY, array() );
417
418        if ( ! is_array( $settings ) ) {
419            $settings = self::get_default_settings();
420        }
421
422        if ( ! isset( $settings['banned_status'] ) ) {
423            $settings['banned_status'] = self::is_banned();
424        }
425
426        if ( $call_worker && ( empty( $settings['monthly_usage'] ) || ! is_array( $settings['monthly_usage'] ) ) ) {
427            $usage_data = ( new ImageService( $container ) )->get_monthly_usage_limit( true );
428            if ( ! is_wp_error( $usage_data ) ) {
429                $settings['monthly_usage'] = $usage_data;
430                update_option( self::SETTING_KEY, $settings );
431            } else {
432                $settings['monthly_usage'] = isset( $settings['monthly_usage'] ) ? $settings['monthly_usage'] : array(
433                    'monthlyRequestCount' => 0,
434                    'maxRequestsPerMonth' => 100000,
435                );
436            }
437        }
438
439        return $settings;
440    }
441
442    /**
443     * Updates the image optimization settings.
444     *
445     * @param array                               $settings  The new settings array.
446     * @param \NewfoldLabs\WP\Container\Container $container Dependency injection container.
447     *
448     * @return bool true if the settings were updated successfully, false otherwise.
449     */
450    public static function update( $settings, $container ) {
451        $instance           = new self( $container );
452        $sanitized_settings = $instance->sanitize_settings( $settings );
453        return update_option( self::SETTING_KEY, $sanitized_settings );
454    }
455
456    /**
457     * Checks if Cloudflare Polish is enabled.
458     *
459     * @return bool True if Polish is enabled, false otherwise.
460     */
461    public static function is_cloudflare_polish_enabled() {
462        $settings = get_option( self::SETTING_KEY, self::get_default_settings() );
463
464        return ! empty( $settings['cloudflare']['polish'] );
465    }
466
467    /**
468     * Checks if Cloudflare Mirage is enabled.
469     *
470     * @return bool True if Mirage is enabled, false otherwise.
471     */
472    public static function is_cloudflare_mirage_enabled() {
473        $settings = get_option( self::SETTING_KEY, self::get_default_settings() );
474
475        return ! empty( $settings['cloudflare']['mirage'] );
476    }
477}