Completed
Push — master ( 267ab8...da5a66 )
by frank
02:00
created

autoptimizeImages   F

Complexity

Total Complexity 282

Size/Duplication

Total Lines 1362
Duplicated Lines 3.23 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 0
Metric Value
dl 44
loc 1362
rs 0.8
c 0
b 0
f 0
wmc 282
lcom 1
cbo 5

50 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 10 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
D can_optimize_image() 0 42 19
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 18 3
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 46 10
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() 4 177 38
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
        $this->lazyload_counter = 0;
0 ignored issues
show
Bug introduced by
The property lazyload_counter does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
35
    }
36
37
    public function set_options( array $options )
38
    {
39
        $this->options = $options;
40
41
        return $this;
42
    }
43
44
    public static function fetch_options()
45
    {
46
        $value = autoptimizeOptionWrapper::get_option( 'autoptimize_imgopt_settings' );
47
        if ( empty( $value ) ) {
48
            // Fallback to returning defaults when no stored option exists yet.
49
            $value = autoptimizeConfig::get_ao_imgopt_default_options();
50
        }
51
52
        // get service availability and add it to the options-array.
53
        $value['availabilities'] = autoptimizeOptionWrapper::get_option( 'autoptimize_service_availablity' );
54
55
        if ( empty( $value['availabilities'] ) ) {
56
            $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...
57
        }
58
59
        return $value;
60
    }
61
62
    public static function imgopt_active()
63
    {
64
        // function to quickly check if imgopt is active, used below but also in
65
        // autoptimizeMain.php to start ob_ even if no HTML, JS or CSS optimizing is done
66
        // and does not use/ request the availablity data (which could slow things down).
67
        static $imgopt_active = null;
68
69
        if ( null === $imgopt_active ) {
70
            $opts = autoptimizeOptionWrapper::get_option( 'autoptimize_imgopt_settings', '' );
71
            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'] ) {
72
                $imgopt_active = true;
73
            } else {
74
                $imgopt_active = false;
75
            }
76
        }
77
78
        return $imgopt_active;
79
    }
80
81
    /**
82
     * Helper for getting a singleton instance. While being an
83
     * anti-pattern generally, it comes in handy for now from a
84
     * readability/maintainability perspective, until we get some
85
     * proper dependency injection going.
86
     *
87
     * @return self
88
     */
89
    public static function instance()
90
    {
91
        if ( null === self::$instance ) {
92
            self::$instance = new self();
93
        }
94
95
        return self::$instance;
96
    }
97
98 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...
99
    {
100
        if ( is_admin() ) {
101
            if ( is_multisite() && is_network_admin() && autoptimizeOptionWrapper::is_ao_active_for_network() ) {
102
                add_action( 'network_admin_menu', array( $this, 'imgopt_admin_menu' ) );
103
            } else {
104
                add_action( 'admin_menu', array( $this, 'imgopt_admin_menu' ) );
105
            }
106
            add_filter( 'autoptimize_filter_settingsscreen_tabs', array( $this, 'add_imgopt_tab' ), 9 );
107
        } else {
108
            add_action( 'wp', array( $this, 'run_on_frontend' ) );
109
        }
110
    }
111
112
    public function run_on_frontend() {
113
        if ( ! $this->should_run() ) {
114
            if ( $this->should_lazyload() ) {
115
                add_filter(
116
                    'wp_lazy_loading_enabled',
117
                    '__return_false'
118
                );
119
                add_filter(
120
                    'autoptimize_html_after_minify',
121
                    array( $this, 'filter_lazyload_images' ),
122
                    10,
123
                    1
124
                );
125
                add_action(
126
                    'wp_footer',
127
                    array( $this, 'add_lazyload_js_footer' ),
128
                    10,
129
                    0
130
                );
131
            }
132
            return;
133
        }
134
135
        $active = false;
136
137
        if ( apply_filters( 'autoptimize_filter_imgopt_do', true ) ) {
138
            add_filter(
139
                'autoptimize_html_after_minify',
140
                array( $this, 'filter_optimize_images' ),
141
                10,
142
                1
143
            );
144
            $active = true;
145
        }
146
147
        if ( apply_filters( 'autoptimize_filter_imgopt_do_css', true ) ) {
148
            add_filter(
149
                'autoptimize_filter_base_replace_cdn',
150
                array( $this, 'filter_optimize_css_images' ),
151
                10,
152
                1
153
            );
154
            $active = true;
155
        }
156
157
        if ( $active ) {
158
            add_filter(
159
                'autoptimize_extra_filter_tobepreconn',
160
                array( $this, 'filter_preconnect_imgopt_url' ),
161
                10,
162
                1
163
            );
164
        }
165
166
        if ( $this->should_lazyload() ) {
167
            add_filter(
168
                'wp_lazy_loading_enabled',
169
                '__return_false'
170
            );
171
            add_action(
172
                'wp_footer',
173
                array( $this, 'add_lazyload_js_footer' ),
174
                10,
175
                0
176
            );
177
        }
178
    }
179
180
    /**
181
     * Basic checks before we can run.
182
     *
183
     * @return bool
184
     */
185
    protected function should_run()
186
    {
187
        $opts              = $this->options;
188
        $service_not_down  = ( 'down' !== $opts['availabilities']['extra_imgopt']['status'] );
189
        $not_launch_status = ( 'launch' !== $opts['availabilities']['extra_imgopt']['status'] );
190
191
        $do_cdn      = true;
192
        $_userstatus = $this->get_imgopt_provider_userstatus();
193
        if ( isset( $_userstatus['Status'] ) && ( -2 == $_userstatus['Status'] || -3 == $_userstatus['Status'] ) ) {
194
            // don't even attempt to put images on CDN if heavily exceeded threshold or if site not reachable.
195
            $do_cdn = false;
196
        }
197
198
        if (
199
            $this->imgopt_active()
200
            && $do_cdn
201
            && $service_not_down
202
            && ( $not_launch_status || $this->launch_ok() )
203
        ) {
204
            return true;
205
        }
206
        return false;
207
    }
208
209
    public function get_imgopt_host()
210
    {
211
        static $imgopt_host = null;
212
213
        if ( null === $imgopt_host ) {
214
            $imgopt_host  = 'https://cdn.shortpixel.ai/';
215
            $avail_imgopt = $this->options['availabilities']['extra_imgopt'];
216
            if ( ! empty( $avail_imgopt ) && array_key_exists( 'hosts', $avail_imgopt ) && is_array( $avail_imgopt['hosts'] ) ) {
217
                $imgopt_host = array_rand( array_flip( $avail_imgopt['hosts'] ) );
218
            }
219
            $imgopt_host = apply_filters( 'autoptimize_filter_imgopt_host', $imgopt_host );
220
        }
221
222
        return $imgopt_host;
223
    }
224
225
    public static function get_imgopt_host_wrapper()
226
    {
227
        // needed for CI tests.
228
        $self = new self();
229
        return $self->get_imgopt_host();
230
    }
231
232
    public static function get_service_url_suffix()
233
    {
234
        $suffix = '/af/GWRGFLW109483/' . AUTOPTIMIZE_SITE_DOMAIN;
235
236
        return $suffix;
237
    }
238
239
    public function get_img_quality_string()
240
    {
241
        static $quality = null;
242
243
        if ( null === $quality ) {
244
            $q_array = $this->get_img_quality_array();
245
            $setting = $this->get_img_quality_setting();
246
            $quality = apply_filters(
247
                'autoptimize_filter_imgopt_quality',
248
                'q_' . $q_array[ $setting ]
249
            );
250
        }
251
252
        return $quality;
253
    }
