Completed
Push — master ( c92350...ab0c5b )
by frank
01:42
created

autoptimizeImages::get_imgopt_status_notice()   C

Complexity

Conditions 11
Paths 14

Size

Total Lines 54

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 11
nc 14
nop 0
dl 0
loc 54
rs 6.8569
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
554
                    } else {
555
                        $_url = $url;
556
                    }
557
558
                    $placeholder = '';
559
                    if ( $this->can_optimize_image( $_url ) && apply_filters( 'autoptimize_filter_imgopt_lazyload_dolqip', true ) ) {
560
                        $lqip_w = '';
561
                        $lqip_h = '';
562
                        if ( isset( $imgopt_w ) && ! empty( $imgopt_w ) ) {
563
                            $lqip_w = ',w_' . $imgopt_w;
564
                        }
565
                        if ( isset( $imgopt_h ) && ! empty( $imgopt_h ) ) {
566
                            $lqip_h = ',h_' . $imgopt_h;
567
                        }
568
                        $placeholder = $this->get_imgopt_host() . 'client/q_lqip,ret_wait' . $lqip_w . $lqip_h . '/' . $_url;
569
                    }
570
                    // then call add_lazyload-function with lpiq placeholder if set.
571
                    $tag = $this->add_lazyload( $tag, $placeholder );
572
                }
573
574
                // and add tag to array for later replacement.
575
                if ( $tag !== $orig_tag ) {
576
                    $to_replace[ $orig_tag ] = $tag;
577
                }
578
            }
579
        }
580
581
        // and replace all.
582
        $out = str_replace( array_keys( $to_replace ), array_values( $to_replace ), $in );
583
584
        // img thumbnails in e.g. woocommerce.
585
        if ( strpos( $out, 'data-thumb' ) !== false && apply_filters( 'autoptimize_filter_imgopt_datathumbs', true ) ) {
586
            $out = preg_replace_callback(
587
                '/\<div(?:[^>]?)\sdata-thumb\=(?:\"|\')(.+?)(?:\"|\')(?:[^>]*)?\>/s',
588
                array( $this, 'replace_data_thumbs' ),
589
                $out
590
            );
591
        }
592
593
        // background-image in inline style.
594 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...
595
            $out = preg_replace_callback(
596
                '/style=(?:"|\').*?background-image:\s?url\((?:"|\')?([^"\')]*)(?:"|\')?\)/',
597
                array( $this, 'replace_img_callback' ),
598
                $out
599
            );
600
        }
601
602
        // lazyload: restore noscript tags + lazyload picture source tags and bgimage.
603
        if ( $this->should_lazyload() ) {
604
            $out = autoptimizeBase::restore_marked_content(
605
                'SCRIPT',
606
                $out
607
            );
608
609
            $out = $this->process_picture_tag( $out, true, true );
610
            $out = $this->process_bgimage( $out );
611
        } else {
612
            $out = $this->process_picture_tag( $out, true, false );
613
        }
614
615
        return $out;
616
    }
617
618
    public function get_size_from_tag( $tag ) {
619
        // reusable function to extract widht and height from an image tag
620
        // enforcing a filterable maximum width and height (default 4999X4999).
621
        $width  = '';
622
        $height = '';
623
624
        if ( preg_match( '#width=("|\')(.*)("|\')#Usmi', $tag, $_width ) ) {
625
            if ( strpos( $_width[2], '%' ) === false ) {
626
                $width = (int) $_width[2];
627
            }
628
        }
629
        if ( preg_match( '#height=("|\')(.*)("|\')#Usmi', $tag, $_height ) ) {
630
            if ( strpos( $_height[2], '%' ) === false ) {
631
                $height = (int) $_height[2];
632
            }
633
        }
634
635
        // check for and enforce (filterable) max sizes.
636
        $_max_width = apply_filters( 'autoptimize_filter_imgopt_max_width', 4999 );
637
        if ( $width > $_max_width ) {
638
            $_width = $_max_width;
639
            $height = $_width / $width * $height;
640
            $width  = $_width;
641
        }
642
        $_max_height = apply_filters( 'autoptimize_filter_imgopt_max_height', 4999 );
643
        if ( $height > $_max_height ) {
644
            $_height = $_max_height;
645
            $width   = $_height / $height * $width;
646
            $height  = $_height;
647
        }
648
649
        return array(
650
            'width'  => $width,
651
            'height' => $height,
652
        );
653
    }
654
655
    /**
656
     * Lazyload functions
657
     */
658
    public static function should_lazyload_wrapper() {
659
        // needed in autoptimizeMain.php.
660
        $self = new self();
661
        return $self->should_lazyload();
662
    }
