Completed
Push — master ( d10a3b...267ab8 )
by frank
01:54
created

autoptimizeImages   F

Complexity

Total Complexity 274

Size/Duplication

Total Lines 1343
Duplicated Lines 3.2 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 0
Metric Value
dl 43
loc 1343
rs 0.8
c 0
b 0
f 0
wmc 274
lcom 1
cbo 5

50 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 2
A set_options() 0 6 1
A fetch_options() 0 17 3
B imgopt_active() 0 18 7
A instance() 0 8 2
A run() 13 13 5
B run_on_frontend() 0 67 7
B should_run() 0 23 9
A get_imgopt_host() 0 15 5
A get_imgopt_host_wrapper() 0 6 1
A get_service_url_suffix() 0 6 1
A get_img_quality_string() 0 15 2
A get_img_quality_array() 0 18 2
B get_img_quality_setting() 0 19 8
A filter_preconnect_imgopt_url() 0 7 1
C normalize_img_url() 0 65 13
A filter_optimize_css_images() 0 10 2
A get_imgopt_base_url() 0 14 2
C can_optimize_image() 0 39 17
B build_imgopt_url() 0 40 8
A replace_data_thumbs() 0 4 1
A replace_img_callback() 0 9 2
A replace_icon_callback() 0 12 2
F filter_optimize_images() 7 146 31
B get_size_from_tag() 0 36 7
A should_lazyload_wrapper() 0 5 1
A should_lazyload() 0 10 3
A check_nolazy() 0 7 3
A filter_lazyload_images() 0 37 4
B add_lazyload() 0 45 9
B add_lazyload_js_footer() 0 43 7
A get_cdn_url() 0 12 2
A get_lazyload_exclusions() 3 24 4
A inject_classes_in_tag() 0 9 2
A get_default_lazyload_placeholder() 0 3 1
A should_ngimg() 0 14 5
C process_picture_tag() 0 35 12
A process_bgimage() 8 11 3
A lazyload_bgimg_callback() 0 15 2
A maybe_fix_missing_quotes() 0 10 4
A imgopt_admin_menu() 0 15 2
A add_imgopt_tab() 0 8 2
F imgopt_options_page() 3 169 34
C get_imgopt_status_notice() 0 54 11
A get_imgopt_status_notice_wrapper() 0 5 1
B query_img_provider_stats() 9 29 7
A get_img_provider_stats() 0 6 1
B launch_ok() 0 19 7
A launch_ok_wrapper() 0 5 1
A get_imgopt_provider_userstatus() 0 27 5

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like autoptimizeImages often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use autoptimizeImages, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Handles optimizing images.
4
 */