254
255
    public function get_img_quality_array()
256
    {
257
        static $map = null;
258
259
        if ( null === $map ) {
260
            $map = array(
261
                '1' => 'lossy',
262
                '2' => 'glossy',
263
                '3' => 'lossless',
264
            );
265
            $map = apply_filters(
266
                'autoptimize_filter_imgopt_quality_array',
267
                $map
268
            );
269
        }
270
271
        return $map;
272
    }
273
274
    public function get_img_quality_setting()
275
    {
276
        static $q = null;
277
278
        if ( null === $q ) {
279
            if ( is_array( $this->options ) && array_key_exists( 'autoptimize_imgopt_select_field_2', $this->options ) ) {
280
                $setting = $this->options['autoptimize_imgopt_select_field_2'];
281
            }
282
283
            if ( ! isset( $setting ) || empty( $setting ) || ( '1' !== $setting && '3' !== $setting ) ) {
284
                // default image opt. value is 2 ("glossy").
285
                $q = '2';
286
            } else {
287
                $q = $setting;
288
            }
289
        }
290
291
        return $q;
292
    }
293
294
    public function filter_preconnect_imgopt_url( array $in )
295
    {
296
        $url_parts = parse_url( $this->get_imgopt_base_url() );
297
        $in[]      = $url_parts['scheme'] . '://' . $url_parts['host'];
298
299
        return $in;
300
    }
301
302
    /**
303
     * Makes sure given url contains the full scheme and hostname
304
     * in case they're not present already.
305
     *
306
     * @param string $in Image url to normalize.
307
     *
308
     * @return string
309
     */
310
    private function normalize_img_url( $in )
311
    {
312
        // Only parse the site url once.
313
        static $parsed_site_url = null;
314
        if ( null === $parsed_site_url ) {
315
            $parsed_site_url = parse_url( site_url() );
316
        }
317
318
        // get CDN domain once.
319
        static $cdn_domain = null;
320
        if ( is_null( $cdn_domain ) ) {
321
            $cdn_url = $this->get_cdn_url();
322
            if ( ! empty( $cdn_url ) ) {
323
                $cdn_domain = parse_url( $cdn_url, PHP_URL_HOST );
324
            } else {
325
                $cdn_domain = '';
326
            }
327
        }
328
329
        /**
330
         * This method gets called a lot, often for identical urls it seems.
331
         * `filter_optimize_css_images()` calls us, uses the resulting url and
332
         * gives it to `can_optimize_image()`, and if that returns trueish
333
         * then `build_imgopt_url()` is called (which, again, calls this method).
334
         * Until we dig deeper into whether this all must really happen that
335
         * way, having an internal cache here helps (to avoid doing repeated
336
         * identical string operations).
337
         */
338
        static $cache = null;
339
        if ( null === $cache ) {
340
            $cache = array();
341
        }
342
343
        // Do the work on cache miss only.
344
        if ( ! isset( $cache[ $in ] ) ) {
345
            // Default to (the trimmed version of) what was given to us.
346
            $result = trim( $in );
347
348
            // Some silly plugins wrap background images in html-encoded quotes, so remove those from the img url.
349
            if ( strpos( $result, '&quot;' ) !== false ) {
350
                $result = str_replace( '&quot;', '', $result );
351
            }
352
353
            if ( autoptimizeUtils::is_protocol_relative( $result ) ) {
354
                $result = $parsed_site_url['scheme'] . ':' . $result;
355
            } elseif ( 0 === strpos( $result, '/' ) ) {
356
                // Root-relative...
357
                $result = $parsed_site_url['scheme'] . '://' . $parsed_site_url['host'] . $result;
358
            } elseif ( ! empty( $cdn_domain ) && strpos( $result, $cdn_domain ) !== 0 ) {
359
                $result = str_replace( $cdn_domain, $parsed_site_url['host'], $result );
360
            }
361
362
            // filter (default off) to remove QS from image URL's to avoid eating away optimization credits.
363
            if ( apply_filters( 'autoptimize_filter_imgopt_no_querystring', false ) && strpos( $result, '?' ) !== false ) {
364
                $result = strtok( $result, '?' );
365
            }
366
367
            $result = apply_filters( 'autoptimize_filter_imgopt_normalized_url', $result );
368
369
            // Store in cache.
370
            $cache[ $in ] = $result;
371
        }
372
373
        return $cache[ $in ];
374
    }
375
376
    public function filter_optimize_css_images( $in )
377
    {
378
        $in = $this->normalize_img_url( $in );
379
380
        if ( $this->can_optimize_image( $in ) ) {
381
            return $this->build_imgopt_url( $in, '', '' );
382
        } else {
383
            return $in;
384
        }
385
    }
386
387
    private function get_imgopt_base_url()
388
    {
389
        static $imgopt_base_url = null;
390
391
        if ( null === $imgopt_base_url ) {
392
            $imgopt_host     = $this->get_imgopt_host();
393
            $quality         = $this->get_img_quality_string();
394
            $ret_val         = apply_filters( 'autoptimize_filter_imgopt_wait', 'ret_img' ); // values: ret_wait, ret_img, ret_json, ret_blank.
395
            $imgopt_base_url = $imgopt_host . 'client/' . $quality . ',' . $ret_val;
396
            $imgopt_base_url = apply_filters( 'autoptimize_filter_imgopt_base_url', $imgopt_base_url );
397
        }
398
399
        return $imgopt_base_url;
400
    }
401
402
    private function can_optimize_image( $url, $tag = '' )
403
    {
404
        static $cdn_url      = null;
405
        static $nopti_images = null;
406
407
        if ( null === $cdn_url ) {
408
            $cdn_url = apply_filters(
409
                'autoptimize_filter_base_cdnurl',
410
                autoptimizeOptionWrapper::get_option( 'autoptimize_cdn_url', '' )
411
            );
412
        }
413
414
        if ( null === $nopti_images ) {
415
            if ( is_array( $this->options ) && array_key_exists( 'autoptimize_imgopt_text_field_6', $this->options ) ) {
416
                $nopti_images = $this->options['autoptimize_imgopt_text_field_6'];
417
            }
418
            $nopti_images = apply_filters( 'autoptimize_filter_imgopt_noptimize', $nopti_images );
419
        }
420
421
        $site_host  = AUTOPTIMIZE_SITE_DOMAIN;
422
        $url        = $this->normalize_img_url( $url );
423
        $url_parsed = parse_url( $url );
424
425
        if ( array_key_exists( 'host', $url_parsed ) && $url_parsed['host'] !== $site_host && empty( $cdn_url ) ) {
426
            return false;
427
        } elseif ( ! empty( $cdn_url ) && strpos( $url, $cdn_url ) === false && array_key_exists( 'host', $url_parsed ) && $url_parsed['host'] !== $site_host ) {
428
            return false;
429
        } elseif ( strpos( $url, '.php' ) !== false ) {
430
            return false;
431
        } elseif ( str_ireplace( array( '.png', '.gif', '.jpg', '.jpeg', '.webp', '.avif' ), '', $url_parsed['path'] ) === $url_parsed['path'] ) {
432
            // fixme: better check against end of string.
433
            return false;
434
        } elseif ( ! empty( $nopti_images ) ) {
435
            $nopti_images_array = array_filter( array_map( 'trim', explode( ',', $nopti_images ) ) );
436
            foreach ( $nopti_images_array as $nopti_image ) {
437
                if ( strpos( $url, $nopti_image ) !== false || ( ( '' !== $tag && strpos( $tag, $nopti_image ) !== false ) ) ) {
438
                    return false;
439
                }
440
            }
441
        }
442
        return true;
443
    }