663
664
    public function should_lazyload( $context = '' ) {
665
        if ( ! empty( $this->options['autoptimize_imgopt_checkbox_field_3'] ) && false === $this->check_nolazy() ) {
666
            $lazyload_return = true;
667
        } else {
668
            $lazyload_return = false;
669
        }
670
        $lazyload_return = apply_filters( 'autoptimize_filter_imgopt_should_lazyload', $lazyload_return, $context );
671
672
        return $lazyload_return;
673
    }
674
675
    public function check_nolazy() {
676
        if ( array_key_exists( 'ao_nolazy', $_GET ) && '1' === $_GET['ao_nolazy'] ) {
677
            return true;
678
        } else {
679
            return false;
680
        }
681
    }
682
683
    public function filter_lazyload_images( $in )
684
    {
685
        // only used is image optimization is NOT active but lazyload is.
686
        $to_replace = array();
687
688
        // hide (no)script tags to avoid nesting noscript tags (as lazyloaded images add noscript).
689
        $out = autoptimizeBase::replace_contents_with_marker_if_exists(
690
            'SCRIPT',
691
            '<script',
692
            '#<(?:no)?script.*?<\/(?:no)?script>#is',
693
            $in
694
        );
695
696
        // extract img tags and add lazyload attribs.
697
        if ( preg_match_all( '#<img[^>]*src[^>]*>#Usmi', $out, $matches ) ) {
698
            foreach ( $matches[0] as $tag ) {
699
                if ( $this->should_lazyload( $out ) ) {
700
                    $to_replace[ $tag ] = $this->add_lazyload( $tag );
701
                }
702
            }
703
            $out = str_replace( array_keys( $to_replace ), array_values( $to_replace ), $out );
704
        }
705
706
        // and also lazyload picture tag.
707
        $out = $this->process_picture_tag( $out, false, true );
708
709
        // and inline style blocks with background-image.
710
        $out = $this->process_bgimage( $out );
711
712
        // restore noscript tags.
713
        $out = autoptimizeBase::restore_marked_content(
714
            'SCRIPT',
715
            $out
716
        );
717
718
        return $out;
719
    }
720
721
    public function add_lazyload( $tag, $placeholder = '' ) {
722
        // adds actual lazyload-attributes to an image node.
723
        if ( str_ireplace( $this->get_lazyload_exclusions(), '', $tag ) === $tag ) {
724
            $tag = $this->maybe_fix_missing_quotes( $tag );
725
726
            // store original tag for use in noscript version.
727
            $noscript_tag = '<noscript>' . autoptimizeUtils::remove_id_from_node( $tag ) . '</noscript>';
728
729
            $lazyload_class = apply_filters( 'autoptimize_filter_imgopt_lazyload_class', 'lazyload' );
730
731
            // insert lazyload class.
732
            $tag = $this->inject_classes_in_tag( $tag, "$lazyload_class " );
733
734
            if ( ! $placeholder || empty( $placeholder ) ) {
735
                // get image width & heigth for placeholder fun (and to prevent content reflow).
736
                $_get_size = $this->get_size_from_tag( $tag );
737
                $width     = $_get_size['width'];
738
                $height    = $_get_size['height'];
739
                if ( false === $width ) {
740
                    $widht = 210; // default width for SVG placeholder.
0 ignored issues
show
Unused Code introduced by
$widht 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...
741
                }
742
                if ( false === $height ) {
743
                    $heigth = $width / 3 * 2; // if no height, base it on width using the 3/2 aspect ratio.
0 ignored issues
show
Unused Code introduced by
$heigth 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...
744
                }
745
746
                // insert the actual lazyload stuff.
747
                // see https://css-tricks.com/preventing-content-reflow-from-lazy-loaded-images/ for great read on why we're using empty svg's.
748
                $placeholder = apply_filters( 'autoptimize_filter_imgopt_lazyload_placeholder', $this->get_default_lazyload_placeholder( $width, $height ) );
749
            }
750
751
            $tag = preg_replace( '/(\s)src=/', ' src=\'' . $placeholder . '\' data-src=', $tag );
752
            $tag = preg_replace( '/(\s)srcset=/', ' data-srcset=', $tag );
753
754
            // move sizes to data-sizes unless filter says no.
755
            if ( apply_filters( 'autoptimize_filter_imgopt_lazyload_move_sizes', true ) ) {
756
                $tag = str_replace( ' sizes=', ' data-sizes=', $tag );
757
            }
758
759
            // add the noscript-tag from earlier.
760
            $tag = $noscript_tag . $tag;
761
            $tag = apply_filters( 'autoptimize_filter_imgopt_lazyloaded_img', $tag );
762
        }
763
764
        return $tag;
765
    }