5
6
if ( ! defined( 'ABSPATH' ) ) {
7
    exit;
8
}
9
10
class autoptimizeImages
11
{
12
    /**
13
     * Options.
14
     *
15
     * @var array
16
     */
17
    protected $options = array();
18
19
    /**
20
     * Singleton instance.
21
     *
22
     * @var self|null
23
     */
24
    protected static $instance = null;
25
26
    public function __construct( array $options = array() )
27
    {
28
        // If options are not provided, fetch them.
29
        if ( empty( $options ) ) {
30
            $options = $this->fetch_options();
31
        }
32
33
        $this->set_options( $options );
34
    }
35
36
    public function set_options( array $options )
37
    {
38
        $this->options = $options;
39
40
        return $this;
41
    }
42
43
    public static function fetch_options()
44
    {
45
        $value = autoptimizeOptionWrapper::get_option( 'autoptimize_imgopt_settings' );
46
        if ( empty( $value ) ) {
47
            // Fallback to returning defaults when no stored option exists yet.
48
            $value = autoptimizeConfig::get_ao_imgopt_default_options();
49
        }
50
51
        // get service availability and add it to the options-array.
52
        $value['availabilities'] = autoptimizeOptionWrapper::get_option( 'autoptimize_service_availablity' );
53
54
        if ( empty( $value['availabilities'] ) ) {
55
            $value['availabilities'] = autoptimizeUtils::check_service_availability( true );
0 ignored issues
show
Documentation introduced by
true is of type boolean, but the function expects a false|string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
56
        }
57
58
        return $value;
59
    }
60
61
    public static function imgopt_active()
62
    {
63
        // function to quickly check if imgopt is active, used below but also in
64
        // autoptimizeMain.php to start ob_ even if no HTML, JS or CSS optimizing is done
65
        // and does not use/ request the availablity data (which could slow things down).
66
        static $imgopt_active = null;
67
68
        if ( null === $imgopt_active ) {
69
            $opts = autoptimizeOptionWrapper::get_option( 'autoptimize_imgopt_settings', '' );
70
            if ( ! empty( $opts ) && is_array( $opts ) && array_key_exists( 'autoptimize_imgopt_checkbox_field_1', $opts ) && ! empty( $opts['autoptimize_imgopt_checkbox_field_1'] ) && '1' === $opts['autoptimize_imgopt_checkbox_field_1'] ) {
71
                $imgopt_active = true;
72
            } else {
73
                $imgopt_active = false;
74
            }
75
        }
76
77
        return $imgopt_active;
78
    }
79
80
    /**
81
     * Helper for getting a singleton instance. While being an
82
     * anti-pattern generally, it comes in handy for now from a
83
     * readability/maintainability perspective, until we get some
84
     * proper dependency injection going.
85
     *
86
     * @return self
87
     */
88
    public static function instance()
89
    {
90
        if ( null === self::$instance ) {
91
            self::$instance = new self();
92
        }
93
94
        return self::$instance;
95
    }
96
97 View Code Duplication
    public function run()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
98
    {
99
        if ( is_admin() ) {
100
            if ( is_multisite() && is_network_admin() && autoptimizeOptionWrapper::is_ao_active_for_network() ) {
101
                add_action( 'network_admin_menu', array( $this, 'imgopt_admin_menu' ) );
102
            } else {
103
                add_action( 'admin_menu', array( $this, 'imgopt_admin_menu' ) );
104
            }
105
            add_filter( 'autoptimize_filter_settingsscreen_tabs', array( $this, 'add_imgopt_tab' ), 9 );
106
        } else {
107
            add_action( 'wp', array( $this, 'run_on_frontend' ) );
108
        }
109
    }
110
111
    public function run_on_frontend() {
112
        if ( ! $this->should_run() ) {
113
            if ( $this->should_lazyload() ) {
114
                add_filter(
115
                    'wp_lazy_loading_enabled',
116
                    '__return_false'
117
                );
118
                add_filter(
119
                    'autoptimize_html_after_minify',
120
                    array( $this, 'filter_lazyload_images' ),
121
                    10,
122
                    1
123
                );
124
                add_action(
125
                    'wp_footer',
126
                    array( $this, 'add_lazyload_js_footer' ),
127
                    10,
128
                    0
129
                );
130
            }
131
            return;
132
        }
133
134
        $active = false;
135
136
        if ( apply_filters( 'autoptimize_filter_imgopt_do', true ) ) {
137
            add_filter(
138
                'autoptimize_html_after_minify',
139
                array( $this, 'filter_optimize_images' ),
140
                10,
141
                1
142
            );
143
            $active = true;
144
        }
145
146
        if ( apply_filters( 'autoptimize_filter_imgopt_do_css', true ) ) {
147
            add_filter(
148
                'autoptimize_filter_base_replace_cdn',
149
                array( $this, 'filter_optimize_css_images' ),
150
                10,
151
                1
152
            );
153
            $active = true;
154
        }
155
156
        if ( $active ) {
157
            add_filter(
158
                'autoptimize_extra_filter_tobepreconn',
159
                array( $this, 'filter_preconnect_imgopt_url' ),
160
                10,
161
                1
162
            );
163
        }
164
165
        if ( $this->should_lazyload() ) {
166
            add_filter(
167
                'wp_lazy_loading_enabled',
168
                '__return_false'
169
            );
170
            add_action(
171
                'wp_footer',
172
                array( $this, 'add_lazyload_js_footer' ),
173
                10,
174
                0
175
            );
176
        }
177
    }
178
179
    /**
180
     * Basic checks before we can run.
181
     *
182
     * @return bool
183
     */
184
    protected function should_run()
185
    {
186
        $opts              = $this->options;
187
        $service_not_down  = ( 'down' !== $opts['availabilities']['extra_imgopt']['status'] );
188
        $not_launch_status = ( 'launch' !== $opts['availabilities']['extra_imgopt']['status'] );
189
190
        $do_cdn      = true;
191
        $_userstatus = $this->get_imgopt_provider_userstatus();
192
        if ( isset( $_userstatus['Status'] ) && ( -2 == $_userstatus['Status'] || -3 == $_userstatus['Status'] ) ) {
193
            // don't even attempt to put images on CDN if heavily exceeded threshold or if site not reachable.
194
            $do_cdn = false;
195
        }
196
197
        if (
198
            $this->imgopt_active()
199
            && $do_cdn
200
            && $service_not_down
201
            && ( $not_launch_status || $this->launch_ok() )
202
        ) {
203
            return true;
204
        }
205
        return false;
206
    }
207
208
    public function get_imgopt_host()
209
    {
210
        static $imgopt_host = null;
211
212
        if ( null === $imgopt_host ) {
213
            $imgopt_host  = 'https://cdn.shortpixel.ai/';
214
            $avail_imgopt = $this->options['availabilities']['extra_imgopt'];
215
            if ( ! empty( $avail_imgopt ) && array_key_exists( 'hosts', $avail_imgopt ) && is_array( $avail_imgopt['hosts'] ) ) {
216
                $imgopt_host = array_rand( array_flip( $avail_imgopt['hosts'] ) );
217
            }
218
            $imgopt_host = apply_filters( 'autoptimize_filter_imgopt_host', $imgopt_host );
219
        }
220
221
        return $imgopt_host;
222
    }
223
224
    public static function get_imgopt_host_wrapper()
225
    {
226
        // needed for CI tests.
227
        $self = new self();
228
        return $self->get_imgopt_host();
229
    }
230
231
    public static function get_service_url_suffix()
232
    {
233
        $suffix = '/af/GWRGFLW109483/' . AUTOPTIMIZE_SITE_DOMAIN;
234
235
        return $suffix;
236
    }
237
238
    public function get_img_quality_string()
239
    {
240
        static $quality = null;
241
242
        if ( null === $quality ) {
243
            $q_array = $this->get_img_quality_array();
244
            $setting = $this->get_img_quality_setting();
245
            $quality = apply_filters(
246
                'autoptimize_filter_imgopt_quality',
247
                'q_' . $q_array[ $setting ]
248
            );
249
        }
250
251
        return $quality;
252
    }
253
254
    public function get_img_quality_array()
255
    {
256
        static $map = null;
257
258
        if ( null === $map ) {
259
            $map = array(
260
                '1' => 'lossy',
261
                '2' => 'glossy',
262
                '3' => 'lossless',
263
            );
264
            $map = apply_filters(
265
                'autoptimize_filter_imgopt_quality_array',
266
                $map
267
            );
268
        }
269
270
        return $map;
271
    }
272
273
    public function get_img_quality_setting()
274
    {
275
        static $q = null;
276
277
        if ( null === $q ) {
278
            if ( is_array( $this->options ) && array_key_exists( 'autoptimize_imgopt_select_field_2', $this->options ) ) {
279
                $setting = $this->options['autoptimize_imgopt_select_field_2'];
280
            }
281
282
            if ( ! isset( $setting ) || empty( $setting ) || ( '1' !== $setting && '3' !== $setting ) ) {
283
                // default image opt. value is 2 ("glossy").
284
                $q = '2';
285
            } else {
286
                $q = $setting;
287
            }
288
        }
289
290
        return $q;
291
    }
292
293
    public function filter_preconnect_imgopt_url( array $in )
294
    {
295
        $url_parts = parse_url( $this->get_imgopt_base_url() );
296
        $in[]      = $url_parts['scheme'] . '://' . $url_parts['host'];
297
298
        return $in;
299
    }
300
301
    /**
302
     * Makes sure given url contains the full scheme and hostname
303
     * in case they're not present already.
304
     *
305
     * @param string $in Image url to normalize.
306
     *
307
     * @return string
308
     */
309
    private function normalize_img_url( $in )
310
    {
311
        // Only parse the site url once.
312
        static $parsed_site_url = null;
313
        if ( null === $parsed_site_url ) {
314
            $parsed_site_url = parse_url( site_url() );
315
        }
316
317
        // get CDN domain once.
318
        static $cdn_domain = null;
319
        if ( is_null( $cdn_domain ) ) {
320
            $cdn_url = $this->get_cdn_url();
321
            if ( ! empty( $cdn_url ) ) {
322
                $cdn_domain = parse_url( $cdn_url, PHP_URL_HOST );
323
            } else {
324
                $cdn_domain = '';
325
            }
326
        }
327
328
        /**
329
         * This method gets called a lot, often for identical urls it seems.
330
         * `filter_optimize_css_images()` calls us, uses the resulting url and
331
         * gives it to `can_optimize_image()`, and if that returns trueish
332
         * then `build_imgopt_url()` is called (which, again, calls this method).
333
         * Until we dig deeper into whether this all must really happen that
334
         * way, having an internal cache here helps (to avoid doing repeated
335
         * identical string operations).
336
         */
337
        static $cache = null;
338
        if ( null === $cache ) {
339
            $cache = array();
340
        }
341
342
        // Do the work on cache miss only.
343
        if ( ! isset( $cache[ $in ] ) ) {
344
            // Default to (the trimmed version of) what was given to us.
345
            $result = trim( $in );
346
347
            // Some silly plugins wrap background images in html-encoded quotes, so remove those from the img url.
348
            if ( strpos( $result, '&quot;' ) !== false ) {
349
                $result = str_replace( '&quot;', '', $result );
350
            }
351
352
            if ( autoptimizeUtils::is_protocol_relative( $result ) ) {
353
                $result = $parsed_site_url['scheme'] . ':' . $result;
354
            } elseif ( 0 === strpos( $result, '/' ) ) {
355
                // Root-relative...
356
                $result = $parsed_site_url['scheme'] . '://' . $parsed_site_url['host'] . $result;
357
            } elseif ( ! empty( $cdn_domain ) && strpos( $result, $cdn_domain ) !== 0 ) {
358
                $result = str_replace( $cdn_domain, $parsed_site_url['host'], $result );
359
            }
360
361
            // filter (default off) to remove QS from image URL's to avoid eating away optimization credits.
362
            if ( apply_filters( 'autoptimize_filter_imgopt_no_querystring', false ) && strpos( $result, '?' ) !== false ) {
363
                $result = strtok( $result, '?' );
364
            }
365
366
            $result = apply_filters( 'autoptimize_filter_imgopt_normalized_url', $result );
367
368
            // Store in cache.
369
            $cache[ $in ] = $result;
370
        }
371
372
        return $cache[ $in ];
373
    }
374
375
    public function filter_optimize_css_images( $in )
376
    {
377
        $in = $this->normalize_img_url( $in );
378
379
        if ( $this->can_optimize_image( $in ) ) {
380
            return $this->build_imgopt_url( $in, '', '' );
381
        } else {
382
            return $in;
383
        }
384
    }
385
386
    private function get_imgopt_base_url()
387
    {
388
        static $imgopt_base_url = null;
389
390
        if ( null === $imgopt_base_url ) {
391
            $imgopt_host     = $this->get_imgopt_host();
392
            $quality         = $this->get_img_quality_string();
393
            $ret_val         = apply_filters( 'autoptimize_filter_imgopt_wait', 'ret_img' ); // values: ret_wait, ret_img, ret_json, ret_blank.
394
            $imgopt_base_url = $imgopt_host . 'client/' . $quality . ',' . $ret_val;
395
            $imgopt_base_url = apply_filters( 'autoptimize_filter_imgopt_base_url', $imgopt_base_url );
396
        }
397
398
        return $imgopt_base_url;
399
    }
400
401
    private function can_optimize_image( $url, $tag = '' )
402
    {
403
        static $cdn_url      = null;
404
        static $nopti_images = null;
405
406
        if ( null === $cdn_url ) {
407
            $cdn_url = apply_filters(
408
                'autoptimize_filter_base_cdnurl',
409
                autoptimizeOptionWrapper::get_option( 'autoptimize_cdn_url', '' )
410
            );
411
        }
412
413
        if ( null === $nopti_images ) {
414
            $nopti_images = apply_filters( 'autoptimize_filter_imgopt_noptimize', '' );
415
        }
416
417
        $site_host  = AUTOPTIMIZE_SITE_DOMAIN;
418
        $url        = $this->normalize_img_url( $url );
419
        $url_parsed = parse_url( $url );
420
421
        if ( array_key_exists( 'host', $url_parsed ) && $url_parsed['host'] !== $site_host && empty( $cdn_url ) ) {
422
            return false;
423
        } elseif ( ! empty( $cdn_url ) && strpos( $url, $cdn_url ) === false && array_key_exists( 'host', $url_parsed ) && $url_parsed['host'] !== $site_host ) {
424
            return false;
425
        } elseif ( strpos( $url, '.php' ) !== false ) {
426
            return false;
427
        } elseif ( str_ireplace( array( '.png', '.gif', '.jpg', '.jpeg', '.webp', '.avif' ), '', $url_parsed['path'] ) === $url_parsed['path'] ) {
428
            // fixme: better check against end of string.
429
            return false;
430
        } elseif ( ! empty( $nopti_images ) ) {
431
            $nopti_images_array = array_filter( array_map( 'trim', explode( ',', $nopti_images ) ) );
432
            foreach ( $nopti_images_array as $nopti_image ) {
433
                if ( strpos( $url, $nopti_image ) !== false || ( ( '' !== $tag && strpos( $tag, $nopti_image ) !== false ) ) ) {
434
                    return false;
435
                }
436
            }
437
        }
438
        return true;
439
    }
440
441
    private function build_imgopt_url( $orig_url, $width = 0, $height = 0 )
442
    {
443
        // sanitize width and height.
444
        if ( strpos( $width, '%' ) !== false ) {
445
            $width = 0;
446
        }
447
        if ( strpos( $height, '%' ) !== false ) {
448
            $height = 0;
449
        }
450
        $width  = (int) $width;
451
        $height = (int) $height;
452
453
        $filtered_url = apply_filters(
454
            'autoptimize_filter_imgopt_build_url',
455
            $orig_url,
456
            $width,
457
            $height
458
        );
459
460
        // If filter modified the url, return that.
461
        if ( $filtered_url !== $orig_url ) {
462
            return $filtered_url;
463
        }
464
465
        $orig_url        = $this->normalize_img_url( $orig_url );
466
        $imgopt_base_url = $this->get_imgopt_base_url();
467
        $imgopt_size     = '';
468
469
        if ( $width && 0 !== $width ) {
470
            $imgopt_size = ',w_' . $width;
471
        }
472
473
        if ( $height && 0 !== $height ) {
474
            $imgopt_size .= ',h_' . $height;
475
        }
476
477
        $url = $imgopt_base_url . $imgopt_size . '/' . $orig_url;
478
479
        return $url;
480
    }
481
482
    public function replace_data_thumbs( $matches )
483
    {
484
        return $this->replace_img_callback( $matches, 150, 150 );
485
    }
486
487
    public function replace_img_callback( $matches, $width = 0, $height = 0 )
488
    {
489
        $_normalized_img_url = $this->normalize_img_url( $matches[1] );
490
        if ( $this->can_optimize_image( $matches[1], $matches[0] ) ) {
491
            return str_replace( $matches[1], $this->build_imgopt_url( $_normalized_img_url, $width, $height ), $matches[0] );
492
        } else {
493
            return $matches[0];
494
        }
495
    }
496
497
    public function replace_icon_callback( $matches )
498
    {
499
        if ( array_key_exists( '2', $matches ) ) {
500
            $sizes  = explode( 'x', $matches[2] );
501
            $width  = $sizes[0];
502
            $height = $sizes[1];
503
        } else {
504
            $width  = 180;
505
            $height = 180;
506
        }
507
        return $this->replace_img_callback( $matches, $width, $height );
508
    }
509
510
    public function filter_optimize_images( $in )
511
    {
512
        /*
513
         * potential future functional improvements:
514
         *
515
         * filter for critical CSS.
516
         */
517
        $to_replace = array();
518
519
        // hide noscript tags to avoid nesting noscript tags (as lazyloaded images add noscript).
520
        if ( $this->should_lazyload() ) {
521
            $in = autoptimizeBase::replace_contents_with_marker_if_exists(
522
                'SCRIPT',
523
                '<script',
524
                '#<(?:no)?script.*?<\/(?:no)?script>#is',
525
                $in
526
            );
527
        }
528
529
        // extract img tags.
530
        if ( preg_match_all( '#<img[^>]*src[^>]*>#Usmi', $in, $matches ) ) {
531
            foreach ( $matches[0] as $tag ) {
532
                $orig_tag = $tag;
533
                $imgopt_w = '';
534
                $imgopt_h = '';
0 ignored issues
show
Unused Code introduced by
$imgopt_h is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
535
536
                // first do (data-)srcsets.
537
                if ( preg_match_all( '#srcset=("|\')(.*)("|\')#Usmi', $tag, $allsrcsets, PREG_SET_ORDER ) ) {
538
                    foreach ( $allsrcsets as $srcset ) {
539
                        $srcset      = $srcset[2];
540
                        $orig_srcset = $srcset;
541
                        $srcsets     = explode( ',', $srcset );
542
                        foreach ( $srcsets as $indiv_srcset ) {
543
                            $indiv_srcset_parts = explode( ' ', trim( $indiv_srcset ) );
544
                            if ( isset( $indiv_srcset_parts[1] ) && rtrim( $indiv_srcset_parts[1], 'w' ) !== $indiv_srcset_parts[1] ) {
545
                                $imgopt_w = rtrim( $indiv_srcset_parts[1], 'w' );
546
                            }
547
                            if ( $this->can_optimize_image( $indiv_srcset_parts[0], $tag ) ) {
548
                                $imgopt_url = $this->build_imgopt_url( $indiv_srcset_parts[0], $imgopt_w, '' );
549
                                $srcset     = str_replace( $indiv_srcset_parts[0], $imgopt_url, $srcset );
550
                            }
551
                        }
552
                        $tag = str_replace( $orig_srcset, $srcset, $tag );
553
                    }
554
                }
555
556
                // proceed with img src.
557
                // get width and height and add to $imgopt_size.
558
                $_get_size = $this->get_size_from_tag( $tag );
559
                $imgopt_w  = $_get_size['width'];
560
                $imgopt_h  = $_get_size['height'];
561
562
                // then start replacing images src.
563
                if ( preg_match_all( '#src=(?:"|\')(?!data)(.*)(?:"|\')#Usmi', $tag, $urls, PREG_SET_ORDER ) ) {
564
                    foreach ( $urls as $url ) {
565
                        $full_src_orig = $url[0];
566
                        $url           = $url[1];
567
                        if ( $this->can_optimize_image( $url, $full_src_orig ) ) {
568
                            $imgopt_url      = $this->build_imgopt_url( $url, $imgopt_w, $imgopt_h );
569
                            $full_imgopt_src = str_replace( $url, $imgopt_url, $full_src_orig );
570
                            $tag             = str_replace( $full_src_orig, $full_imgopt_src, $tag );
571
                        }
572
                    }
573
                }
574
575
                // do lazyload stuff.
576
                if ( $this->should_lazyload( $in ) && ! empty( $url ) ) {
577
                    // first do lpiq placeholder logic.
578
                    if ( strpos( $url, $this->get_imgopt_host() ) === 0 ) {
579
                        // if all img src have been replaced during srcset, we have to extract the
580
                        // origin url from the imgopt one to be able to set a lqip placeholder.
581
                        $_url = substr( $url, strpos( $url, '/http' ) + 1 );
582
                    } else {
583
                        $_url = $url;
584
                    }
585
586
                    $_url = $this->normalize_img_url( $_url );
587
588
                    $placeholder = '';
589
                    if ( $this->can_optimize_image( $_url, $tag ) && apply_filters( 'autoptimize_filter_imgopt_lazyload_dolqip', true ) ) {
590
                        $lqip_w = '';
591
                        $lqip_h = '';
592
                        if ( isset( $imgopt_w ) && ! empty( $imgopt_w ) ) {
593
                            $lqip_w = ',w_' . $imgopt_w;
594
                        }
595
                        if ( isset( $imgopt_h ) && ! empty( $imgopt_h ) ) {
596
                            $lqip_h = ',h_' . $imgopt_h;
597
                        }
598
                        $placeholder = $this->get_imgopt_host() . 'client/q_lqip,ret_wait' . $lqip_w . $lqip_h . '/' . $_url;
599
                    }
600
                    // then call add_lazyload-function with lpiq placeholder if set.
601
                    $tag = $this->add_lazyload( $tag, $placeholder );
602
                }
603
604
                // and add tag to array for later replacement.
605
                if ( $tag !== $orig_tag ) {
606
                    $to_replace[ $orig_tag ] = $tag;
607
                }
608
            }
609
        }
610
611
        // and replace all.
612
        $out = str_replace( array_keys( $to_replace ), array_values( $to_replace ), $in );
613
614
        // img thumbnails in e.g. woocommerce.
615
        if ( strpos( $out, 'data-thumb' ) !== false && apply_filters( 'autoptimize_filter_imgopt_datathumbs', true ) ) {
616
            $out = preg_replace_callback(
617
                '/\<div(?:[^>]?)\sdata-thumb\=(?:\"|\')(.+?)(?:\"|\')(?:[^>]*)?\>/s',
618
                array( $this, 'replace_data_thumbs' ),
619
                $out
620
            );
621
        }
622
623
        // background-image in inline style.
624 View Code Duplication
        if ( strpos( $out, 'background-image:' ) !== false && apply_filters( 'autoptimize_filter_imgopt_backgroundimages', true ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
625
            $out = preg_replace_callback(
626
                '/style=(?:"|\')[^<>]*?background-image:\s?url\((?:"|\')?([^"\')]*)(?:"|\')?\)/',
627
                array( $this, 'replace_img_callback' ),
628
                $out
629
            );
630
        }
631
632
        // act on icon links.
633
        if ( ( strpos( $out, '<link rel="icon"' ) !== false || ( strpos( $out, "<link rel='icon'" ) !== false ) ) && apply_filters( 'autoptimize_filter_imgopt_linkicon', true ) ) {
634
            $out = preg_replace_callback(
635
                '/<link\srel=(?:"|\')(?:apple-touch-)?icon(?:"|\').*\shref=(?:"|\')(.*)(?:"|\')(?:\ssizes=(?:"|\')(\d*x\d*)(?:"|\'))?\s\/>/Um',
636
                array( $this, 'replace_icon_callback' ),
637
                $out
638
            );
639
        }
640
641
        // lazyload: restore noscript tags + lazyload picture source tags and bgimage.
642
        if ( $this->should_lazyload() ) {
643
            $out = autoptimizeBase::restore_marked_content(
644
                'SCRIPT',
645
                $out
646
            );
647
648
            $out = $this->process_picture_tag( $out, true, true );
649
            $out = $this->process_bgimage( $out );
650
        } else {
651
            $out = $this->process_picture_tag( $out, true, false );
652
        }
653
654
        return $out;
655
    }
656
657
    public function get_size_from_tag( $tag ) {
658
        // reusable function to extract widht and height from an image tag
659
        // enforcing a filterable maximum width and height (default 4999X4999).
660
        $width  = '';
661
        $height = '';
662
663
        if ( preg_match( '#width=("|\')(.*)("|\')#Usmi', $tag, $_width ) ) {
664
            if ( strpos( $_width[2], '%' ) === false ) {
665
                $width = (int) $_width[2];
666
            }
667
        }
668
        if ( preg_match( '#height=("|\')(.*)("|\')#Usmi', $tag, $_height ) ) {
669
            if ( strpos( $_height[2], '%' ) === false ) {
670
                $height = (int) $_height[2];
671
            }
672
        }
673
674
        // check for and enforce (filterable) max sizes.
675
        $_max_width = apply_filters( 'autoptimize_filter_imgopt_max_width', 4999 );
676
        if ( $width > $_max_width ) {
677
            $_width = $_max_width;
678
            $height = $_width / $width * $height;
679
            $width  = $_width;
680
        }
681
        $_max_height = apply_filters( 'autoptimize_filter_imgopt_max_height', 4999 );
682
        if ( $height > $_max_height ) {
683
            $_height = $_max_height;
684
            $width   = $_height / $height * $width;
685
            $height  = $_height;
686
        }
687
688
        return array(
689
            'width'  => $width,
690
            'height' => $height,
691
        );
692
    }
693
694
    /**
695
     * Lazyload functions
696
     */
697
    public static function should_lazyload_wrapper() {
698
        // needed in autoptimizeMain.php.
699
        $self = new self();
700
        return $self->should_lazyload();
701
    }
702
703
    public function should_lazyload( $context = '' ) {
704
        if ( ! empty( $this->options['autoptimize_imgopt_checkbox_field_3'] ) && false === $this->check_nolazy() ) {
705
            $lazyload_return = true;
706
        } else {
707
            $lazyload_return = false;
708
        }
709
        $lazyload_return = apply_filters( 'autoptimize_filter_imgopt_should_lazyload', $lazyload_return, $context );
710
711
        return $lazyload_return;
712
    }
713
714
    public function check_nolazy() {
715
        if ( array_key_exists( 'ao_nolazy', $_GET ) && '1' === $_GET['ao_nolazy'] ) {
716
            return true;
717
        } else {
718
            return false;
719
        }
720
    }
721
722
    public function filter_lazyload_images( $in )
723
    {
724
        // only used is image optimization is NOT active but lazyload is.
725
        $to_replace = array();
726
727
        // hide (no)script tags to avoid nesting noscript tags (as lazyloaded images add noscript).
728
        $out = autoptimizeBase::replace_contents_with_marker_if_exists(
729
            'SCRIPT',
730
            '<script',
731
            '#<(?:no)?script.*?<\/(?:no)?script>#is',
732
            $in
733
        );
734
735
        // extract img tags and add lazyload attribs.
736
        if ( preg_match_all( '#<img[^>]*src[^>]*>#Usmi', $out, $matches ) ) {
737
            foreach ( $matches[0] as $tag ) {
738
                if ( $this->should_lazyload( $out ) ) {
739
                    $to_replace[ $tag ] = $this->add_lazyload( $tag );
740
                }
741
            }
742
            $out = str_replace( array_keys( $to_replace ), array_values( $to_replace ), $out );
743
        }
744
745
        // and also lazyload picture tag.
746
        $out = $this->process_picture_tag( $out, false, true );
747
748
        // and inline style blocks with background-image.
749
        $out = $this->process_bgimage( $out );
750
751
        // restore noscript tags.
752
        $out = autoptimizeBase::restore_marked_content(
753
            'SCRIPT',
754
            $out
755
        );
756
757
        return $out;
758
    }
759
760
    public function add_lazyload( $tag, $placeholder = '' ) {
761
        // adds actual lazyload-attributes to an image node.
762
        if ( str_ireplace( $this->get_lazyload_exclusions(), '', $tag ) === $tag ) {
763
            $tag = $this->maybe_fix_missing_quotes( $tag );
764
765
            // store original tag for use in noscript version.
766
            $noscript_tag = '<noscript>' . autoptimizeUtils::remove_id_from_node( $tag ) . '</noscript>';
767
768
            $lazyload_class = apply_filters( 'autoptimize_filter_imgopt_lazyload_class', 'lazyload' );
769
770
            // insert lazyload class.
771
            $tag = $this->inject_classes_in_tag( $tag, "$lazyload_class " );
772
773
            if ( ! $placeholder || empty( $placeholder ) ) {
774
                // get image width & heigth for placeholder fun (and to prevent content reflow).
775
                $_get_size = $this->get_size_from_tag( $tag );
776
                $width     = $_get_size['width'];
777
                $height    = $_get_size['height'];
778
                if ( false === $width || empty( $width ) ) {
779
                    $width = 210; // default width for SVG placeholder.
780
                }
781
                if ( false === $height || empty( $height ) ) {
782
                    $height = $width / 3 * 2; // if no height, base it on width using the 3/2 aspect ratio.
783
                }
784
785
                // insert the actual lazyload stuff.
786
                // see https://css-tricks.com/preventing-content-reflow-from-lazy-loaded-images/ for great read on why we're using empty svg's.
787
                $placeholder = apply_filters( 'autoptimize_filter_imgopt_lazyload_placeholder', $this->get_default_lazyload_placeholder( $width, $height ) );
788
            }
789
790
            $tag = preg_replace( '/(\s)src=/', ' src=\'' . $placeholder . '\' data-src=', $tag );
791
            $tag = preg_replace( '/(\s)srcset=/', ' data-srcset=', $tag );
792
793
            // move sizes to data-sizes unless filter says no.
794
            if ( apply_filters( 'autoptimize_filter_imgopt_lazyload_move_sizes', true ) ) {
795
                $tag = str_replace( ' sizes=', ' data-sizes=', $tag );
796
            }
797
798
            // add the noscript-tag from earlier.
799
            $tag = $noscript_tag . $tag;
800
            $tag = apply_filters( 'autoptimize_filter_imgopt_lazyloaded_img', $tag );
801
        }
802
803
        return $tag;
804
    }
805
806
    public function add_lazyload_js_footer() {
807
        if ( false === autoptimizeMain::should_buffer() ) {
808
            return;
809
        }
810
811
        // The JS will by default be excluded form autoptimization but this can be changed with a filter.
812
        $noptimize_flag = '';
813
        if ( apply_filters( 'autoptimize_filter_imgopt_lazyload_js_noptimize', true ) ) {
814
            $noptimize_flag = ' data-noptimize="1"';
815
        }
816
817
        $lazysizes_js = plugins_url( 'external/js/lazysizes.min.js?ao_version=' . AUTOPTIMIZE_PLUGIN_VERSION, __FILE__ );
818
        $cdn_url      = $this->get_cdn_url();
819
        if ( ! empty( $cdn_url ) ) {
820
            $cdn_url      = rtrim( $cdn_url, '/' );
821
            $lazysizes_js = str_replace( AUTOPTIMIZE_WP_SITE_URL, $cdn_url, $lazysizes_js );
822
        }
823
824
        $type_js = '';
825
        if ( apply_filters( 'autoptimize_filter_cssjs_addtype', false ) ) {
826
            $type_js = ' type="text/javascript"';
827
        }
828
829
        // Adds lazyload CSS & JS to footer, using echo because wp_enqueue_script seems not to support pushing attributes (async).
830
        echo apply_filters( 'autoptimize_filter_imgopt_lazyload_cssoutput', '<noscript><style>.lazyload{display:none;}</style></noscript>' );
831
        echo apply_filters( 'autoptimize_filter_imgopt_lazyload_jsconfig', '<script' . $type_js . $noptimize_flag . '>window.lazySizesConfig=window.lazySizesConfig||{};window.lazySizesConfig.loadMode=1;</script>' );
832
        echo apply_filters( 'autoptimize_filter_imgopt_lazyload_js', '<script async' . $type_js . $noptimize_flag . ' src=\'' . $lazysizes_js . '\'></script>' );
833
834
        // And add webp detection and loading JS.
835
        if ( $this->should_ngimg() ) {
836
            // Add AVIF code, can be disabled for now to only do webp.
837
            if ( apply_filters( 'autoptimize_filter_imgopt_do_avif', true ) ) {
838
                $_ngimg_detect = 'function c_img(a,b){src="avif"==b?"data:image/avif;base64,AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAABoAAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAAAamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAEAAAABAAAAEHBpeGkAAAAAAwgICAAAAAxhdjFDgQ0MAAAAABNjb2xybmNseAACAAIAAYAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAACJtZGF0EgAKCBgADsgQEAwgMgwf8AAAWAAAAACvJ+o=":"data:image/webp;base64,UklGRhoAAABXRUJQVlA4TA0AAAAvAAAAEAcQERGIiP4HAA==";var c=new Image;c.onload=function(){var d=0<c.width&&0<c.height;a(d,b)},c.onerror=function(){a(!1,b)},c.src=src}function s_img(a,b){w=window,"avif"==b?!1==a?c_img(s_img,"webp"):w.ngImg="avif":!1==a?w.ngImg=!1:w.ngImg="webp"}c_img(s_img,"avif");';
839
                $_ngimg_load   = 'document.addEventListener("lazybeforeunveil",function({target:a}){window.ngImg&&["data-src","data-srcset"].forEach(function(b){attr=a.getAttribute(b),null!==attr&&-1==attr.indexOf("/client/to_")&&a.setAttribute(b,attr.replace(/\/client\//,"/client/to_"+window.ngImg+","))})});';
840
            } else {
841
                $_ngimg_detect = "function c_webp(A){var n=new Image;n.onload=function(){var e=0<n.width&&0<n.height;A(e)},n.onerror=function(){A(!1)},n.src='data:image/webp;base64,UklGRhoAAABXRUJQVlA4TA0AAAAvAAAAEAcQERGIiP4HAA=='}function s_webp(e){window.supportsWebP=e}c_webp(s_webp);";
842
                $_ngimg_load   = "document.addEventListener('lazybeforeunveil',function({target:b}){window.supportsWebP&&['data-src','data-srcset'].forEach(function(c){attr=b.getAttribute(c),null!==attr&&-1==attr.indexOf('/client/to_webp')&&b.setAttribute(c,attr.replace(/\/client\//,'/client/to_webp,'))})});";
843
            }
844
            // Keeping autoptimize_filter_imgopt_webp_js filter for now, but it is deprecated as not only for webp any more.
845
            $_ngimg_output = apply_filters( 'autoptimize_filter_imgopt_webp_js', '<script' . $type_js . $noptimize_flag . '>' . $_ngimg_detect . $_ngimg_load . '</script>' );
846
            echo apply_filters( 'autoptimize_filter_imgopt_ngimg_js', $_ngimg_output );
847
        }
848
    }
849
850
    public function get_cdn_url() {
851
        // getting CDN url here to avoid having to make bigger changes to autoptimizeBase.
852
        static $cdn_url = null;
853
854
        if ( null === $cdn_url ) {
855
            $cdn_url = autoptimizeOptionWrapper::get_option( 'autoptimize_cdn_url', '' );
856
            $cdn_url = autoptimizeUtils::tweak_cdn_url_if_needed( $cdn_url );
857
            $cdn_url = apply_filters( 'autoptimize_filter_base_cdnurl', $cdn_url );
858
        }
859
860
        return $cdn_url;
861
    }
862
863
    public function get_lazyload_exclusions() {
864
        // returns array of strings that if found in an <img tag will stop the img from being lazy-loaded.
865
        static $exclude_lazyload_array = null;
866
867
        if ( null === $exclude_lazyload_array ) {
868
            $options = $this->options;
869
870
            // set default exclusions.
871
            $exclude_lazyload_array = array( 'skip-lazy', 'data-no-lazy', 'notlazy', 'data-src', 'data-srcset', 'data:image/', 'data-lazyload', 'rev-slidebg', 'loading="eager"' );
872
873
            // add from setting.
874
            if ( array_key_exists( 'autoptimize_imgopt_text_field_5', $options ) ) {
875
                $exclude_lazyload_option = $options['autoptimize_imgopt_text_field_5'];
876 View Code Duplication
                if ( ! empty( $exclude_lazyload_option ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
877
                    $exclude_lazyload_array = array_merge( $exclude_lazyload_array, array_filter( array_map( 'trim', explode( ',', $options['autoptimize_imgopt_text_field_5'] ) ) ) );
878
                }
879
            }
880
881
            // and filter for developer-initiated changes.
882
            $exclude_lazyload_array = apply_filters( 'autoptimize_filter_imgopt_lazyload_exclude_array', $exclude_lazyload_array );
883
        }
884
885
        return $exclude_lazyload_array;
886
    }
887
888
    public function inject_classes_in_tag( $tag, $target_class ) {
889
        if ( strpos( $tag, 'class=' ) !== false ) {
890
            $tag = preg_replace( '/(\sclass\s?=\s?("|\'))/', '$1' . $target_class, $tag );
891
        } else {
892
            $tag = preg_replace( '/(<img)\s/', '$1 class="' . trim( $target_class ) . '" ', $tag );
893
        }
894
895
        return $tag;
896
    }
897
898
    public function get_default_lazyload_placeholder( $imgopt_w, $imgopt_h ) {
899
        return 'data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%20' . $imgopt_w . '%20' . $imgopt_h . '%22%3E%3C/svg%3E';
900
    }
901
902
    public function should_ngimg() {
903
        static $ngimg_return = null;
904
905
        if ( is_null( $ngimg_return ) ) {
906
            // webp only works if imgopt and lazyload are also active.
907
            if ( ! empty( $this->options['autoptimize_imgopt_checkbox_field_4'] ) && ! empty( $this->options['autoptimize_imgopt_checkbox_field_3'] ) && $this->imgopt_active() ) {
908
                $ngimg_return = true;
909
            } else {
910
                $ngimg_return = false;
911
            }
912
        }
913
914
        return $ngimg_return;
915
    }
916
917
    public function process_picture_tag( $in, $imgopt = false, $lazy = false ) {
918
        // check if "<picture" is present and if filter allows us to process <picture>.
919
        if ( strpos( $in, '<picture' ) === false || apply_filters( 'autoptimize_filter_imgopt_dopicture', true ) === false ) {
920
            return $in;
921
        }
922
923
        $_exclusions     = $this->get_lazyload_exclusions();
924
        $to_replace_pict = array();
925
926
        // extract and process each picture-node.
927
        preg_match_all( '#<picture.*</picture>#Usmi', $in, $_pictures, PREG_SET_ORDER );
928
        foreach ( $_pictures as $_picture ) {
929
            $_picture = $this->maybe_fix_missing_quotes( $_picture );
930
            if ( strpos( $_picture[0], '<source ' ) !== false && preg_match_all( '#<source .*srcset=(?:"|\')(?!data)(.*)(?:"|\').*>#Usmi', $_picture[0], $_sources, PREG_SET_ORDER ) !== false ) {
931
                foreach ( $_sources as $_source ) {
932
                    $_picture_replacement = $_source[0];
933
934
                    // should we optimize the image?
935
                    if ( $imgopt && $this->can_optimize_image( $_source[1], $_picture[0] ) ) {
936
                        $_picture_replacement = str_replace( $_source[1], $this->build_imgopt_url( $_source[1] ), $_picture_replacement );
937
                    }
938
                    // should we lazy-load?
939
                    if ( $lazy && $this->should_lazyload() && str_ireplace( $_exclusions, '', $_picture_replacement ) === $_picture_replacement ) {
940
                        $_picture_replacement = str_replace( ' srcset=', ' data-srcset=', $_picture_replacement );
941
                    }
942
                    $to_replace_pict[ $_source[0] ] = $_picture_replacement;
943
                }
944
            }
945
        }
946
947
        // and return the fully procesed $in.
948
        $out = str_replace( array_keys( $to_replace_pict ), array_values( $to_replace_pict ), $in );
949
950
        return $out;
951
    }
952
953
    public function process_bgimage( $in ) {
954 View Code Duplication
        if ( strpos( $in, 'background-image:' ) !== false && apply_filters( 'autoptimize_filter_imgopt_lazyload_backgroundimages', true ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
955
            $out = preg_replace_callback(
956
                '/(<(?:article|aside|body|div|footer|header|p|section|table)[^>]*)\sstyle=(?:"|\')[^<>]*?background-image:\s?url\((?:"|\')?([^"\')]*)(?:"|\')?\)[^>]*/',
957
                array( $this, 'lazyload_bgimg_callback' ),
958
                $in
959
            );
960
            return $out;
961
        }
962
        return $in;
963
    }
964
965
    public function lazyload_bgimg_callback( $matches ) {
966
        if ( str_ireplace( $this->get_lazyload_exclusions(), '', $matches[0] ) === $matches[0] ) {
967
            // get placeholder & lazyload class strings.
968
            $placeholder    = apply_filters( 'autoptimize_filter_imgopt_lazyload_placeholder', $this->get_default_lazyload_placeholder( 500, 300 ) );
969
            $lazyload_class = apply_filters( 'autoptimize_filter_imgopt_lazyload_class', 'lazyload' );
970
            // replace background-image URL with SVG placeholder.
971
            $out = str_replace( $matches[2], $placeholder, $matches[0] );
972
            // add data-bg attribute with real background-image URL for lazyload to pick up.
973
            $out = str_replace( $matches[1], $matches[1] . ' data-bg="' . trim( str_replace( array( "\r\n", '&quot;' ), '', $matches[2] ) ) . '"', $out );
974
            // add lazyload class to tag.
975
            $out = $this->inject_classes_in_tag( $out, "$lazyload_class " );
976
            return $out;
977
        }
978
        return $matches[0];
979
    }
980
981
    public function maybe_fix_missing_quotes( $tag_in ) {
982
        // W3TC's Minify_HTML class removes quotes around attribute value, this re-adds them for the class and width/height attributes so we can lazyload properly.
983
        if ( file_exists( WP_PLUGIN_DIR . '/w3-total-cache/w3-total-cache.php' ) && class_exists( 'Minify_HTML' ) && apply_filters( 'autoptimize_filter_imgopt_fixquotes', true ) ) {
984
            $tag_out = preg_replace( '/class\s?=([^("|\')]*)(\s|>)/U', 'class=\'$1\'$2', $tag_in );
985
            $tag_out = preg_replace( '/\s(width|height)=(?:"|\')?([^\s"\'>]*)(?:"|\')?/', ' $1=\'$2\'', $tag_out );
986
            return $tag_out;
987
        } else {
988
            return $tag_in;
989
        }
990
    }
991
992
    /**
993
     * Admin page logic and related functions below.
994
     */
995
    public function imgopt_admin_menu()
996
    {
997
        // no acces if multisite and not network admin and no site config allowed.
998
        if ( autoptimizeConfig::should_show_menu_tabs() ) {
999
            add_submenu_page(
1000
                null,
1001
                'autoptimize_imgopt',
1002
                'autoptimize_imgopt',
1003
                'manage_options',
1004
                'autoptimize_imgopt',
1005
                array( $this, 'imgopt_options_page' )
1006
            );
1007
        }
1008
        register_setting( 'autoptimize_imgopt_settings', 'autoptimize_imgopt_settings' );
1009
    }
1010
1011
    public function add_imgopt_tab( $in )
1012
    {
1013
        if ( autoptimizeConfig::should_show_menu_tabs() ) {
1014
            $in = array_merge( $in, array( 'autoptimize_imgopt' => __( 'Images', 'autoptimize' ) ) );
1015
        }
1016
1017
        return $in;
1018
    }
1019
1020
    public function imgopt_options_page()
1021
    {
1022
        // Check querystring for "refreshCacheChecker" and call cachechecker if so.
1023
        if ( array_key_exists( 'refreshImgProvStats', $_GET ) && 1 == $_GET['refreshImgProvStats'] ) {
1024
            $this->query_img_provider_stats();
1025
        }
1026
1027
        $options       = $this->fetch_options();
1028
        $sp_url_suffix = $this->get_service_url_suffix();
1029
        ?>
1030
    <style>
1031
        #ao_settings_form {background: white;border: 1px solid #ccc;padding: 1px 15px;margin: 15px 10px 10px 0;}
1032
        #ao_settings_form .form-table th {font-weight: normal;}
1033
        #autoptimize_imgopt_descr{font-size: 120%;}
1034
    </style>
1035
    <script>document.title = "Autoptimize: <?php _e( 'Images', 'autoptimize' ); ?> " + document.title;</script>
1036
    <div class="wrap">
1037
    <h1><?php _e( 'Autoptimize Settings', 'autoptimize' ); ?></h1>
1038
        <?php echo autoptimizeConfig::ao_admin_tabs(); ?>
1039
        <?php if ( 'down' === $options['availabilities']['extra_imgopt']['status'] ) { ?>
1040
            <div class="notice-warning notice"><p>
1041
            <?php
1042
            // translators: "Autoptimize support forum" will appear in a "a href".
1043
            echo sprintf( __( 'The image optimization service is currently down, image optimization will be skipped until further notice. Check the %1$sAutoptimize support forum%2$s for more info.', 'autoptimize' ), '<a href="https://wordpress.org/support/plugin/autoptimize/" target="_blank">', '</a>' );
1044
            ?>
1045
            </p></div>
1046
        <?php } ?>
1047
1048
        <?php if ( 'launch' === $options['availabilities']['extra_imgopt']['status'] && ! autoptimizeImages::instance()->launch_ok() ) { ?>
0 ignored issues
show
Bug Best Practice introduced by
The expression \autoptimizeImages::instance()->launch_ok() of type null|boolean is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
1049
            <div class="notice-warning notice"><p>
1050
            <?php _e( 'The image optimization service is launching, but not yet available for this domain, it should become available in the next couple of days.', 'autoptimize' ); ?>
1051
            </p></div>
1052
        <?php } ?>
1053
1054
        <?php if ( class_exists( 'Jetpack' ) && method_exists( 'Jetpack', 'get_active_modules' ) && in_array( 'photon', Jetpack::get_active_modules() ) ) { ?>
1055
            <div class="notice-warning notice"><p>
1056
            <?php
1057
            // translators: "disable  Jetpack's site accelerator for images" will appear in a "a href" linking to the jetpack settings page.
1058
            echo sprintf( __( 'Please %1$sdisable Jetpack\'s site accelerator for images%2$s to be able to use Autoptomize\'s advanced image optimization features below.', 'autoptimize' ), '<a href="admin.php?page=jetpack#/settings">', '</a>' );
1059
            ?>
1060
            </p></div>
1061
        <?php } ?>
1062
    <form id='ao_settings_form' action='<?php echo admin_url( 'options.php' ); ?>' method='post'>
1063
        <?php settings_fields( 'autoptimize_imgopt_settings' ); ?>
1064
        <h2><?php _e( 'Image optimization', 'autoptimize' ); ?></h2>
1065
        <span id='autoptimize_imgopt_descr'><?php _e( 'Make your site significantly faster by just ticking a couple of checkboxes to optimize and lazy load your images, WebP and AVIF support included!', 'autoptimize' ); ?></span>
1066
        <table class="form-table">
1067
            <tr>
1068
                <th scope="row"><?php _e( 'Optimize Images', 'autoptimize' ); ?></th>
1069
                <td>
1070
                    <label><input id='autoptimize_imgopt_checkbox' type='checkbox' name='autoptimize_imgopt_settings[autoptimize_imgopt_checkbox_field_1]' <?php if ( ! empty( $options['autoptimize_imgopt_checkbox_field_1'] ) && '1' === $options['autoptimize_imgopt_checkbox_field_1'] ) { echo 'checked="checked"'; } ?> value='1'><?php _e( 'Optimize images on the fly and serve them from Shortpixel\'s global CDN.', 'autoptimize' ); ?></label>
1071
                    <?php
1072
                    // show shortpixel status.
1073
                    $_notice = autoptimizeImages::instance()->get_imgopt_status_notice();
1074
                    if ( $_notice ) {
1075
                        switch ( $_notice['status'] ) {
1076
                            case 2:
1077
                                $_notice_color = 'green';
1078
                                break;
1079
                            case 1:
1080
                                $_notice_color = 'orange';
1081
                                break;
1082
                            case -1:
1083
                            case -2:
1084
                            case -3:
1085
                                $_notice_color = 'red';
1086
                                break;
1087
                            default:
1088
                                $_notice_color = 'green';
1089
                        }
1090
                        echo apply_filters( 'autoptimize_filter_imgopt_settings_status', '<p><strong><span style="color:' . $_notice_color . ';">' . __( 'Shortpixel status: ', 'autoptimize' ) . '</span></strong>' . $_notice['notice'] . '</p>' );
1091
                    } else {
1092
                        // translators: link points to shortpixel.
1093
                        $upsell_msg_1 = '<p>' . sprintf( __( 'Get more Google love and improve your website\'s loading speed by having your publicly available images optimized on the fly (also in the "next-gen" WebP and AVIF image format) by %1$sShortPixel%2$s and then cached and served fast from Shortpixel\'s global CDN.', 'autoptimize' ), '<a href="https://shortpixel.com/aospai' . $sp_url_suffix . '" target="_blank">', '</a>' );
1094
                        if ( 'launch' === $options['availabilities']['extra_imgopt']['status'] ) {
1095
                            $upsell_msg_2 = __( 'For a limited time only, this service is offered free for all Autoptimize users, <b>don\'t miss the chance to test it</b> and see how much it could improve your site\'s speed.', 'autoptimize' );
1096
                        } else {
1097
                            // translators: link points to shortpixel.
1098
                            $upsell_msg_2 = sprintf( __( '%1$sSign-up now%2$s to receive extra traffic or image optimization credits for free. You\'ll also receive +50&percnt; more CDN traffic or image optimization credits regardless for any future plan that you\'ll choose to purchase.', 'autoptimize' ), '<a href="https://shortpixel.com/aospai' . $sp_url_suffix . '" target="_blank">', '</a>' );
1099
                        }
1100
                        echo apply_filters( 'autoptimize_imgopt_imgopt_settings_copy', $upsell_msg_1 . ' ' . $upsell_msg_2 . '</p>' );
1101
                    }
1102
                    // translators: link points to shortpixel FAQ.
1103
                    $faqcopy = sprintf( __( '<strong>Questions</strong>? Have a look at the %1$sShortPixel FAQ%2$s!', 'autoptimize' ), '<strong><a href="https://help.shortpixel.com/category/405-autoptimize" target="_blank">', '</strong></a>' );
1104
                    $faqcopy = $faqcopy . ' ' . __( 'Only works for sites/ images that are publicly available.', 'autoptimize' );
1105
                    // translators: links points to shortpixel TOS & Privacy Policy.
1106
                    $toscopy = sprintf( __( 'Usage of this feature is subject to Shortpixel\'s %1$sTerms of Use%2$s and %3$sPrivacy policy%4$s.', 'autoptimize' ), '<a href="https://shortpixel.com/tos' . $sp_url_suffix . '" target="_blank">', '</a>', '<a href="https://shortpixel.com/pp' . $sp_url_suffix . '" target="_blank">', '</a>' );
1107
                    echo apply_filters( 'autoptimize_imgopt_imgopt_settings_tos', '<p>' . $faqcopy . ' ' . $toscopy . '</p>' );
1108
                    ?>
1109
                </td>
1110
            </tr>
1111 View Code Duplication
            <tr id='autoptimize_imgopt_quality' <?php if ( ! array_key_exists( 'autoptimize_imgopt_checkbox_field_1', $options ) || ( isset( $options['autoptimize_imgopt_checkbox_field_1'] ) && '1' !== $options['autoptimize_imgopt_checkbox_field_1'] ) ) { echo 'class="hidden"'; } ?>>
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1112
                <th scope="row"><?php _e( 'Image Optimization quality', 'autoptimize' ); ?></th>
1113
                <td>
1114
                    <label>
1115
                    <select name='autoptimize_imgopt_settings[autoptimize_imgopt_select_field_2]'>
1116
                        <?php
1117
                        $_imgopt_array = autoptimizeImages::instance()->get_img_quality_array();
1118
                        $_imgopt_val   = autoptimizeImages::instance()->get_img_quality_setting();
1119
1120
                        foreach ( $_imgopt_array as $key => $value ) {
1121
                            echo '<option value="' . $key . '"';
1122
                            if ( $_imgopt_val == $key ) {
1123
                                echo ' selected';
1124
                            }
1125
                            echo '>' . ucfirst( $value ) . '</option>';
1126
                        }
1127
                        echo "\n";
1128
                        ?>
1129
                    </select>
1130
                    </label>
1131
                    <p>
1132
                        <?php
1133
                            // translators: link points to shortpixel image test page.
1134
                            echo apply_filters( 'autoptimize_imgopt_imgopt_quality_copy', sprintf( __( 'You can %1$stest compression levels here%2$s.', 'autoptimize' ), '<a href="https://shortpixel.com/oic' . $sp_url_suffix . '" target="_blank">', '</a>' ) );
1135
                        ?>
1136
                    </p>
1137
                </td>
1138
            </tr>
1139 View Code Duplication
            <tr id='autoptimize_imgopt_ngimg' <?php if ( ! array_key_exists( 'autoptimize_imgopt_checkbox_field_1', $options ) || ( isset( $options['autoptimize_imgopt_checkbox_field_1'] ) && '1' !== $options['autoptimize_imgopt_checkbox_field_1'] ) ) { echo 'class="hidden"'; } ?>>
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1140
                <th scope="row"><?php _e( 'Load WebP or AVIF in supported browsers?', 'autoptimize' ); ?></th>
1141
                <td>
1142
                    <label><input type='checkbox' id='autoptimize_imgopt_ngimg_checkbox' name='autoptimize_imgopt_settings[autoptimize_imgopt_checkbox_field_4]' <?php if ( ! empty( $options['autoptimize_imgopt_checkbox_field_4'] ) && '1' === $options['autoptimize_imgopt_checkbox_field_3'] ) { echo 'checked="checked"'; } ?> value='1'><?php _e( 'Automatically serve "next-gen" WebP or AVIF image formats to any browser that supports it (requires lazy load to be active).', 'autoptimize' ); ?></label>
1143
                </td>
1144
            </tr>
1145
            <tr>
1146
                <th scope="row"><?php _e( 'Lazy-load images?', 'autoptimize' ); ?></th>
1147
                <td>
1148
                    <label><input type='checkbox' id='autoptimize_imgopt_lazyload_checkbox' name='autoptimize_imgopt_settings[autoptimize_imgopt_checkbox_field_3]' <?php if ( ! empty( $options['autoptimize_imgopt_checkbox_field_3'] ) && '1' === $options['autoptimize_imgopt_checkbox_field_3'] ) { echo 'checked="checked"'; } ?> value='1'><?php _e( 'Image lazy-loading will delay the loading of non-visible images to allow the browser to optimally load all resources for the "above the fold"-page first.', 'autoptimize' ); ?></label>
1149
                </td>
1150
            </tr>
1151 View Code Duplication
            <tr id='autoptimize_imgopt_lazyload_exclusions' <?php if ( ! array_key_exists( 'autoptimize_imgopt_checkbox_field_3', $options ) || ( isset( $options['autoptimize_imgopt_checkbox_field_3'] ) && '1' !== $options['autoptimize_imgopt_checkbox_field_3'] ) ) { echo 'class="hidden"'; } ?>>
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1152
                <th scope="row"><?php _e( 'Lazy-load exclusions', 'autoptimize' ); ?></th>
1153
                <td>
1154
                    <label><input type='text' style='width:80%' id='autoptimize_imgopt_lazyload_exclusions' name='autoptimize_imgopt_settings[autoptimize_imgopt_text_field_5]' value='<?php if ( ! empty( $options['autoptimize_imgopt_text_field_5'] ) ) { echo esc_attr( $options['autoptimize_imgopt_text_field_5'] ); } ?>'><br /><?php _e( 'Comma-separated list of to be excluded image classes or filenames.', 'autoptimize' ); ?></label>
1155
                </td>
1156
            </tr>
1157
        </table>
1158
        <p class="submit"><input type="submit" name="submit" id="submit" class="button button-primary" value="<?php _e( 'Save Changes', 'autoptimize' ); ?>" /></p>
1159
    </form>
1160
    <script>
1161
        jQuery(document).ready(function() {
1162
            jQuery("#autoptimize_imgopt_checkbox").change(function() {
1163
                if (this.checked) {
1164
                    jQuery("#autoptimize_imgopt_quality").show("slow");
1165
                    jQuery("#autoptimize_imgopt_ngimg").show("slow");
1166
                } else {
1167
                    jQuery("#autoptimize_imgopt_quality").hide("slow");
1168
                    jQuery("#autoptimize_imgopt_ngimg").hide("slow");
1169
                }
1170
            });
1171
            jQuery("#autoptimize_imgopt_ngimg_checkbox").change(function() {
1172
                if (this.checked) {
1173
                    jQuery("#autoptimize_imgopt_lazyload_checkbox")[0].checked = true;
1174
                    jQuery("#autoptimize_imgopt_lazyload_exclusions").show("slow");
1175
                }
1176
            });
1177
            jQuery("#autoptimize_imgopt_lazyload_checkbox").change(function() {
1178
                if (this.checked) {
1179
                    jQuery("#autoptimize_imgopt_lazyload_exclusions").show("slow");
1180
                } else {
1181
                    jQuery("#autoptimize_imgopt_lazyload_exclusions").hide("slow");
1182
                    jQuery("#autoptimize_imgopt_ngimg_checkbox")[0].checked = false;
1183
                }
1184
            });
1185
        });
1186
    </script>
1187
        <?php
1188
    }
1189
1190
    /**
1191
     * Ïmg opt status as used on dashboard.
1192
     */
1193
    public function get_imgopt_status_notice() {
1194
        if ( $this->imgopt_active() ) {
1195
            $_imgopt_notice  = '';
0 ignored issues
show
Unused Code introduced by
$_imgopt_notice is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1196
            $_stat           = autoptimizeOptionWrapper::get_option( 'autoptimize_imgopt_provider_stat', '' );
1197
            $_site_host      = AUTOPTIMIZE_SITE_DOMAIN;
1198
            $_imgopt_upsell  = 'https://shortpixel.com/aospai/af/GWRGFLW109483/' . $_site_host;
1199
            $_imgopt_assoc   = 'https://shortpixel.helpscoutdocs.com/article/94-how-to-associate-a-domain-to-my-account';
1200
            $_imgopt_unreach = 'https://shortpixel.helpscoutdocs.com/article/148-why-are-my-images-redirected-from-cdn-shortpixel-ai';
1201
1202
            if ( is_array( $_stat ) ) {
1203
                if ( 1 == $_stat['Status'] ) {
1204
                    // translators: "add more credits" will appear in a "a href".
1205
                    $_imgopt_notice = sprintf( __( 'Your ShortPixel image optimization and CDN quota is almost used, make sure you %1$sadd more credits%2$s to avoid slowing down your website.', 'autoptimize' ), '<a href="' . $_imgopt_upsell . '" target="_blank">', '</a>' );
1206
                } elseif ( -1 == $_stat['Status'] || -2 == $_stat['Status'] ) {
1207
                    // translators: "add more credits" will appear in a "a href".
1208
                    $_imgopt_notice = sprintf( __( 'Your ShortPixel image optimization and CDN quota was used, %1$sadd more credits%2$s to keep fast serving optimized images on your site', 'autoptimize' ), '<a href="' . $_imgopt_upsell . '" target="_blank">', '</a>' );
1209
                    // translators: "associate your domain" will appear in a "a href".
1210
                    $_imgopt_notice = $_imgopt_notice . ' ' . sprintf( __( 'If you have enough credits/ CDN quota remaining, then you may need to %1$sassociate your domain%2$s to your Shortpixel account.', 'autoptimize' ), '<a rel="noopener noreferrer" href="' . $_imgopt_assoc . '" target="_blank">', '</a>' );
1211
                } elseif ( -3 == $_stat['Status'] ) {
1212
                    // translators: "check the documentation here" will appear in a "a href".
1213
                    $_imgopt_notice = sprintf( __( 'It seems ShortPixel image optimization is not able to fetch images from your site, %1$scheck the documentation here%2$s for more information', 'autoptimize' ), '<a href="' . $_imgopt_unreach . '" target="_blank">', '</a>' );
1214
                } else {
1215
                    $_imgopt_upsell = 'https://shortpixel.com/g/af/GWRGFLW109483';
1216
                    // translators: "log in to check your account" will appear in a "a href".
1217
                    $_imgopt_notice = sprintf( __( 'Your ShortPixel image optimization and CDN quota are in good shape, %1$slog in to check your account%2$s.', 'autoptimize' ), '<a href="' . $_imgopt_upsell . '" target="_blank">', '</a>' );
1218
                }
1219
1220
                // add info on freshness + refresh link if status is not 2 (good shape).
1221
                if ( 2 != $_stat['Status'] ) {
1222
                    $_imgopt_stats_refresh_url = add_query_arg( array(
1223
                        'page'                => 'autoptimize_imgopt',
1224
                        'refreshImgProvStats' => '1',
1225
                    ), admin_url( 'options-general.php' ) );
1226
                    if ( $_stat && array_key_exists( 'timestamp', $_stat ) && ! empty( $_stat['timestamp'] ) ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $_stat of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1227
                        $_imgopt_stats_last_run = __( 'based on status at ', 'autoptimize' ) . date_i18n( autoptimizeOptionWrapper::get_option( 'time_format' ), $_stat['timestamp'] );
1228
                    } else {
1229
                        $_imgopt_stats_last_run = __( 'based on previously fetched data', 'autoptimize' );
1230
                    }
1231
                    $_imgopt_notice .= ' (' . $_imgopt_stats_last_run . ', ';
1232
                    // translators: "here to refresh" links to the Autoptimize Extra page and forces a refresh of the img opt stats.
1233
                    $_imgopt_notice .= sprintf( __( 'click %1$shere to refresh%2$s', 'autoptimize' ), '<a href="' . $_imgopt_stats_refresh_url . '">', '</a>).' );
1234
                }
1235
1236
                // and make the full notice filterable.
1237
                $_imgopt_notice = apply_filters( 'autoptimize_filter_imgopt_notice', $_imgopt_notice );
1238
1239
                return array(
1240
                    'status' => $_stat['Status'],
1241
                    'notice' => $_imgopt_notice,
1242
                );
1243
            }
1244
        }
1245
        return false;
1246
    }
1247
1248
    public static function get_imgopt_status_notice_wrapper() {
1249
        // needed for notice being shown in autoptimizeCacheChecker.php.
1250
        $self = new self();
1251
        return $self->get_imgopt_status_notice();
1252
    }
1253
1254
    /**
1255
     * Get img provider stats (used to display notice).
1256
     */
1257
    public function query_img_provider_stats() {
1258
        if ( ! empty( $this->options['autoptimize_imgopt_checkbox_field_1'] ) ) {
1259
            $url      = '';
1260
            $endpoint = $this->get_imgopt_host() . 'read-domain/';
1261
            $domain   = AUTOPTIMIZE_SITE_DOMAIN;
1262
1263
            // make sure parse_url result makes sense, keeping $url empty if not.
1264
            if ( $domain && ! empty( $domain ) ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $domain of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1265
                $url = $endpoint . $domain;
1266
            }
1267
1268
            $url = apply_filters(
1269
                'autoptimize_filter_imgopt_stat_url',
1270
                $url
1271
            );
1272
1273
            // only do the remote call if $url is not empty to make sure no parse_url
1274
            // weirdness results in useless calls.
1275 View Code Duplication
            if ( ! empty( $url ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1276
                $response = wp_remote_get( $url );
1277
                if ( ! is_wp_error( $response ) ) {
1278
                    if ( '200' == wp_remote_retrieve_response_code( $response ) ) {
1279
                        $stats = json_decode( wp_remote_retrieve_body( $response ), true );
1280
                        autoptimizeOptionWrapper::update_option( 'autoptimize_imgopt_provider_stat', $stats );
1281
                    }
1282
                }
1283
            }
1284
        }
1285
    }
1286
1287
    public static function get_img_provider_stats()
1288
    {
1289
        // wrapper around query_img_provider_stats() so we can get to $this->options from cronjob() in autoptimizeCacheChecker.
1290
        $self = new self();
1291
        return $self->query_img_provider_stats();
1292
    }
1293
1294
    /**
1295
     * Determines and returns the service launch status.
1296
     *
1297
     * @return bool
1298
     */
1299
    public function launch_ok()
1300
    {
1301
        static $launch_status = null;
1302
1303
        if ( null === $launch_status ) {
1304
            $avail_imgopt  = $this->options['availabilities']['extra_imgopt'];
1305
            $magic_number  = intval( substr( md5( parse_url( AUTOPTIMIZE_WP_SITE_URL, PHP_URL_HOST ) ), 0, 3 ), 16 );
1306
            $has_launched  = autoptimizeOptionWrapper::get_option( 'autoptimize_imgopt_launched', '' );
1307
            $launch_status = false;
1308
            if ( $has_launched || ( is_array( $avail_imgopt ) && array_key_exists( 'launch-threshold', $avail_imgopt ) && $magic_number < $avail_imgopt['launch-threshold'] ) ) {
1309
                $launch_status = true;
1310
                if ( ! $has_launched ) {
1311
                    autoptimizeOptionWrapper::update_option( 'autoptimize_imgopt_launched', 'on' );
1312
                }
1313
            }
1314
        }
1315
1316
        return $launch_status;
1317
    }
1318
1319
    public static function launch_ok_wrapper() {
1320
        // needed for "plug" notice in autoptimizeMain.php.
1321
        $self = new self();
1322
        return $self->launch_ok();
1323
    }
1324
1325
    public function get_imgopt_provider_userstatus() {
1326
        static $_provider_userstatus = null;
1327
1328
        if ( is_null( $_provider_userstatus ) ) {
1329
            $_stat = autoptimizeOptionWrapper::get_option( 'autoptimize_imgopt_provider_stat', '' );
1330
            if ( is_array( $_stat ) ) {
1331
                if ( array_key_exists( 'Status', $_stat ) ) {
1332
                    $_provider_userstatus['Status'] = $_stat['Status'];
1333
                } else {
1334
                    // if no stats then we assume all is well.
1335
                    $_provider_userstatus['Status'] = 2;
1336
                }
1337
                if ( array_key_exists( 'timestamp', $_stat ) ) {
1338
                    $_provider_userstatus['timestamp'] = $_stat['timestamp'];
1339
                } else {
1340
                    // if no timestamp then we return "".
1341
                    $_provider_userstatus['timestamp'] = '';
1342
                }
1343
            } else {
1344
                // no provider_stat yet, assume/ return all OK.
1345
                $_provider_userstatus['Status']    = 2;
1346
                $_provider_userstatus['timestamp'] = '';
1347
            }
1348
        }
1349
1350
        return $_provider_userstatus;
1351
    }
1352
}
1353