444
445
    private function build_imgopt_url( $orig_url, $width = 0, $height = 0 )
446
    {
447
        // sanitize width and height.
448
        if ( strpos( $width, '%' ) !== false ) {
449
            $width = 0;
450
        }
451
        if ( strpos( $height, '%' ) !== false ) {
452
            $height = 0;
453
        }
454
        $width  = (int) $width;
455
        $height = (int) $height;
456
457
        $filtered_url = apply_filters(
458
            'autoptimize_filter_imgopt_build_url',
459
            $orig_url,
460
            $width,
461
            $height
462
        );
463
464
        // If filter modified the url, return that.
465
        if ( $filtered_url !== $orig_url ) {
466
            return $filtered_url;
467
        }
468
469
        $orig_url        = $this->normalize_img_url( $orig_url );
470
        $imgopt_base_url = $this->get_imgopt_base_url();
471
        $imgopt_size     = '';
472
473
        if ( $width && 0 !== $width ) {
474
            $imgopt_size = ',w_' . $width;
475
        }
476
477
        if ( $height && 0 !== $height ) {
478
            $imgopt_size .= ',h_' . $height;
479
        }
480
481
        $url = $imgopt_base_url . $imgopt_size . '/' . $orig_url;
482
483
        return $url;
484
    }
485
486
    public function replace_data_thumbs( $matches )
487
    {
488
        return $this->replace_img_callback( $matches, 150, 150 );
489
    }
490
491
    public function replace_img_callback( $matches, $width = 0, $height = 0 )
492
    {
493
        $_normalized_img_url = $this->normalize_img_url( $matches[1] );
494
        if ( $this->can_optimize_image( $matches[1], $matches[0] ) ) {
495
            return str_replace( $matches[1], $this->build_imgopt_url( $_normalized_img_url, $width, $height ), $matches[0] );
496
        } else {
497
            return $matches[0];
498
        }
499
    }
500
501
    public function replace_icon_callback( $matches )
502
    {
503
        if ( array_key_exists( '2', $matches ) ) {
504
            $sizes  = explode( 'x', $matches[2] );
505
            $width  = $sizes[0];
506
            $height = $sizes[1];
507
        } else {
508
            $width  = 180;
509
            $height = 180;
510
        }
511
512
        // make sure we're not trying to optimize a *.ico file
513
        if ( strpos( $matches[1], '.ico' ) === false ) {
514
            return $this->replace_img_callback( $matches, $width, $height );
515
        } else {
516
            return $matches;
517
        }
518
    }
519
520
    public function filter_optimize_images( $in )
521
    {
522
        /*
523
         * potential future functional improvements:
524
         *
525
         * filter for critical CSS.
526
         */
527
        $to_replace = array();
528
529
        // hide noscript tags to avoid nesting noscript tags (as lazyloaded images add noscript).
530
        if ( $this->should_lazyload() ) {
531
            $in = autoptimizeBase::replace_contents_with_marker_if_exists(
532
                'SCRIPT',
533
                '<script',
534
                '#<(?:no)?script.*?<\/(?:no)?script>#is',
535
                $in
536
            );
537
        }
538
539
        // extract img tags.
540
        if ( preg_match_all( '#<img[^>]*src[^>]*>#Usmi', $in, $matches ) ) {
541
            foreach ( $matches[0] as $tag ) {
542
                $orig_tag = $tag;
543
                $imgopt_w = '';
544
                $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...
545
546
                // first do (data-)srcsets.
547
                if ( preg_match_all( '#srcset=("|\')(.*)("|\')#Usmi', $tag, $allsrcsets, PREG_SET_ORDER ) ) {
548
                    foreach ( $allsrcsets as $srcset ) {
549
                        $srcset      = $srcset[2];
550
                        $orig_srcset = $srcset;
551
                        $srcsets     = explode( ',', $srcset );
552
                        foreach ( $srcsets as $indiv_srcset ) {
553
                            $indiv_srcset_parts = explode( ' ', trim( $indiv_srcset ) );
554
                            if ( isset( $indiv_srcset_parts[1] ) && rtrim( $indiv_srcset_parts[1], 'w' ) !== $indiv_srcset_parts[1] ) {
555
                                $imgopt_w = rtrim( $indiv_srcset_parts[1], 'w' );
556
                            }
557
                            if ( $this->can_optimize_image( $indiv_srcset_parts[0], $tag ) ) {
558
                                $imgopt_url = $this->build_imgopt_url( $indiv_srcset_parts[0], $imgopt_w, '' );
559
                                $srcset     = str_replace( $indiv_srcset_parts[0], $imgopt_url, $srcset );
560
                            }
561
                        }
562
                        $tag = str_replace( $orig_srcset, $srcset, $tag );
563
                    }
564
                }
565
566
                // proceed with img src.
567
                // get width and height and add to $imgopt_size.
568
                $_get_size = $this->get_size_from_tag( $tag );
569
                $imgopt_w  = $_get_size['width'];
570
                $imgopt_h  = $_get_size['height'];
571
572
                // then start replacing images src.
573
                if ( preg_match_all( '#src=(?:"|\')(?!data)(.*)(?:"|\')#Usmi', $tag, $urls, PREG_SET_ORDER ) ) {
574
                    foreach ( $urls as $url ) {
575
                        $full_src_orig = $url[0];
576
                        $url           = $url[1];
577
                        if ( $this->can_optimize_image( $url, $full_src_orig ) ) {
578
                            $imgopt_url      = $this->build_imgopt_url( $url, $imgopt_w, $imgopt_h );
579
                            $full_imgopt_src = str_replace( $url, $imgopt_url, $full_src_orig );
580
                            $tag             = str_replace( $full_src_orig, $full_imgopt_src, $tag );
581
                        }
582
                    }
583
                }
584
585
                // do lazyload stuff.
586
                if ( $this->should_lazyload( $in ) && ! empty( $url ) ) {
587
                    // first do lpiq placeholder logic.
588
                    if ( strpos( $url, $this->get_imgopt_host() ) === 0 ) {
589
                        // if all img src have been replaced during srcset, we have to extract the
590
                        // origin url from the imgopt one to be able to set a lqip placeholder.
591
                        $_url = substr( $url, strpos( $url, '/http' ) + 1 );
592
                    } else {
593
                        $_url = $url;
594
                    }
595
596
                    $_url = $this->normalize_img_url( $_url );
597
598
                    $placeholder = '';
599
                    if ( $this->can_optimize_image( $_url, $tag ) && apply_filters( 'autoptimize_filter_imgopt_lazyload_dolqip', true ) ) {
600
                        $lqip_w = '';
601
                        $lqip_h = '';
602
                        if ( isset( $imgopt_w ) && ! empty( $imgopt_w ) ) {
603
                            $lqip_w = ',w_' . $imgopt_w;
604
                        }
605
                        if ( isset( $imgopt_h ) && ! empty( $imgopt_h ) ) {
606
                            $lqip_h = ',h_' . $imgopt_h;
607
                        }
608
                        $placeholder = $this->get_imgopt_host() . 'client/q_lqip,ret_wait' . $lqip_w . $lqip_h . '/' . $_url;
609
                    }
610
                    // then call add_lazyload-function with lpiq placeholder if set.
611
                    $tag = $this->add_lazyload( $tag, $placeholder );
612
                }
613
614
                // and add tag to array for later replacement.
615
                if ( $tag !== $orig_tag ) {
616
                    $to_replace[ $orig_tag ] = $tag;
617
                }
618
            }
619
        }
620
621
        // and replace all.
622
        $out = str_replace( array_keys( $to_replace ), array_values( $to_replace ), $in );
623
624
        // img thumbnails in e.g. woocommerce.
625
        if ( strpos( $out, 'data-thumb' ) !== false && apply_filters( 'autoptimize_filter_imgopt_datathumbs', true ) ) {
626
            $out = preg_replace_callback(
627
                '/\<div(?:[^>]?)\sdata-thumb\=(?:\"|\')(.+?)(?:\"|\')(?:[^>]*)?\>/s',
628
                array( $this, 'replace_data_thumbs' ),
629
                $out
630
            );
631
        }
632
633
        // background-image in inline style.
634 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...
635
            $out = preg_replace_callback(
636
                '/style=(?:"|\')[^<>]*?background-image:\s?url\((?:"|\')?([^"\')]*)(?:"|\')?\)/',
637
                array( $this, 'replace_img_callback' ),
638
                $out
639
            );
640
        }