766
767
    public function add_lazyload_js_footer() {
768
        if ( false === autoptimizeMain::should_buffer() ) {
769
            return;
770
        }
771
772
        // The JS will by default be excluded form autoptimization but this can be changed with a filter.
773
        $noptimize_flag = '';
774
        if ( apply_filters( 'autoptimize_filter_imgopt_lazyload_js_noptimize', true ) ) {
775
            $noptimize_flag = ' data-noptimize="1"';
776
        }
777
778
        $lazysizes_js = plugins_url( 'external/js/lazysizes.min.js', __FILE__ );
779
        $cdn_url      = $this->get_cdn_url();
780
        if ( ! empty( $cdn_url ) ) {
781
            $lazysizes_js = str_replace( AUTOPTIMIZE_WP_SITE_URL, $cdn_url, $lazysizes_js );
782
        }
783
784
        // Adds lazyload CSS & JS to footer, using echo because wp_enqueue_script seems not to support pushing attributes (async).
785
        echo apply_filters( 'autoptimize_filter_imgopt_lazyload_cssoutput', '<style>.lazyload,.lazyloading{opacity:0;}.lazyloaded{opacity:1;transition:opacity 300ms;}</style><noscript><style>.lazyload{display:none;}</style></noscript>' );
786
        echo apply_filters( 'autoptimize_filter_imgopt_lazyload_jsconfig', '<script' . $noptimize_flag . '>window.lazySizesConfig=window.lazySizesConfig||{};window.lazySizesConfig.loadMode=1;</script>' );
787
        echo apply_filters( 'autoptimize_filter_imgopt_lazyload_js', '<script async' . $noptimize_flag . ' src=\'' . $lazysizes_js . '\'></script>' );
788
789
        // And add webp detection and loading JS.
790
        if ( $this->should_webp() ) {
791
            $_webp_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);";
792
            $_webp_load   = "document.addEventListener('lazybeforeunveil',function({target:c}){supportsWebP&&['data-src','data-srcset'].forEach(function(a){attr=c.getAttribute(a),null!==attr&&c.setAttribute(a,attr.replace(/\/client\//,'/client/to_webp,'))})});";
793
            echo apply_filters( 'autoptimize_filter_imgopt_webp_js', '<script' . $noptimize_flag . '>' . $_webp_detect . $_webp_load . '</script>' );
794
        }
795
    }
796
797
    public function get_cdn_url() {
798
        // getting CDN url here to avoid having to make bigger changes to autoptimizeBase.
799
        static $cdn_url = null;
800
801
        if ( null === $cdn_url ) {
802
            $cdn_url = autoptimizeOptionWrapper::get_option( 'autoptimize_cdn_url', '' );
803
            $cdn_url = autoptimizeUtils::tweak_cdn_url_if_needed( $cdn_url );
804
            $cdn_url = apply_filters( 'autoptimize_filter_base_cdnurl', $cdn_url );
805
        }
806
807
        return $cdn_url;
808
    }
809
810
    public function get_lazyload_exclusions() {
811
        // returns array of strings that if found in an <img tag will stop the img from being lazy-loaded.
812
        static $exclude_lazyload_array = null;
813
814
        if ( null === $exclude_lazyload_array ) {
815
            $options = $this->options;
816
817
            // set default exclusions.
818
            $exclude_lazyload_array = array( 'skip-lazy', 'data-no-lazy', 'notlazy', 'data-src', 'data-srcset', 'data:image/', 'data-lazyload', 'rev-slidebg' );
819
820
            // add from setting.
821
            if ( array_key_exists( 'autoptimize_imgopt_text_field_5', $options ) ) {
822
                $exclude_lazyload_option = $options['autoptimize_imgopt_text_field_5'];
823 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...
824
                    $exclude_lazyload_array = array_merge( $exclude_lazyload_array, array_filter( array_map( 'trim', explode( ',', $options['autoptimize_imgopt_text_field_5'] ) ) ) );
825
                }
826
            }
827
828
            // and filter for developer-initiated changes.
829
            $exclude_lazyload_array = apply_filters( 'autoptimize_filter_imgopt_lazyload_exclude_array', $exclude_lazyload_array );
830
        }
831
832
        return $exclude_lazyload_array;
833
    }
834
835
    public function inject_classes_in_tag( $tag, $target_class ) {
836
        if ( strpos( $tag, 'class=' ) !== false ) {
837
            $tag = preg_replace( '/(\sclass\s?=\s?("|\'))/', '$1' . $target_class, $tag );
838
        } else {
839
            $tag = preg_replace( '/(<img)\s/', '$1 class="' . trim( $target_class ) . '" ', $tag );
840
        }
841
842
        return $tag;
843
    }