641
642
        // act on icon links.
643
        if ( ( strpos( $out, '<link rel="icon"' ) !== false || ( strpos( $out, "<link rel='icon'" ) !== false ) ) && apply_filters( 'autoptimize_filter_imgopt_linkicon', true ) ) {
644
            $out = preg_replace_callback(
645
                '/<link\srel=(?:"|\')(?:apple-touch-)?icon(?:"|\').*\shref=(?:"|\')(.*)(?:"|\')(?:\ssizes=(?:"|\')(\d*x\d*)(?:"|\'))?\s\/>/Um',
646
                array( $this, 'replace_icon_callback' ),
647
                $out
648
            );
649
        }
650
651
        // lazyload: restore noscript tags + lazyload picture source tags and bgimage.
652
        if ( $this->should_lazyload() ) {
653
            $out = autoptimizeBase::restore_marked_content(
654
                'SCRIPT',
655
                $out
656
            );
657
658
            $out = $this->process_picture_tag( $out, true, true );
659
            $out = $this->process_bgimage( $out );
660
        } else {
661
            $out = $this->process_picture_tag( $out, true, false );
662
        }
663
664
        return $out;
665
    }
666
667
    public function get_size_from_tag( $tag ) {
668
        // reusable function to extract widht and height from an image tag
669
        // enforcing a filterable maximum width and height (default 4999X4999).
670
        $width  = '';
671
        $height = '';
672
673
        if ( preg_match( '#width=("|\')(.*)("|\')#Usmi', $tag, $_width ) ) {
674
            if ( strpos( $_width[2], '%' ) === false ) {
675
                $width = (int) $_width[2];
676
            }
677
        }
678
        if ( preg_match( '#height=("|\')(.*)("|\')#Usmi', $tag, $_height ) ) {
679
            if ( strpos( $_height[2], '%' ) === false ) {
680
                $height = (int) $_height[2];
681
            }
682
        }
683
684
        // check for and enforce (filterable) max sizes.
685
        $_max_width = apply_filters( 'autoptimize_filter_imgopt_max_width', 4999 );
686
        if ( $width > $_max_width ) {
687
            $_width = $_max_width;
688
            $height = $_width / $width * $height;
689
            $width  = $_width;
690
        }
691
        $_max_height = apply_filters( 'autoptimize_filter_imgopt_max_height', 4999 );
692
        if ( $height > $_max_height ) {
693
            $_height = $_max_height;
694
            $width   = $_height / $height * $width;
695
            $height  = $_height;
696
        }
697
698
        return array(
699
            'width'  => $width,
700
            'height' => $height,
701
        );
702
    }
703
704
    /**
705
     * Lazyload functions
706
     */
707
    public static function should_lazyload_wrapper() {
708
        // needed in autoptimizeMain.php.
709
        $self = new self();
710
        return $self->should_lazyload();
711
    }
712
713
    public function should_lazyload( $context = '' ) {
714
        if ( ! empty( $this->options['autoptimize_imgopt_checkbox_field_3'] ) && false === $this->check_nolazy() ) {
715
            $lazyload_return = true;
716
        } else {
717
            $lazyload_return = false;
718
        }
719
        $lazyload_return = apply_filters( 'autoptimize_filter_imgopt_should_lazyload', $lazyload_return, $context );
720
721
        return $lazyload_return;
722
    }
723
724
    public function check_nolazy() {
725
        if ( array_key_exists( 'ao_nolazy', $_GET ) && '1' === $_GET['ao_nolazy'] ) {
726
            return true;
727
        } else {
728
            return false;
729
        }
730
    }
731
732
    public function filter_lazyload_images( $in )
733
    {
734
        // only used is image optimization is NOT active but lazyload is.
735
        $to_replace = array();
736
737
        // hide (no)script tags to avoid nesting noscript tags (as lazyloaded images add noscript).
738
        $out = autoptimizeBase::replace_contents_with_marker_if_exists(
739
            'SCRIPT',
740
            '<script',
741
            '#<(?:no)?script.*?<\/(?:no)?script>#is',
742
            $in
743
        );
744
745
        // extract img tags and add lazyload attribs.
746
        if ( preg_match_all( '#<img[^>]*src[^>]*>#Usmi', $out, $matches ) ) {
747
            foreach ( $matches[0] as $tag ) {
748
                if ( $this->should_lazyload( $out ) ) {
749
                    $to_replace[ $tag ] = $this->add_lazyload( $tag );
750
                }
751
            }
752
            $out = str_replace( array_keys( $to_replace ), array_values( $to_replace ), $out );
753
        }
754
755
        // and also lazyload picture tag.
756
        $out = $this->process_picture_tag( $out, false, true );
757
758
        // and inline style blocks with background-image.
759
        $out = $this->process_bgimage( $out );
760
761
        // restore noscript tags.
762
        $out = autoptimizeBase::restore_marked_content(
763
            'SCRIPT',
764
            $out
765
        );
766
767
        return $out;
768
    }
769
770
    public function add_lazyload( $tag, $placeholder = '' ) {
771
        // adds actual lazyload-attributes to an image node.
772
        $this->lazyload_counter++;
773
        if ( str_ireplace( $this->get_lazyload_exclusions(), '', $tag ) === $tag && $this->lazyload_counter >= apply_filters( 'autoptimize_filter_imgopt_lazyload_from_nth', 0 ) ) {
774
            $tag = $this->maybe_fix_missing_quotes( $tag );
775
776
            // store original tag for use in noscript version.
777
            $noscript_tag = '<noscript>' . autoptimizeUtils::remove_id_from_node( $tag ) . '</noscript>';
778
779
            $lazyload_class = apply_filters( 'autoptimize_filter_imgopt_lazyload_class', 'lazyload' );
780
781
            // insert lazyload class.
782
            $tag = $this->inject_classes_in_tag( $tag, "$lazyload_class " );
783
784
            if ( ! $placeholder || empty( $placeholder ) ) {
785
                // get image width & heigth for placeholder fun (and to prevent content reflow).
786
                $_get_size = $this->get_size_from_tag( $tag );
787
                $width     = $_get_size['width'];
788
                $height    = $_get_size['height'];
789
                if ( false === $width || empty( $width ) ) {
790
                    $width = 210; // default width for SVG placeholder.
791
                }
792
                if ( false === $height || empty( $height ) ) {
793
                    $height = $width / 3 * 2; // if no height, base it on width using the 3/2 aspect ratio.
794
                }
795
796
                // insert the actual lazyload stuff.
797
                // see https://css-tricks.com/preventing-content-reflow-from-lazy-loaded-images/ for great read on why we're using empty svg's.
798
                $placeholder = apply_filters( 'autoptimize_filter_imgopt_lazyload_placeholder', $this->get_default_lazyload_placeholder( $width, $height ) );
799
            }
800
801
            $tag = preg_replace( '/(\s)src=/', ' src=\'' . $placeholder . '\' data-src=', $tag );
802
            $tag = preg_replace( '/(\s)srcset=/', ' data-srcset=', $tag );
803
804
            // move sizes to data-sizes unless filter says no.
805
            if ( apply_filters( 'autoptimize_filter_imgopt_lazyload_move_sizes', true ) ) {
806
                $tag = str_replace( ' sizes=', ' data-sizes=', $tag );
807
            }
808
809
            // add the noscript-tag from earlier.
810
            $tag = $noscript_tag . $tag;
811
            $tag = apply_filters( 'autoptimize_filter_imgopt_lazyloaded_img', $tag );
812
        }
813
814
        return $tag;
815
    }
816
817
    public function add_lazyload_js_footer() {
818
        if ( false === autoptimizeMain::should_buffer() ) {
819
            return;
820
        }
821
822
        // The JS will by default be excluded form autoptimization but this can be changed with a filter.
823
        $noptimize_flag = '';
824
        if ( apply_filters( 'autoptimize_filter_imgopt_lazyload_js_noptimize', true ) ) {
825
            $noptimize_flag = ' data-noptimize="1"';
826
        }
827
828
        $lazysizes_js = plugins_url( 'external/js/lazysizes.min.js?ao_version=' . AUTOPTIMIZE_PLUGIN_VERSION, __FILE__ );
829
        $cdn_url      = $this->get_cdn_url();
830
        if ( ! empty( $cdn_url ) ) {
831
            $cdn_url      = rtrim( $cdn_url, '/' );
832
            $lazysizes_js = str_replace( AUTOPTIMIZE_WP_SITE_URL, $cdn_url, $lazysizes_js );
833
        }
834
835
        $type_js = '';
836
        if ( apply_filters( 'autoptimize_filter_cssjs_addtype', false ) ) {
837
            $type_js = ' type="text/javascript"';
838
        }
839
840
        // Adds lazyload CSS & JS to footer, using echo because wp_enqueue_script seems not to support pushing attributes (async).
841
        echo apply_filters( 'autoptimize_filter_imgopt_lazyload_cssoutput', '<noscript><style>.lazyload{display:none;}</style></noscript>' );
842
        echo apply_filters( 'autoptimize_filter_imgopt_lazyload_jsconfig', '<script' . $type_js . $noptimize_flag . '>window.lazySizesConfig=window.lazySizesConfig||{};window.lazySizesConfig.loadMode=1;</script>' );
843
        echo apply_filters( 'autoptimize_filter_imgopt_lazyload_js', '<script async' . $type_js . $noptimize_flag . ' src=\'' . $lazysizes_js . '\'></script>' );
844
845
        // And add webp detection and loading JS.
846
        if ( $this->should_ngimg() ) {
847
            // Add AVIF code, can be disabled for now to only do webp.
848
            if ( apply_filters( 'autoptimize_filter_imgopt_do_avif', true ) ) {
849
                $_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");';
850
                $_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+","))})});';
851
            } else {
852
                $_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);";
853
                $_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,'))})});";
854
            }
855
            // Keeping autoptimize_filter_imgopt_webp_js filter for now, but it is deprecated as not only for webp any more.
856
            $_ngimg_output = apply_filters( 'autoptimize_filter_imgopt_webp_js', '<script' . $type_js . $noptimize_flag . '>' . $_ngimg_detect . $_ngimg_load . '</script>' );
857
            echo apply_filters( 'autoptimize_filter_imgopt_ngimg_js', $_ngimg_output );
858
        }
859
    }
860
861
    public function get_cdn_url() {
862
        // getting CDN url here to avoid having to make bigger changes to autoptimizeBase.
863
        static $cdn_url = null;
864
865
        if ( null === $cdn_url ) {
866
            $cdn_url = autoptimizeOptionWrapper::get_option( 'autoptimize_cdn_url', '' );
867
            $cdn_url = autoptimizeUtils::tweak_cdn_url_if_needed( $cdn_url );
868
            $cdn_url = apply_filters( 'autoptimize_filter_base_cdnurl', $cdn_url );
869
        }
870
871
        return $cdn_url;
872
    }
873
874
    public function get_lazyload_exclusions() {
875
        // returns array of strings that if found in an <img tag will stop the img from being lazy-loaded.
876
        static $exclude_lazyload_array = null;
877
878
        if ( null === $exclude_lazyload_array ) {
879
            $options = $this->options;
880
881
            // set default exclusions.
882
            $exclude_lazyload_array = array( 'skip-lazy', 'data-no-lazy', 'notlazy', 'data-src', 'data-srcset', 'data:image/', 'data-lazyload', 'rev-slidebg', 'loading="eager"' );
883
884
            // add from setting.
885
            if ( array_key_exists( 'autoptimize_imgopt_text_field_5', $options ) ) {
886
                $exclude_lazyload_option = $options['autoptimize_imgopt_text_field_5'];
887 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...
888
                    $exclude_lazyload_array = array_merge( $exclude_lazyload_array, array_filter( array_map( 'trim', explode( ',', $options['autoptimize_imgopt_text_field_5'] ) ) ) );
889
                }
890
            }
891
892
            // and filter for developer-initiated changes.
893
            $exclude_lazyload_array = apply_filters( 'autoptimize_filter_imgopt_lazyload_exclude_array', $exclude_lazyload_array );
894
        }
895
896
        return $exclude_lazyload_array;
897
    }
898
899
    public function inject_classes_in_tag( $tag, $target_class ) {
900
        if ( strpos( $tag, 'class=' ) !== false ) {
901
            $tag = preg_replace( '/(\sclass\s?=\s?("|\'))/', '$1' . $target_class, $tag );
902
        } else {
903
            $tag = preg_replace( '/(<[a-zA-Z]*)\s/', '$1 class="' . trim( $target_class ) . '" ', $tag );
904
        }
905
906
        return $tag;
907
    }
908
909
    public function get_default_lazyload_placeholder( $imgopt_w, $imgopt_h ) {
910
        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';
911
    }
912
913
    public function should_ngimg() {
914
        static $ngimg_return = null;
915
916
        if ( is_null( $ngimg_return ) ) {
917
            // webp only works if imgopt and lazyload are also active.
918
            if ( ! empty( $this->options['autoptimize_imgopt_checkbox_field_4'] ) && ! empty( $this->options['autoptimize_imgopt_checkbox_field_3'] ) && $this->imgopt_active() ) {
919
                $ngimg_return = true;
920
            } else {
921
                $ngimg_return = false;
922
            }
923
        }
924
925
        return $ngimg_return;
926
    }