844
845
    public function get_default_lazyload_placeholder( $imgopt_w, $imgopt_h ) {
846
        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';
847
    }
848
849
    public function should_webp() {
850
        static $webp_return = null;
851
852
        if ( is_null( $webp_return ) ) {
853
            // webp only works if imgopt and lazyload are also active.
854
            if ( ! empty( $this->options['autoptimize_imgopt_checkbox_field_4'] ) && ! empty( $this->options['autoptimize_imgopt_checkbox_field_3'] ) && $this->imgopt_active() ) {
855
                $webp_return = true;
856
            } else {
857
                $webp_return = false;
858
            }
859
        }
860
861
        return $webp_return;
862
    }
863
864
    public function process_picture_tag( $in, $imgopt = false, $lazy = false ) {
865
        // check if "<picture" is present and if filter allows us to process <picture>.
866
        if ( strpos( $in, '<picture' ) === false || apply_filters( 'autoptimize_filter_imgopt_dopicture', true ) === false ) {
867
            return $in;
868
        }
869
870
        $_exclusions     = $this->get_lazyload_exclusions();
871
        $to_replace_pict = array();
872
873
        // extract and process each picture-node.
874
        preg_match_all( '#<picture.*</picture>#Usmi', $in, $_pictures, PREG_SET_ORDER );
875
        foreach ( $_pictures as $_picture ) {
876
            $_picture = $this->maybe_fix_missing_quotes( $_picture );
877
            if ( strpos( $_picture[0], '<source ' ) !== false && preg_match_all( '#<source .*srcset=(?:"|\')(?!data)(.*)(?:"|\').*>#Usmi', $_picture[0], $_sources, PREG_SET_ORDER ) !== false ) {
878
                foreach ( $_sources as $_source ) {
879
                    $_picture_replacement = $_source[0];
880
881
                    // should we optimize the image?
882
                    if ( $imgopt && $this->can_optimize_image( $_source[1] ) ) {
883
                        $_picture_replacement = str_replace( $_source[1], $this->build_imgopt_url( $_source[1] ), $_picture_replacement );
884
                    }
885
                    // should we lazy-load?
886
                    if ( $lazy && $this->should_lazyload() && str_ireplace( $_exclusions, '', $_picture_replacement ) === $_picture_replacement ) {
887
                        $_picture_replacement = str_replace( ' srcset=', ' data-srcset=', $_picture_replacement );
888
                    }
889
                    $to_replace_pict[ $_source[0] ] = $_picture_replacement;
890
                }
891
            }
892
        }
893
894
        // and return the fully procesed $in.
895
        $out = str_replace( array_keys( $to_replace_pict ), array_values( $to_replace_pict ), $in );
896
897
        return $out;
898
    }
899
900
    public function process_bgimage( $in ) {
901 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...
902
            $out = preg_replace_callback(
903
                '/(<(?:article|aside|body|div|footer|header|p|section|table)[^>]*)\sstyle=(?:"|\').*?background-image:\s?url\((?:"|\')?([^"\')]*)(?:"|\')?\)[^>]*/',
904
                array( $this, 'lazyload_bgimg_callback' ),
905
                $in
906
            );
907
            return $out;
908
        }
909
        return $in;
910
    }
911
912
    public function lazyload_bgimg_callback( $matches ) {
913
        if ( str_ireplace( $this->get_lazyload_exclusions(), '', $matches[0] ) === $matches[0] ) {
914
            // get placeholder & lazyload class strings.
915
            $placeholder    = apply_filters( 'autoptimize_filter_imgopt_lazyload_placeholder', $this->get_default_lazyload_placeholder( 500, 300 ) );
916
            $lazyload_class = apply_filters( 'autoptimize_filter_imgopt_lazyload_class', 'lazyload' );
917
            // replace background-image URL with SVG placeholder.
918
            $out = str_replace( $matches[2], $placeholder, $matches[0] );
919
            // add data-bg attribute with real background-image URL for lazyload to pick up.
920
            $out = str_replace( $matches[1], $matches[1] . ' data-bg="' . $matches[2] . '"', $out );
921
            // add lazyload class to tag.
922
            $out = $this->inject_classes_in_tag( $out, "$lazyload_class " );
923
            return $out;
924
        }
925
        return $matches[0];
926
    }
927
928
    public function maybe_fix_missing_quotes( $tag_in ) {
929
        // W3TC's Minify_HTML class removes quotes around attribute value, this re-adds them for the class attribute only so we can safely add the lazyload class.
930
        if ( file_exists( WP_PLUGIN_DIR . '/w3-total-cache/w3-total-cache.php' ) && class_exists( 'Minify_HTML' ) && apply_filters( 'autoptimize_filter_imgopt_fixquotes', true ) ) {
931
            return preg_replace( '/class\s?=([^("|\')]*)(\s|>)/U', 'class=\'$1\'$2', $tag_in );
932
        } else {
933
            return $tag_in;
934
        }
935
    }
936
937
    /**
938
     * Admin page logic and related functions below.
939
     */
940
    public function imgopt_admin_menu()
941
    {
942
        // no acces if multisite and not network admin and no site config allowed.
943
        if ( autoptimizeConfig::should_show_menu_tabs() ) {
944
            add_submenu_page(
945
                null,
946
                'autoptimize_imgopt',
947
                'autoptimize_imgopt',
948
                'manage_options',
949
                'autoptimize_imgopt',
950
                array( $this, 'imgopt_options_page' )
951
            );
952
            register_setting( 'autoptimize_imgopt_settings', 'autoptimize_imgopt_settings' );
953
        }
954
    }
955
956
    public function add_imgopt_tab( $in )
957
    {
958
        if ( autoptimizeConfig::should_show_menu_tabs() ) {
959
            $in = array_merge( $in, array( 'autoptimize_imgopt' => __( 'Images', 'autoptimize' ) ) );
960
        }
961
962
        return $in;
963
    }
964
965
    public function imgopt_options_page()
966
    {
967
        // Check querystring for "refreshCacheChecker" and call cachechecker if so.
968
        if ( array_key_exists( 'refreshImgProvStats', $_GET ) && 1 == $_GET['refreshImgProvStats'] ) {
969
            $this->query_img_provider_stats();
970
        }
971
972
        $options       = $this->fetch_options();
973
        $sp_url_suffix = $this->get_service_url_suffix();
974
        ?>
975
    <style>
976
        #ao_settings_form {background: white;border: 1px solid #ccc;padding: 1px 15px;margin: 15px 10px 10px 0;}
977
        #ao_settings_form .form-table th {font-weight: normal;}
978
        #autoptimize_imgopt_descr{font-size: 120%;}
979
    </style>
980
    <script>document.title = "Autoptimize: <?php _e( 'Images', 'autoptimize' ); ?> " + document.title;</script>
981
    <div class="wrap">
982
    <h1><?php _e( 'Autoptimize Settings', 'autoptimize' ); ?></h1>
983
        <?php echo autoptimizeConfig::ao_admin_tabs(); ?>
984
        <?php if ( 'down' === $options['availabilities']['extra_imgopt']['status'] ) { ?>
985
            <div class="notice-warning notice"><p>
986
            <?php
987
            // translators: "Autoptimize support forum" will appear in a "a href".
988
            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>' );
989
            ?>
990
            </p></div>
991
        <?php } ?>
992
993
        <?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...
994
            <div class="notice-warning notice"><p>
995
            <?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' ); ?>
996
            </p></div>
997
        <?php } ?>
998
999
        <?php if ( class_exists( 'Jetpack' ) && method_exists( 'Jetpack', 'get_active_modules' ) && in_array( 'photon', Jetpack::get_active_modules() ) ) { ?>
1000
            <div class="notice-warning notice"><p>
1001
            <?php
1002
            // translators: "disable  Jetpack's site accelerator for images" will appear in a "a href" linking to the jetpack settings page.
1003
            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>' );
1004
            ?>
1005
            </p></div>
1006
        <?php } ?>
1007
    <form id='ao_settings_form' action='<?php echo admin_url( 'options.php' ); ?>' method='post'>
1008
        <?php settings_fields( 'autoptimize_imgopt_settings' ); ?>
1009
        <h2><?php _e( 'Image optimization', 'autoptimize' ); ?></h2>
1010
        <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 support included!', 'autoptimize' ); ?></span>
1011
        <table class="form-table">
1012
            <tr>
1013
                <th scope="row"><?php _e( 'Optimize Images', 'autoptimize' ); ?></th>
1014
                <td>
1015
                    <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>
1016
                    <?php
1017
                    // show shortpixel status.
1018
                    $_notice = autoptimizeImages::instance()->get_imgopt_status_notice();