927
928
    public function process_picture_tag( $in, $imgopt = false, $lazy = false ) {
929
        // check if "<picture" is present and if filter allows us to process <picture>.
930
        if ( strpos( $in, '<picture' ) === false || apply_filters( 'autoptimize_filter_imgopt_dopicture', true ) === false ) {
931
            return $in;
932
        }
933
934
        $_exclusions     = $this->get_lazyload_exclusions();
935
        $to_replace_pict = array();
936
937
        // extract and process each picture-node.
938
        preg_match_all( '#<picture.*</picture>#Usmi', $in, $_pictures, PREG_SET_ORDER );
939
        foreach ( $_pictures as $_picture ) {
940
            $_picture = $this->maybe_fix_missing_quotes( $_picture );
941
            if ( strpos( $_picture[0], '<source ' ) !== false && preg_match_all( '#<source .*srcset=(?:"|\')(?!data)(.*)(?:"|\').*>#Usmi', $_picture[0], $_sources, PREG_SET_ORDER ) !== false ) {
942
                foreach ( $_sources as $_source ) {
943
                    $_picture_replacement = $_source[0];
944
945
                    // should we optimize the image?
946
                    if ( $imgopt && $this->can_optimize_image( $_source[1], $_picture[0] ) ) {
947
                        $_picture_replacement = str_replace( $_source[1], $this->build_imgopt_url( $_source[1] ), $_picture_replacement );
948
                    }
949
                    // should we lazy-load?
950
                    if ( $lazy && $this->should_lazyload() && str_ireplace( $_exclusions, '', $_picture_replacement ) === $_picture_replacement ) {
951
                        $_picture_replacement = str_replace( ' srcset=', ' data-srcset=', $_picture_replacement );
952
                    }
953
                    $to_replace_pict[ $_source[0] ] = $_picture_replacement;
954
                }
955
            }
956
        }
957
958
        // and return the fully procesed $in.
959
        $out = str_replace( array_keys( $to_replace_pict ), array_values( $to_replace_pict ), $in );
960
961
        return $out;
962
    }
963
964
    public function process_bgimage( $in ) {
965 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...
966
            $out = preg_replace_callback(
967
                '/(<(?:article|aside|body|div|footer|header|p|section|table)[^>]*)\sstyle=(?:"|\')[^<>]*?background-image:\s?url\((?:"|\')?([^"\')]*)(?:"|\')?\)[^>]*/',
968
                array( $this, 'lazyload_bgimg_callback' ),
969
                $in
970
            );
971
            return $out;
972
        }
973
        return $in;
974
    }
975
976
    public function lazyload_bgimg_callback( $matches ) {
977
        if ( str_ireplace( $this->get_lazyload_exclusions(), '', $matches[0] ) === $matches[0] ) {
978
            // get placeholder & lazyload class strings.
979
            $placeholder    = apply_filters( 'autoptimize_filter_imgopt_lazyload_placeholder', $this->get_default_lazyload_placeholder( 500, 300 ) );
980
            $lazyload_class = apply_filters( 'autoptimize_filter_imgopt_lazyload_class', 'lazyload' );
981
            // replace background-image URL with SVG placeholder.
982
            $out = str_replace( $matches[2], $placeholder, $matches[0] );
983
            // add data-bg attribute with real background-image URL for lazyload to pick up.
984
            $out = str_replace( $matches[1], $matches[1] . ' data-bg="' . trim( str_replace( array( "\r\n", '&quot;' ), '', $matches[2] ) ) . '"', $out );
985
            // add lazyload class to tag.
986
            $out = $this->inject_classes_in_tag( $out, "$lazyload_class " );
987
            return $out;
988
        }
989
        return $matches[0];
990
    }
991
992
    public function maybe_fix_missing_quotes( $tag_in ) {
993
        // 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.
994
        if ( file_exists( WP_PLUGIN_DIR . '/w3-total-cache/w3-total-cache.php' ) && class_exists( 'Minify_HTML' ) && apply_filters( 'autoptimize_filter_imgopt_fixquotes', true ) ) {
995
            $tag_out = preg_replace( '/class\s?=([^("|\')]*)(\s|>)/U', 'class=\'$1\'$2', $tag_in );
996
            $tag_out = preg_replace( '/\s(width|height)=(?:"|\')?([^\s"\'>]*)(?:"|\')?/', ' $1=\'$2\'', $tag_out );
997
            return $tag_out;
998
        } else {
999
            return $tag_in;
1000
        }
1001
    }
1002
1003
    /**
1004
     * Admin page logic and related functions below.
1005
     */
1006
    public function imgopt_admin_menu()
1007
    {
1008
        // no acces if multisite and not network admin and no site config allowed.
1009
        if ( autoptimizeConfig::should_show_menu_tabs() ) {
1010
            add_submenu_page(
1011
                null,
1012
                'autoptimize_imgopt',
1013
                'autoptimize_imgopt',
1014
                'manage_options',
1015
                'autoptimize_imgopt',
1016
                array( $this, 'imgopt_options_page' )
1017
            );
1018
        }
1019
        register_setting( 'autoptimize_imgopt_settings', 'autoptimize_imgopt_settings' );
1020
    }
1021
1022
    public function add_imgopt_tab( $in )
1023
    {
1024
        if ( autoptimizeConfig::should_show_menu_tabs() ) {
1025
            $in = array_merge( $in, array( 'autoptimize_imgopt' => __( 'Images', 'autoptimize' ) ) );
1026
        }
1027
1028
        return $in;
1029
    }
1030
1031
    public function imgopt_options_page()
1032
    {
1033
        // Check querystring for "refreshCacheChecker" and call cachechecker if so.
1034
        if ( array_key_exists( 'refreshImgProvStats', $_GET ) && 1 == $_GET['refreshImgProvStats'] ) {
1035
            $this->query_img_provider_stats();
1036
        }
1037
1038
        $options       = $this->fetch_options();
1039
        $sp_url_suffix = $this->get_service_url_suffix();
1040
        ?>
1041
    <style>
1042
        #ao_settings_form {background: white;border: 1px solid #ccc;padding: 1px 15px;margin: 15px 10px 10px 0;}
1043
        #ao_settings_form .form-table th {font-weight: normal;}
1044
        #autoptimize_imgopt_descr{font-size: 120%;}
1045
    </style>
1046
    <script>document.title = "Autoptimize: <?php _e( 'Images', 'autoptimize' ); ?> " + document.title;</script>
1047
    <div class="wrap">
1048
    <h1><?php _e( 'Autoptimize Settings', 'autoptimize' ); ?></h1>
1049
        <?php echo autoptimizeConfig::ao_admin_tabs(); ?>
1050
        <?php if ( 'down' === $options['availabilities']['extra_imgopt']['status'] ) { ?>
1051
            <div class="notice-warning notice"><p>
1052
            <?php
1053
            // translators: "Autoptimize support forum" will appear in a "a href".
1054
            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>' );
1055
            ?>
1056
            </p></div>
1057
        <?php } ?>
1058
1059
        <?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...
1060
            <div class="notice-warning notice"><p>
1061
            <?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' ); ?>
1062
            </p></div>
1063
        <?php } ?>
1064
1065
        <?php if ( class_exists( 'Jetpack' ) && method_exists( 'Jetpack', 'get_active_modules' ) && in_array( 'photon', Jetpack::get_active_modules() ) ) { ?>
1066
            <div class="notice-warning notice"><p>
1067
            <?php
1068
            // translators: "disable  Jetpack's site accelerator for images" will appear in a "a href" linking to the jetpack settings page.
1069
            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>' );
1070
            ?>
1071
            </p></div>
1072
        <?php } ?>
1073
    <form id='ao_settings_form' action='<?php echo admin_url( 'options.php' ); ?>' method='post'>
1074
        <?php settings_fields( 'autoptimize_imgopt_settings' ); ?>
1075
        <h2><?php _e( 'Image optimization', 'autoptimize' ); ?></h2>
1076
        <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>
1077
        <table class="form-table">
1078
            <tr>
1079
                <th scope="row"><?php _e( 'Optimize Images', 'autoptimize' ); ?></th>
1080
                <td>
1081
                    <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>
1082
                    <?php
1083
                    // show shortpixel status.
1084
                    $_notice = autoptimizeImages::instance()->get_imgopt_status_notice();
1085
                    if ( $_notice ) {
1086
                        switch ( $_notice['status'] ) {
1087
                            case 2:
1088
                                $_notice_color = 'green';
1089
                                break;
1090
                            case 1:
1091
                                $_notice_color = 'orange';
1092
                                break;
1093
                            case -1:
1094
                            case -2:
1095
                            case -3:
1096
                                $_notice_color = 'red';
1097
                                break;
1098
                            default:
1099
                                $_notice_color = 'green';
1100
                        }
1101
                        echo apply_filters( 'autoptimize_filter_imgopt_settings_status', '<p><strong><span style="color:' . $_notice_color . ';">' . __( 'Shortpixel status: ', 'autoptimize' ) . '</span></strong>' . $_notice['notice'] . '</p>' );
1102
                    } else {
1103
                        // translators: link points to shortpixel.
1104
                        $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>' );
1105
                        if ( 'launch' === $options['availabilities']['extra_imgopt']['status'] ) {
1106
                            $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' );
1107
                        } else {
1108
                            // translators: link points to shortpixel.
1109
                            $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>' );
1110
                        }
1111
                        echo apply_filters( 'autoptimize_imgopt_imgopt_settings_copy', $upsell_msg_1 . ' ' . $upsell_msg_2 . '</p>' );
1112
                    }
1113
                    // translators: link points to shortpixel FAQ.
1114
                    $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>' );
1115
                    $faqcopy = $faqcopy . ' ' . __( 'Only works for sites/ images that are publicly available.', 'autoptimize' );
1116
                    // translators: links points to shortpixel TOS & Privacy Policy.
1117
                    $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>' );
1118
                    echo apply_filters( 'autoptimize_imgopt_imgopt_settings_tos', '<p>' . $faqcopy . ' ' . $toscopy . '</p>' );
1119
                    ?>
1120
                </td>
1121
            </tr>
1122 View Code Duplication
            <tr id='autoptimize_imgopt_optimization_exclusions' <?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...
1123
                <th scope="row"><?php _e( 'Optimization exclusions', 'autoptimize' ); ?></th>
1124
                <td>
1125
                    <label><input type='text' style='width:80%' id='autoptimize_imgopt_optimization_exclusions' name='autoptimize_imgopt_settings[autoptimize_imgopt_text_field_6]' value='<?php if ( ! empty( $options['autoptimize_imgopt_text_field_6'] ) ) { echo esc_attr( $options['autoptimize_imgopt_text_field_6'] ); } ?>'><br /><?php _e( 'Comma-separated list of image classes or filenames that should not be optimized.', 'autoptimize' ); ?></label>
1126
                </td>
1127
            </tr>
1128 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...
1129
                <th scope="row"><?php _e( 'Image Optimization quality', 'autoptimize' ); ?></th>
1130
                <td>
1131
                    <label>
1132
                    <select name='autoptimize_imgopt_settings[autoptimize_imgopt_select_field_2]'>
1133
                        <?php
1134
                        $_imgopt_array = autoptimizeImages::instance()->get_img_quality_array();
1135
                        $_imgopt_val   = autoptimizeImages::instance()->get_img_quality_setting();
1136
1137
                        foreach ( $_imgopt_array as $key => $value ) {
1138
                            echo '<option value="' . $key . '"';
1139
                            if ( $_imgopt_val == $key ) {
1140
                                echo ' selected';
1141
                            }
1142
                            echo '>' . ucfirst( $value ) . '</option>';
1143
                        }
1144
                        echo "\n";
1145
                        ?>
1146
                    </select>
1147
                    </label>
1148
                    <p>
1149
                        <?php
1150
                            // translators: link points to shortpixel image test page.
1151
                            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>' ) );
1152
                        ?>
1153
                    </p>
1154
                </td>
1155
            </tr>
1156 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...
1157
                <th scope="row"><?php _e( 'Load WebP or AVIF in supported browsers?', 'autoptimize' ); ?></th>
1158
                <td>
1159
                    <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>
1160
                </td>
1161
            </tr>
1162
            <tr>
1163
                <th scope="row"><?php _e( 'Lazy-load images?', 'autoptimize' ); ?></th>
1164
                <td>
1165
                    <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>
1166
                </td>
1167
            </tr>
1168 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...
1169
                <th scope="row"><?php _e( 'Lazy-load exclusions', 'autoptimize' ); ?></th>
1170
                <td>
1171
                    <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>
1172
                </td>
1173
            </tr>
1174
        </table>
1175
        <p class="submit"><input type="submit" name="submit" id="submit" class="button button-primary" value="<?php _e( 'Save Changes', 'autoptimize' ); ?>" /></p>
1176
    </form>
1177
    <script>
1178
        jQuery(document).ready(function() {
1179
            jQuery("#autoptimize_imgopt_checkbox").change(function() {
1180
                if (this.checked) {
1181
                    jQuery("#autoptimize_imgopt_quality").show("slow");
1182
                    jQuery("#autoptimize_imgopt_ngimg").show("slow");
1183
                    jQuery("#autoptimize_imgopt_optimization_exclusions").show("slow");
1184
                } else {
1185
                    jQuery("#autoptimize_imgopt_quality").hide("slow");
1186
                    jQuery("#autoptimize_imgopt_ngimg").hide("slow");
1187
                    jQuery("#autoptimize_imgopt_optimization_exclusions").hide("slow");
1188
                }
1189
            });
1190
            jQuery("#autoptimize_imgopt_ngimg_checkbox").change(function() {
1191
                if (this.checked) {
1192
                    jQuery("#autoptimize_imgopt_lazyload_checkbox")[0].checked = true;
1193
                    jQuery("#autoptimize_imgopt_lazyload_exclusions").show("slow");
1194
                }
1195
            });
1196
            jQuery("#autoptimize_imgopt_lazyload_checkbox").change(function() {
1197
                if (this.checked) {
1198
                    jQuery("#autoptimize_imgopt_lazyload_exclusions").show("slow");
1199
                } else {
1200
                    jQuery("#autoptimize_imgopt_lazyload_exclusions").hide("slow");
1201
                    jQuery("#autoptimize_imgopt_ngimg_checkbox")[0].checked = false;
1202
                }
1203
            });
1204
        });
1205
    </script>
1206
        <?php
1207
    }
1208
1209
    /**
1210
     * Ïmg opt status as used on dashboard.
1211
     */