1019
                    if ( $_notice ) {
1020
                        switch ( $_notice['status'] ) {
1021
                            case 2:
1022
                                $_notice_color = 'green';
1023
                                break;
1024
                            case 1:
1025
                                $_notice_color = 'orange';
1026
                                break;
1027
                            case -1:
1028
                            case -2:
1029
                            case -3:
1030
                                $_notice_color = 'red';
1031
                                break;
1032
                            default:
1033
                                $_notice_color = 'green';
1034
                        }
1035
                        echo apply_filters( 'autoptimize_filter_imgopt_settings_status', '<p><strong><span style="color:' . $_notice_color . ';">' . __( 'Shortpixel status: ', 'autoptimize' ) . '</span></strong>' . $_notice['notice'] . '</p>' );
1036
                    } else {
1037
                        // translators: link points to shortpixel.
1038
                        $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 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>' );
1039
                        if ( 'launch' === $options['availabilities']['extra_imgopt']['status'] ) {
1040
                            $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' );
1041
                        } else {
1042
                            // translators: link points to shortpixel.
1043
                            $upsell_msg_2 = sprintf( __( '%1$sSign-up now%2$s to receive a 1 000 bonus + 50&#37; more image optimization credits regardless of the traffic used. More image optimizations can be purchased starting with $4.99.', 'autoptimize' ), '<a href="https://shortpixel.com/aospai' . $sp_url_suffix . '" target="_blank">', '</a>' );
1044
                        }
1045
                        echo apply_filters( 'autoptimize_imgopt_imgopt_settings_copy', $upsell_msg_1 . ' ' . $upsell_msg_2 . '</p>' );
1046
                    }
1047
                    // translators: link points to shortpixel FAQ.
1048
                    $faqcopy = sprintf( __( '<strong>Questions</strong>? Have a look at the %1$sShortPixel FAQ%2$s!', 'autoptimize' ), '<strong><a href="https://shortpixel.helpscoutdocs.com/category/60-shortpixel-ai-cdn" target="_blank">', '</strong></a>' );
1049
                    $faqcopy = $faqcopy . ' ' . __( 'Only works for sites/ images that are publicly available.', 'autoptimize' );
1050
                    // translators: links points to shortpixel TOS & Privacy Policy.
1051
                    $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>' );
1052
                    echo apply_filters( 'autoptimize_imgopt_imgopt_settings_tos', '<p>' . $faqcopy . ' ' . $toscopy . '</p>' );
1053
                    ?>
1054
                </td>
1055
            </tr>
1056 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...
1057
                <th scope="row"><?php _e( 'Image Optimization quality', 'autoptimize' ); ?></th>
1058
                <td>
1059
                    <label>
1060
                    <select name='autoptimize_imgopt_settings[autoptimize_imgopt_select_field_2]'>
1061
                        <?php
1062
                        $_imgopt_array = autoptimizeImages::instance()->get_img_quality_array();
1063
                        $_imgopt_val   = autoptimizeImages::instance()->get_img_quality_setting();
1064
1065
                        foreach ( $_imgopt_array as $key => $value ) {
1066
                            echo '<option value="' . $key . '"';
1067
                            if ( $_imgopt_val == $key ) {
1068
                                echo ' selected';
1069
                            }
1070
                            echo '>' . ucfirst( $value ) . '</option>';
1071
                        }
1072
                        echo "\n";
1073
                        ?>
1074
                    </select>
1075
                    </label>
1076
                    <p>
1077
                        <?php
1078
                            // translators: link points to shortpixel image test page.
1079
                            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>' ) );
1080
                        ?>
1081
                    </p>
1082
                </td>
1083
            </tr>
1084 View Code Duplication
            <tr id='autoptimize_imgopt_webp' <?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...
1085
                <th scope="row"><?php _e( 'Load WebP in supported browsers?', 'autoptimize' ); ?></th>
1086
                <td>
1087
                    <label><input type='checkbox' id='autoptimize_imgopt_webp_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 image format to any browser that supports it (requires lazy load to be active).', 'autoptimize' ); ?></label>
1088
                </td>
1089
            </tr>
1090
            <tr>
1091
                <th scope="row"><?php _e( 'Lazy-load images?', 'autoptimize' ); ?></th>
1092
                <td>
1093
                    <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>
1094
                </td>
1095
            </tr>
1096 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...
1097
                <th scope="row"><?php _e( 'Lazy-load exclusions', 'autoptimize' ); ?></th>
1098
                <td>
1099
                    <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>
1100
                </td>
1101
            </tr>
1102
        </table>
1103
        <p class="submit"><input type="submit" name="submit" id="submit" class="button button-primary" value="<?php _e( 'Save Changes', 'autoptimize' ); ?>" /></p>
1104
    </form>
1105
    <script>