1212
    public function get_imgopt_status_notice() {
1213
        if ( $this->imgopt_active() ) {
1214
            $_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...
1215
            $_stat           = autoptimizeOptionWrapper::get_option( 'autoptimize_imgopt_provider_stat', '' );
1216
            $_site_host      = AUTOPTIMIZE_SITE_DOMAIN;
1217
            $_imgopt_upsell  = 'https://shortpixel.com/aospai/af/GWRGFLW109483/' . $_site_host;
1218
            $_imgopt_assoc   = 'https://shortpixel.helpscoutdocs.com/article/94-how-to-associate-a-domain-to-my-account';
1219
            $_imgopt_unreach = 'https://shortpixel.helpscoutdocs.com/article/148-why-are-my-images-redirected-from-cdn-shortpixel-ai';
1220
1221
            if ( is_array( $_stat ) ) {
1222
                if ( 1 == $_stat['Status'] ) {
1223
                    // translators: "add more credits" will appear in a "a href".
1224
                    $_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>' );
1225
                } elseif ( -1 == $_stat['Status'] || -2 == $_stat['Status'] ) {
1226
                    // translators: "add more credits" will appear in a "a href".
1227
                    $_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>' );
1228
                    // translators: "associate your domain" will appear in a "a href".
1229
                    $_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>' );
1230
                } elseif ( -3 == $_stat['Status'] ) {
1231
                    // translators: "check the documentation here" will appear in a "a href".
1232
                    $_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>' );
1233
                } else {
1234
                    $_imgopt_upsell = 'https://shortpixel.com/g/af/GWRGFLW109483';
1235
                    // translators: "log in to check your account" will appear in a "a href".
1236
                    $_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>' );
1237
                }
1238
1239
                // add info on freshness + refresh link if status is not 2 (good shape).
1240
                if ( 2 != $_stat['Status'] ) {
1241
                    $_imgopt_stats_refresh_url = add_query_arg( array(
1242
                        'page'                => 'autoptimize_imgopt',
1243
                        'refreshImgProvStats' => '1',
1244
                    ), admin_url( 'options-general.php' ) );
1245
                    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...
1246
                        $_imgopt_stats_last_run = __( 'based on status at ', 'autoptimize' ) . date_i18n( autoptimizeOptionWrapper::get_option( 'time_format' ), $_stat['timestamp'] );
1247
                    } else {
1248
                        $_imgopt_stats_last_run = __( 'based on previously fetched data', 'autoptimize' );
1249
                    }
1250
                    $_imgopt_notice .= ' (' . $_imgopt_stats_last_run . ', ';
1251
                    // translators: "here to refresh" links to the Autoptimize Extra page and forces a refresh of the img opt stats.
1252
                    $_imgopt_notice .= sprintf( __( 'click %1$shere to refresh%2$s', 'autoptimize' ), '<a href="' . $_imgopt_stats_refresh_url . '">', '</a>).' );
1253
                }
1254
1255
                // and make the full notice filterable.
1256
                $_imgopt_notice = apply_filters( 'autoptimize_filter_imgopt_notice', $_imgopt_notice );
1257
1258
                return array(
1259
                    'status' => $_stat['Status'],
1260
                    'notice' => $_imgopt_notice,
1261
                );
1262
            }
1263
        }
1264
        return false;
1265
    }
1266
1267
    public static function get_imgopt_status_notice_wrapper() {
1268
        // needed for notice being shown in autoptimizeCacheChecker.php.
1269
        $self = new self();
1270
        return $self->get_imgopt_status_notice();
1271
    }
1272
1273
    /**
1274
     * Get img provider stats (used to display notice).
1275
     */
1276
    public function query_img_provider_stats() {
1277
        if ( ! empty( $this->options['autoptimize_imgopt_checkbox_field_1'] ) ) {
1278
            $url      = '';
1279
            $endpoint = $this->get_imgopt_host() . 'read-domain/';
1280
            $domain   = AUTOPTIMIZE_SITE_DOMAIN;
1281
1282
            // make sure parse_url result makes sense, keeping $url empty if not.
1283
            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...
1284
                $url = $endpoint . $domain;
1285
            }
1286
1287
            $url = apply_filters(
1288
                'autoptimize_filter_imgopt_stat_url',
1289
                $url
1290
            );
1291
1292
            // only do the remote call if $url is not empty to make sure no parse_url
1293
            // weirdness results in useless calls.
1294 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...
1295
                $response = wp_remote_get( $url );
1296
                if ( ! is_wp_error( $response ) ) {
1297
                    if ( '200' == wp_remote_retrieve_response_code( $response ) ) {
1298
                        $stats = json_decode( wp_remote_retrieve_body( $response ), true );
1299
                        autoptimizeOptionWrapper::update_option( 'autoptimize_imgopt_provider_stat', $stats );
1300
                    }
1301
                }
1302
            }
1303
        }
1304
    }
1305
1306
    public static function get_img_provider_stats()
1307
    {
1308
        // wrapper around query_img_provider_stats() so we can get to $this->options from cronjob() in autoptimizeCacheChecker.
1309
        $self = new self();
1310
        return $self->query_img_provider_stats();
1311
    }
1312
1313
    /**
1314
     * Determines and returns the service launch status.
1315
     *
1316
     * @return bool
1317
     */
1318
    public function launch_ok()
1319
    {
1320
        static $launch_status = null;
1321
1322
        if ( null === $launch_status ) {
1323
            $avail_imgopt  = $this->options['availabilities']['extra_imgopt'];
1324
            $magic_number  = intval( substr( md5( parse_url( AUTOPTIMIZE_WP_SITE_URL, PHP_URL_HOST ) ), 0, 3 ), 16 );
1325
            $has_launched  = autoptimizeOptionWrapper::get_option( 'autoptimize_imgopt_launched', '' );
1326
            $launch_status = false;
1327
            if ( $has_launched || ( is_array( $avail_imgopt ) && array_key_exists( 'launch-threshold', $avail_imgopt ) && $magic_number < $avail_imgopt['launch-threshold'] ) ) {
1328
                $launch_status = true;
1329
                if ( ! $has_launched ) {
1330
                    autoptimizeOptionWrapper::update_option( 'autoptimize_imgopt_launched', 'on' );
1331
                }
1332
            }
1333
        }
1334
1335
        return $launch_status;
1336
    }
1337
1338
    public static function launch_ok_wrapper() {
1339
        // needed for "plug" notice in autoptimizeMain.php.
1340
        $self = new self();
1341
        return $self->launch_ok();
1342
    }
1343
1344
    public function get_imgopt_provider_userstatus() {
1345
        static $_provider_userstatus = null;
1346
1347
        if ( is_null( $_provider_userstatus ) ) {
1348
            $_stat = autoptimizeOptionWrapper::get_option( 'autoptimize_imgopt_provider_stat', '' );
1349
            if ( is_array( $_stat ) ) {
1350
                if ( array_key_exists( 'Status', $_stat ) ) {
1351
                    $_provider_userstatus['Status'] = $_stat['Status'];
1352
                } else {
1353
                    // if no stats then we assume all is well.
1354
                    $_provider_userstatus['Status'] = 2;
1355
                }
1356
                if ( array_key_exists( 'timestamp', $_stat ) ) {
1357
                    $_provider_userstatus['timestamp'] = $_stat['timestamp'];
1358
                } else {
1359
                    // if no timestamp then we return "".
1360
                    $_provider_userstatus['timestamp'] = '';
1361
                }
1362
            } else {
1363
                // no provider_stat yet, assume/ return all OK.
1364
                $_provider_userstatus['Status']    = 2;
1365
                $_provider_userstatus['timestamp'] = '';
1366
            }
1367
        }
1368
1369
        return $_provider_userstatus;
1370
    }
1371
}
1372