1106
        jQuery(document).ready(function() {
1107
            jQuery("#autoptimize_imgopt_checkbox").change(function() {
1108
                if (this.checked) {
1109
                    jQuery("#autoptimize_imgopt_quality").show("slow");
1110
                    jQuery("#autoptimize_imgopt_webp").show("slow");
1111
                } else {
1112
                    jQuery("#autoptimize_imgopt_quality").hide("slow");
1113
                    jQuery("#autoptimize_imgopt_webp").hide("slow");
1114
                }
1115
            });
1116
            jQuery("#autoptimize_imgopt_webp_checkbox").change(function() {
1117
                if (this.checked) {
1118
                    jQuery("#autoptimize_imgopt_lazyload_checkbox")[0].checked = true;
1119
                    jQuery("#autoptimize_imgopt_lazyload_exclusions").show("slow");
1120
                }
1121
            });
1122
            jQuery("#autoptimize_imgopt_lazyload_checkbox").change(function() {
1123
                if (this.checked) {
1124
                    jQuery("#autoptimize_imgopt_lazyload_exclusions").show("slow");
1125
                } else {
1126
                    jQuery("#autoptimize_imgopt_lazyload_exclusions").hide("slow");
1127
                    jQuery("#autoptimize_imgopt_webp_checkbox")[0].checked = false;
1128
                }
1129
            });
1130
        });
1131
    </script>
1132
        <?php
1133
    }
1134
1135
    /**
1136
     * Ïmg opt status as used on dashboard.
1137
     */
1138
    public function get_imgopt_status_notice() {
1139
        if ( $this->imgopt_active() ) {
1140
            $_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...
1141
            $_stat           = autoptimizeOptionWrapper::get_option( 'autoptimize_imgopt_provider_stat', '' );
1142
            $_site_host      = AUTOPTIMIZE_SITE_DOMAIN;
1143
            $_imgopt_upsell  = 'https://shortpixel.com/aospai/af/GWRGFLW109483/' . $_site_host;
1144
            $_imgopt_assoc   = 'https://shortpixel.helpscoutdocs.com/article/94-how-to-associate-a-domain-to-my-account';
1145
            $_imgopt_unreach = 'https://shortpixel.helpscoutdocs.com/article/148-why-are-my-images-redirected-from-cdn-shortpixel-ai';
1146
1147
            if ( is_array( $_stat ) ) {
1148
                if ( 1 == $_stat['Status'] ) {
1149
                    // translators: "add more credits" will appear in a "a href".
1150
                    $_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>' );
1151
                } elseif ( -1 == $_stat['Status'] || -2 == $_stat['Status'] ) {
1152
                    // translators: "add more credits" will appear in a "a href".
1153
                    $_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>' );
1154
                    // translators: "associate your domain" will appear in a "a href".
1155
                    $_imgopt_notice = $_imgopt_notice . ' ' . sprintf( __( 'If you already have enough credits 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>' );
1156
                } elseif ( -3 == $_stat['Status'] ) {
1157
                    // translators: "add more credits" will appear in a "a href".
1158
                    $_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>' );
1159
                } else {
1160
                    $_imgopt_upsell = 'https://shortpixel.com/g/af/GWRGFLW109483';
1161
                    // translators: "log in to check your account" will appear in a "a href".
1162
                    $_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>' );
1163
                }
1164
1165
                // add info on freshness + refresh link if status is not 2 (good shape).
1166
                if ( 2 != $_stat['Status'] ) {
1167
                    $_imgopt_stats_refresh_url = add_query_arg( array(
1168
                        'page'                => 'autoptimize_imgopt',
1169
                        'refreshImgProvStats' => '1',
1170
                    ), admin_url( 'options-general.php' ) );
1171
                    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...
1172
                        $_imgopt_stats_last_run = __( 'based on status at ', 'autoptimize' ) . date_i18n( autoptimizeOptionWrapper::get_option( 'time_format' ), $_stat['timestamp'] );
1173
                    } else {
1174
                        $_imgopt_stats_last_run = __( 'based on previously fetched data', 'autoptimize' );
1175
                    }
1176
                    $_imgopt_notice .= ' (' . $_imgopt_stats_last_run . ', ';
1177
                    // translators: "here to refresh" links to the Autoptimize Extra page and forces a refresh of the img opt stats.
1178
                    $_imgopt_notice .= sprintf( __( 'click %1$shere to refresh%2$s', 'autoptimize' ), '<a href="' . $_imgopt_stats_refresh_url . '">', '</a>).' );
1179
                }
1180
1181
                // and make the full notice filterable.
1182
                $_imgopt_notice = apply_filters( 'autoptimize_filter_imgopt_notice', $_imgopt_notice );
1183
1184
                return array(
1185
                    'status' => $_stat['Status'],
1186
                    'notice' => $_imgopt_notice,
1187
                );
1188
            }
1189
        }
1190
        return false;
1191
    }
1192
1193
    public static function get_imgopt_status_notice_wrapper() {
1194
        // needed for notice being shown in autoptimizeCacheChecker.php.
1195
        $self = new self();
1196
        return $self->get_imgopt_status_notice();
1197
    }
1198
1199
    /**
1200
     * Get img provider stats (used to display notice).
1201
     */
1202
    public function query_img_provider_stats() {
1203
        if ( ! empty( $this->options['autoptimize_imgopt_checkbox_field_1'] ) ) {
1204
            $url      = '';
1205
            $endpoint = $this->get_imgopt_host() . 'read-domain/';
1206
            $domain   = AUTOPTIMIZE_SITE_DOMAIN;
1207
1208
            // make sure parse_url result makes sense, keeping $url empty if not.
1209
            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...
1210
                $url = $endpoint . $domain;
1211
            }
1212
1213
            $url = apply_filters(
1214
                'autoptimize_filter_imgopt_stat_url',
1215
                $url
1216
            );
1217
1218
            // only do the remote call if $url is not empty to make sure no parse_url
1219
            // weirdness results in useless calls.
1220 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...
1221
                $response = wp_remote_get( $url );
1222
                if ( ! is_wp_error( $response ) ) {
1223
                    if ( '200' == wp_remote_retrieve_response_code( $response ) ) {
1224
                        $stats = json_decode( wp_remote_retrieve_body( $response ), true );
1225
                        autoptimizeOptionWrapper::update_option( 'autoptimize_imgopt_provider_stat', $stats );
1226
                    }
1227
                }
1228
            }
1229
        }
1230
    }
1231
1232
    public static function get_img_provider_stats()
1233
    {
1234
        // wrapper around query_img_provider_stats() so we can get to $this->options from cronjob() in autoptimizeCacheChecker.
1235
        $self = new self();
1236
        return $self->query_img_provider_stats();
1237
    }
1238
1239
    /**
1240
     * Determines and returns the service launch status.
1241
     *
1242
     * @return bool
1243
     */
1244
    public function launch_ok()
1245
    {
1246
        static $launch_status = null;
1247
1248
        if ( null === $launch_status ) {
1249
            $avail_imgopt  = $this->options['availabilities']['extra_imgopt'];
1250
            $magic_number  = intval( substr( md5( parse_url( AUTOPTIMIZE_WP_SITE_URL, PHP_URL_HOST ) ), 0, 3 ), 16 );
1251
            $has_launched  = autoptimizeOptionWrapper::get_option( 'autoptimize_imgopt_launched', '' );
1252
            $launch_status = false;
1253
            if ( $has_launched || ( is_array( $avail_imgopt ) && array_key_exists( 'launch-threshold', $avail_imgopt ) && $magic_number < $avail_imgopt['launch-threshold'] ) ) {
1254
                $launch_status = true;
1255
                if ( ! $has_launched ) {
1256
                    autoptimizeOptionWrapper::update_option( 'autoptimize_imgopt_launched', 'on' );
1257
                }
1258
            }
1259
        }
1260
1261
        return $launch_status;
1262
    }
1263
1264
    public static function launch_ok_wrapper() {
1265
        // needed for "plug" notice in autoptimizeMain.php.
1266
        $self = new self();
1267
        return $self->launch_ok();
1268
    }
1269
1270
    public function get_imgopt_provider_userstatus() {
1271
        static $_provider_userstatus = null;
1272
1273
        if ( is_null( $_provider_userstatus ) ) {
1274
            $_stat = autoptimizeOptionWrapper::get_option( 'autoptimize_imgopt_provider_stat', '' );
1275
            if ( is_array( $_stat ) ) {
1276
                if ( array_key_exists( 'Status', $_stat ) ) {
1277
                    $_provider_userstatus['Status'] = $_stat['Status'];
1278
                } else {
1279
                    // if no stats then we assume all is well.
1280
                    $_provider_userstatus['Status'] = 2;
1281
                }
1282
                if ( array_key_exists( 'timestamp', $_stat ) ) {
1283
                    $_provider_userstatus['timestamp'] = $_stat['timestamp'];
1284
                } else {
1285
                    // if no timestamp then we return "".
1286
                    $_provider_userstatus['timestamp'] = '';
1287
                }
1288
            } else {
1289
                // no provider_stat yet, assume/ return all OK.
1290
                $_provider_userstatus['Status']    = 2;
1291
                $_provider_userstatus['timestamp'] = '';
1292
            }
1293
        }
1294
1295
        return $_provider_userstatus;
1296
    }
1297
}
1298