Completed
Push — master ( 888448...89a848 )
by frank
07:21 queued 03:15
created

autoptimizeImages::can_optimize_image()   C

Complexity

Conditions 15
Paths 28

Size

Total Lines 39

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 15
nc 28
nop 1
dl 0
loc 39
rs 5.9166
c 0
b 0
f 0

How to fix   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 = 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'] = 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 = get_option( 'autoptimize_imgopt_settings', '' );
70
            if ( ! empty( $opts ) && is_array( $opts ) && array_key_exists( 'autoptimize_imgopt_checkbox_field_1', $opts ) && ! empty( $opts['autoptimize_imgopt_checkbox_field_1'] ) && '1' === $opts['autoptimize_imgopt_checkbox_field_1'] ) {
71
                $imgopt_active = true;
72
            } else {
73
                $imgopt_active = false;
74
            }
75
        }
76
77
        return $imgopt_active;
78
    }
79
80
    /**
81
     * Helper for getting a singleton instance. While being an
82
     * anti-pattern generally, it comes in handy for now from a
83
     * readability/maintainability perspective, until we get some
84
     * proper dependency injection going.
85
     *
86
     * @return self
87
     */
88
    public static function instance()
89
    {
90
        if ( null === self::$instance ) {
91
            self::$instance = new self();
92
        }
93
94
        return self::$instance;
95
    }
96
97 View Code Duplication
    public function run()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
98
    {
99
        if ( is_admin() ) {
100
            add_action( 'admin_menu', array( $this, 'imgopt_admin_menu' ) );
101
            add_filter( 'autoptimize_filter_settingsscreen_tabs', array( $this, 'add_imgopt_tab' ), 9 );
102
        } else {
103
            $this->run_on_frontend();
104
        }
105
    }
106
107
    public function run_on_frontend() {
108
        if ( ! $this->should_run() ) {
109
            if ( $this->should_lazyload() ) {
110
                add_filter(
111
                    'autoptimize_html_after_minify',
112
                    array( $this, 'filter_lazyload_images' ),
113
                    10,
114
                    1
115
                );
116
                add_action(
117
                    'wp_footer',
118
                    array( $this, 'add_lazyload_js_footer' ),
119
                    10,
120
                    0
121
                );
122
            }
123
            return;
124
        }
125
126
        $active = false;
127
128
        if ( apply_filters( 'autoptimize_filter_imgopt_do', true ) ) {
129
            add_filter(
130
                'autoptimize_html_after_minify',
131
                array( $this, 'filter_optimize_images' ),
132
                10,
133
                1
134
            );
135
            $active = true;
136
        }
137
138
        if ( apply_filters( 'autoptimize_filter_imgopt_do_css', true ) ) {
139
            add_filter(
140
                'autoptimize_filter_base_replace_cdn',
141
                array( $this, 'filter_optimize_css_images' ),
142
                10,
143
                1
144
            );
145
            $active = true;
146
        }
147
148
        if ( $active ) {
149
            add_filter(
150
                'autoptimize_extra_filter_tobepreconn',
151
                array( $this, 'filter_preconnect_imgopt_url' ),
152
                10,
153
                1
154
            );
155
        }
156
157
        if ( $this->should_lazyload() ) {
158
            add_action(
159
                'wp_footer',
160
                array( $this, 'add_lazyload_js_footer' )
161
            );
162
        }
163
    }
164
165
    /**
166
     * Basic checks before we can run.
167
     *
168
     * @return bool
169
     */
170
    protected function should_run()
171
    {
172
        $opts              = $this->options;
173
        $service_not_down  = ( 'down' !== $opts['availabilities']['extra_imgopt']['status'] );
174
        $not_launch_status = ( 'launch' !== $opts['availabilities']['extra_imgopt']['status'] );
175
176
        $do_cdn      = true;
177
        $_userstatus = $this->get_imgopt_provider_userstatus();
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $_userstatus is correct as $this->get_imgopt_provider_userstatus() (which targets autoptimizeImages::get_i...t_provider_userstatus()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
178
        if ( -2 == $_userstatus['Status'] ) {
179
            $do_cdn = false;
180
        }
181
182
        if (
183
            $this->imgopt_active()
184
            && $do_cdn
185
            && $service_not_down
186
            && ( $not_launch_status || $this->launch_ok() )
187
        ) {
188
            return true;
189
        }
190
        return false;
191
    }
192
193
    public function get_imgopt_host()
194
    {
195
        static $imgopt_host = null;
196
197
        if ( null === $imgopt_host ) {
198
            $imgopt_host  = 'https://cdn.shortpixel.ai/';
199
            $avail_imgopt = $this->options['availabilities']['extra_imgopt'];
200
            if ( ! empty( $avail_imgopt ) && array_key_exists( 'hosts', $avail_imgopt ) && is_array( $avail_imgopt['hosts'] ) ) {
201
                $imgopt_host = array_rand( array_flip( $avail_imgopt['hosts'] ) );
202
            }
203
        }
204
205
        return $imgopt_host;
206
    }
207
208
    public static function get_imgopt_host_wrapper()
209
    {
210
        // needed for CI tests.
211
        $self = new self();
212
        return $self->get_imgopt_host();
213
    }
214
215
    public static function get_service_url_suffix()
216
    {
217
        $suffix = '/af/GWRGFLW109483/' . AUTOPTIMIZE_SITE_DOMAIN;
218
219
        return $suffix;
220
    }
221
222
    public function get_img_quality_string()
223
    {
224
        static $quality = null;
225
226
        if ( null === $quality ) {
227
            $q_array = $this->get_img_quality_array();
228
            $setting = $this->get_img_quality_setting();
229
            $quality = apply_filters(
230
                'autoptimize_filter_imgopt_quality',
231
                'q_' . $q_array[ $setting ]
232
            );
233
        }
234
235
        return $quality;
236
    }
237
238
    public function get_img_quality_array()
239
    {
240
        static $map = null;
241
242
        if ( null === $map ) {
243
            $map = array(
244
                '1' => 'lossy',
245
                '2' => 'glossy',
246
                '3' => 'lossless',
247
            );
248
            $map = apply_filters(
249
                'autoptimize_filter_imgopt_quality_array',
250
                $map
251
            );
252
        }
253
254
        return $map;
255
    }
256
257
    public function get_img_quality_setting()
258
    {
259
        static $q = null;
260
261
        if ( null === $q ) {
262
            if ( is_array( $this->options ) && array_key_exists( 'autoptimize_imgopt_select_field_2', $this->options ) ) {
263
                $setting = $this->options['autoptimize_imgopt_select_field_2'];
264
            }
265
266
            if ( ! isset( $setting ) || empty( $setting ) || ( '1' !== $setting && '3' !== $setting ) ) {
267
                // default image opt. value is 2 ("glossy").
268
                $q = '2';
269
            } else {
270
                $q = $setting;
271
            }
272
        }
273
274
        return $q;
275
    }
276
277
    public function filter_preconnect_imgopt_url( array $in )
278
    {
279
        $url_parts = parse_url( $this->get_imgopt_base_url() );
280
        $in[]      = $url_parts['scheme'] . '://' . $url_parts['host'];
281
282
        return $in;
283
    }
284
285
    /**
286
     * Makes sure given url contains the full scheme and hostname
287
     * in case they're not present already.
288
     *
289
     * @param string $in Image url to normalize.
290
     *
291
     * @return string
292
     */
293
    private function normalize_img_url( $in )
294
    {
295
        // Only parse the site url once.
296
        static $parsed_site_url = null;
297
        if ( null === $parsed_site_url ) {
298
            $parsed_site_url = parse_url( site_url() );
299
        }
300
301
        // get CDN domain once.
302
        static $cdn_domain = null;
303
        if ( is_null( $cdn_domain ) ) {
304
            $cdn_url = apply_filters( 'autoptimize_filter_base_cdnurl', get_option( 'autoptimize_cdn_url', '' ) );
305
            if ( ! empty( $cdn_url ) ) {
306
                $cdn_domain = parse_url( $cdn_url, PHP_URL_HOST );
307
            } else {
308
                $cdn_domain = '';
309
            }
310
        }
311
312
        /**
313
         * This method gets called a lot, often for identical urls it seems.
314
         * `filter_optimize_css_images()` calls us, uses the resulting url and
315
         * gives it to `can_optimize_image()`, and if that returns trueish
316
         * then `build_imgopt_url()` is called (which, again, calls this method).
317
         * Until we dig deeper into whether this all must really happen that
318
         * way, having an internal cache here helps (to avoid doing repeated
319
         * identical string operations).
320
         */
321
        static $cache = null;
322
        if ( null === $cache ) {
323
            $cache = array();
324
        }
325
326
        // Do the work on cache miss only.
327
        if ( ! isset( $cache[ $in ] ) ) {
328
            // Default to what was given to us.
329
            $result = $in;
330
            if ( autoptimizeUtils::is_protocol_relative( $in ) ) {
331
                $result = $parsed_site_url['scheme'] . ':' . $in;
332
            } elseif ( 0 === strpos( $in, '/' ) ) {
333
                // Root-relative...
334
                $result = $parsed_site_url['scheme'] . '://' . $parsed_site_url['host'];
335
                // Add the path for subfolder installs.
336
                if ( isset( $parsed_site_url['path'] ) ) {
337
                    $result .= $parsed_site_url['path'];
338
                }
339
                $result .= $in;
340
            } elseif ( ! empty( $cdn_domain ) && strpos( $in, $cdn_domain ) !== 0 ) {
341
                $result = str_replace( $cdn_domain, $parsed_site_url['host'], $in );
342
            }
343
344
            $result = apply_filters( 'autoptimize_filter_imgopt_normalized_url', $result );
345
346
            // Store in cache.
347
            $cache[ $in ] = $result;
348
        }
349
350
        return $cache[ $in ];
351
    }
352
353
    public function filter_optimize_css_images( $in )
354
    {
355
        $in = $this->normalize_img_url( $in );
356
357
        if ( $this->can_optimize_image( $in ) ) {
358
            return $this->build_imgopt_url( $in, '', '' );
359
        } else {
360
            return $in;
361
        }
362
    }
363
364
    private function get_imgopt_base_url()
365
    {
366
        static $imgopt_base_url = null;
367
368
        if ( null === $imgopt_base_url ) {
369
            $imgopt_host     = $this->get_imgopt_host();
370
            $quality         = $this->get_img_quality_string();
371
            $ret_val         = apply_filters( 'autoptimize_filter_imgopt_wait', 'ret_img' ); // values: ret_wait, ret_img, ret_json, ret_blank.
372
            $imgopt_base_url = $imgopt_host . 'client/' . $quality . ',' . $ret_val;
373
            $imgopt_base_url = apply_filters( 'autoptimize_filter_imgopt_base_url', $imgopt_base_url );
374
        }
375
376
        return $imgopt_base_url;
377
    }
378
379
    private function can_optimize_image( $url )
380
    {
381
        static $cdn_url      = null;
382
        static $nopti_images = null;
383
384
        if ( null === $cdn_url ) {
385
            $cdn_url = apply_filters(
386
                'autoptimize_filter_base_cdnurl',
387
                get_option( 'autoptimize_cdn_url', '' )
388
            );
389
        }
390
391
        if ( null === $nopti_images ) {
392
            $nopti_images = apply_filters( 'autoptimize_filter_imgopt_noptimize', '' );
393
        }
394
395
        $site_host  = AUTOPTIMIZE_SITE_DOMAIN;
396
        $url        = $this->normalize_img_url( $url );
397
        $url_parsed = parse_url( $url );
398
399
        if ( array_key_exists( 'host', $url_parsed ) && $url_parsed['host'] !== $site_host && empty( $cdn_url ) ) {
400
            return false;
401
        } elseif ( ! empty( $cdn_url ) && strpos( $url, $cdn_url ) === false && array_key_exists( 'host', $url_parsed ) && $url_parsed['host'] !== $site_host ) {
402
            return false;
403
        } elseif ( strpos( $url, '.php' ) !== false ) {
404
            return false;
405
        } elseif ( str_ireplace( array( '.png', '.gif', '.jpg', '.jpeg', '.webp' ), '', $url_parsed['path'] ) === $url_parsed['path'] ) {
406
            // fixme: better check against end of string.
407
            return false;
408
        } elseif ( ! empty( $nopti_images ) ) {
409
            $nopti_images_array = array_filter( array_map( 'trim', explode( ',', $nopti_images ) ) );
410
            foreach ( $nopti_images_array as $nopti_image ) {
411
                if ( strpos( $url, $nopti_image ) !== false ) {
412
                    return false;
413
                }
414
            }
415
        }
416
        return true;
417
    }
418
419
    private function build_imgopt_url( $orig_url, $width = 0, $height = 0 )
420
    {
421
        // sanitize width and height.
422
        if ( strpos( $width, '%' ) !== false ) {
423
            $width = 0;
424
        }
425
        if ( strpos( $height, '%' ) !== false ) {
426
            $height = 0;
427
        }
428
        $width  = (int) $width;
429
        $height = (int) $height;
430
431
        $filtered_url = apply_filters(
432
            'autoptimize_filter_imgopt_build_url',
433
            $orig_url,
434
            $width,
435
            $height
436
        );
437
438
        // If filter modified the url, return that.
439
        if ( $filtered_url !== $orig_url ) {
440
            return $filtered_url;
441
        }
442
443
        $orig_url        = $this->normalize_img_url( $orig_url );
444
        $imgopt_base_url = $this->get_imgopt_base_url();
445
        $imgopt_size     = '';
446
447
        if ( $width && 0 !== $width ) {
448
            $imgopt_size = ',w_' . $width;
449
        }
450
451
        if ( $height && 0 !== $height ) {
452
            $imgopt_size .= ',h_' . $height;
453
        }
454
455
        $url = $imgopt_base_url . $imgopt_size . '/' . $orig_url;
456
457
        return $url;
458
    }
459
460
    public function replace_data_thumbs( $matches )
461
    {
462
        return $this->replace_img_callback( $matches, 150, 150 );
463
    }
464
465
    public function replace_img_callback( $matches, $width = 0, $height = 0 )
466
    {
467
        if ( $this->can_optimize_image( $matches[1] ) ) {
468
            return str_replace( $matches[1], $this->build_imgopt_url( $matches[1], $width, $height ), $matches[0] );
469
        } else {
470
            return $matches[0];
471
        }
472
    }
473
474
    public function filter_optimize_images( $in )
475
    {
476
        /*
477
         * potential future functional improvements:
478
         *
479
         * picture element.
480
         * filter for critical CSS.
481
         */
482
        $to_replace = array();
483
484
        // hide noscript tags to avoid nesting noscript tags (as lazyloaded images add noscript).
485
        if ( $this->should_lazyload() ) {
486
            $in = autoptimizeBase::replace_contents_with_marker_if_exists(
487
                'NOSCRIPT',
488
                '<noscript',
489
                '#<noscript.*?<\/noscript>#is',
490
                $in
491
            );
492
        }
493
494
        // extract img tags.
495
        if ( preg_match_all( '#<img[^>]*src[^>]*>#Usmi', $in, $matches ) ) {
496
            foreach ( $matches[0] as $tag ) {
497
                $orig_tag = $tag;
498
                $imgopt_w = '';
499
                $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...
500
501
                // first do (data-)srcsets.
502
                if ( preg_match_all( '#srcset=("|\')(.*)("|\')#Usmi', $tag, $allsrcsets, PREG_SET_ORDER ) ) {
503
                    foreach ( $allsrcsets as $srcset ) {
504
                        $srcset  = $srcset[2];
505
                        $srcsets = explode( ',', $srcset );
506
                        foreach ( $srcsets as $indiv_srcset ) {
507
                            $indiv_srcset_parts = explode( ' ', trim( $indiv_srcset ) );
508
                            if ( isset( $indiv_srcset_parts[1] ) && rtrim( $indiv_srcset_parts[1], 'w' ) !== $indiv_srcset_parts[1] ) {
509
                                $imgopt_w = rtrim( $indiv_srcset_parts[1], 'w' );
510
                            }
511
                            if ( $this->can_optimize_image( $indiv_srcset_parts[0] ) ) {
512
                                $imgopt_url = $this->build_imgopt_url( $indiv_srcset_parts[0], $imgopt_w, '' );
513
                                $tag        = str_replace( $indiv_srcset_parts[0], $imgopt_url, $tag );
514
                            }
515
                        }
516
                    }
517
                }
518
519
                // proceed with img src.
520
                // get width and height and add to $imgopt_size.
521
                $_get_size = $this->get_size_from_tag( $tag );
522
                $imgopt_w  = $_get_size['width'];
523
                $imgopt_h  = $_get_size['height'];
524
525
                // then start replacing images src.
526
                if ( preg_match_all( '#src=(?:"|\')(?!data)(.*)(?:"|\')#Usmi', $tag, $urls, PREG_SET_ORDER ) ) {
527
                    foreach ( $urls as $url ) {
528
                        $full_src_orig = $url[0];
529
                        $url           = $url[1];
530
                        if ( $this->can_optimize_image( $url ) ) {
531
                            $imgopt_url      = $this->build_imgopt_url( $url, $imgopt_w, $imgopt_h );
532
                            $full_imgopt_src = str_replace( $url, $imgopt_url, $full_src_orig );
533
                            $tag             = str_replace( $full_src_orig, $full_imgopt_src, $tag );
534
                        }
535
                    }
536
                }
537
538
                // do lazyload stuff.
539
                if ( $this->should_lazyload() && str_ireplace( $this->get_lazyload_exclusions(), '', $tag ) === $tag ) {
540
                    $noscript_tag = '<noscript>' . $tag . '</noscript>';
541
                    $tag          = str_replace( 'srcset=', 'data-srcset=', $tag );
542
543
                    // add lazyload class.
544
                    $tag = $this->inject_classes_in_tag( $tag, 'lazyload ' );
545
546
                    // set placeholder.
547
                    if ( strpos( $url, $this->get_imgopt_host() ) === 0 ) {
548
                        // if all img src have been replaced during srcset, we have to extract the
549
                        // origin url from the imgopt one to be able to set a lqip placeholder.
550
                        $_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...
551
                    } else {
552
                        $_url = $url;
553
                    }
554
                    if ( $this->can_optimize_image( $_url ) && apply_filters( 'autoptimize_filter_imgopt_lazyload_dolqip', true ) ) {
555
                        $placeholder = $this->get_imgopt_host() . 'client/q_lqip,ret_wait,w_' . $imgopt_w . ',h_' . $imgopt_h . '/' . $_url;
556
                    } else {
557
                        $placeholder = $this->get_default_lazyload_placeholder( $imgopt_w, $imgopt_h );
558
                    }
559
                    $placeholder = ' src=\'' . apply_filters( 'autoptimize_filter_imgopt_lazyload_placeholder', $placeholder );
560
561
                    // add min-heigth off by default as it can deform images, can be enabled with filter.
562
                    $min_height = '';
563
                    if ( apply_filters( 'autoptimize_filter_imgopt_lazyload_addminheight', false ) ) {
564
                        $min_height = ' style="min-height:' . $imgopt_h . 'px;"';
565
                    }
566
567
                    // add noscript & placeholder.
568
                    $tag = $noscript_tag . str_replace( ' src=', $min_height . $placeholder . '\' data-src=', $tag );
569
                    $tag = apply_filters( 'autoptimize_filter_imgopt_lazyloaded_img', $tag );
570
                }
571
572
                // add tag to array for later replacement.
573
                if ( $tag !== $orig_tag ) {
574
                    $to_replace[ $orig_tag ] = $tag;
575
                }
576
            }
577
        }
578
579
        // and replace all.
580
        $out = str_replace( array_keys( $to_replace ), array_values( $to_replace ), $in );
581
582
        // img thumbnails in e.g. woocommerce.
583
        if ( strpos( $out, 'data-thumb' ) !== false && apply_filters( 'autoptimize_filter_imgopt_datathumbs', true ) ) {
584
            $out = preg_replace_callback(
585
                '/\<div(?:[^>]?)\sdata-thumb\=(?:\"|\')(.+?)(?:\"|\')(?:[^>]*)?\>/s',
586
                array( $this, 'replace_data_thumbs' ),
587
                $out
588
            );
589
        }
590
591
        // background-image in inline style.
592
        if ( strpos( $out, 'background-image:' ) !== false && apply_filters( 'autoptimize_filter_imgopt_backgroundimages', true ) ) {
593
            $out = preg_replace_callback(
594
                '/style=(?:"|\').*?background-image:\s?url\((?:"|\')?([^"\')]*)(?:"|\')?\)/',
595
                array( $this, 'replace_img_callback' ),
596
                $out
597
            );
598
        }
599
600
        // and restore noscript tags if these were hidden for lazyload purposes.
601
        if ( $this->should_lazyload() ) {
602
            $out = autoptimizeBase::restore_marked_content(
603
                'NOSCRIPT',
604
                $out
605
            );
606
        }
607
608
        return $out;
609
    }
610
611
    public function get_size_from_tag( $tag ) {
612
        // reusable function to extract widht and height from an image tag
613
        // enforcing a filterable maximum width and height (default 4999X4999).
614
        $width  = '';
615
        $height = '';
616
617
        if ( preg_match( '#width=("|\')(.*)("|\')#Usmi', $tag, $_width ) ) {
618
            if ( strpos( $_width[2], '%' ) === false ) {
619
                $width = (int) $_width[2];
620
            }
621
        }
622
        if ( preg_match( '#height=("|\')(.*)("|\')#Usmi', $tag, $_height ) ) {
623
            if ( strpos( $_height[2], '%' ) === false ) {
624
                $height = (int) $_height[2];
625
            }
626
        }
627
628
        // check for and enforce (filterable) max sizes.
629
        $_max_width = apply_filters( 'autoptimize_filter_imgopt_max_width', 4999 );
630
        if ( $width > $_max_width ) {
631
            $_width = $_max_width;
632
            $height = $_width / $width * $height;
633
            $width  = $_width;
634
        }
635
        $_max_height = apply_filters( 'autoptimize_filter_imgopt_max_height', 4999 );
636
        if ( $height > $_max_height ) {
637
            $_height = $_max_height;
638
            $width   = $_height / $height * $width;
639
            $height  = $_height;
640
        }
641
642
        return array(
643
            'width'  => $width,
644
            'height' => $height,
645
        );
646
    }
647
648
    /**
649
     * Lazyload functions
650
     */
651
    public function should_lazyload() {
652
        if ( ! empty( $this->options['autoptimize_imgopt_checkbox_field_3'] ) ) {
653
            $lazyload_return = true;
654
        } else {
655
            $lazyload_return = false;
656
        }
657
658
        return $lazyload_return;
659
    }
660
661
    public static function should_lazyload_wrapper() {
662
        // needed in autoptimizeMain.php.
663
        $self = new self();
664
        return $self->should_lazyload();
665
    }
666
667
    public function filter_lazyload_images( $in )
668
    {
669
        // only used is image optimization is NOT active but lazyload is.
670
        $to_replace = array();
671
672
        // hide noscript tags to avoid nesting noscript tags (as lazyloaded images add noscript).
673
        $out = autoptimizeBase::replace_contents_with_marker_if_exists(
674
            'NOSCRIPT',
675
            '<noscript',
676
            '#<noscript.*?<\/noscript>#is',
677
            $in
678
        );
679
680
        // extract img tags and add lazyload attribs.
681
        if ( preg_match_all( '#<img[^>]*src[^>]*>#Usmi', $out, $matches ) ) {
682
            foreach ( $matches[0] as $tag ) {
683
                $to_replace[ $tag ] = $this->add_lazyload( $tag );
684
            }
685
            $out = str_replace( array_keys( $to_replace ), array_values( $to_replace ), $out );
686
        }
687
688
        // restore noscript tags.
689
        $out = autoptimizeBase::restore_marked_content(
690
            'NOSCRIPT',
691
            $out
692
        );
693
694
        return $out;
695
    }
696
697
    public function add_lazyload( $tag ) {
698
        // adds actual lazyload-attributes to an image node.
699
        if ( str_ireplace( $this->get_lazyload_exclusions(), '', $tag ) === $tag ) {
700
            // store original tag for use in noscript version.
701
            $noscript_tag = '<noscript>' . $tag . '</noscript>';
702
703
            // insert lazyload class.
704
            $tag = $this->inject_classes_in_tag( $tag, 'lazyload ' );
705
706
            // get image width & heigth for placeholder fun (and to prevent content reflow).
707
            $_get_size = $this->get_size_from_tag( $tag );
708
            $width     = $_get_size['width'];
709
            $height    = $_get_size['height'];
710
            if ( false === $width ) {
711
                $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...
712
            }
713
            if ( false === $height ) {
714
                $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...
715
            }
716
717
            // insert the actual lazyload stuff.
718
            // see https://css-tricks.com/preventing-content-reflow-from-lazy-loaded-images/ for great read on why we're using empty svg's.
719
            $placeholder = apply_filters( 'autoptimize_filter_imgopt_lazyload_placeholder', $this->get_default_lazyload_placeholder( $width, $height ) );
720
            $tag         = str_replace( ' src=', ' src=\'' . $placeholder . '\' data-src=', $tag );
721
            $tag         = str_replace( ' srcset=', ' data-srcset=', $tag );
722
723
            // add the noscript-tag from earlier.
724
            $tag = $noscript_tag . $tag;
725
            $tag = apply_filters( 'autoptimize_filter_imgopt_lazyloaded_img', $tag );
726
        }
727
728
        return $tag;
729
    }
730
731
    public function add_lazyload_js_footer() {
732
        // The JS will by default be excluded form autoptimization but this can be changed with a filter.
733
        $noptimize_flag = '';
734
        if ( apply_filters( 'autoptimize_filter_imgopt_lazyload_js_noptimize', true ) ) {
735
            $noptimize_flag = ' data-noptimize="1"';
736
        }
737
738
        // Adds lazyload CSS & JS to footer, using echo because wp_enqueue_script seems not to support pushing attributes (async).
739
        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>' );
740
        echo apply_filters( 'autoptimize_filter_imgopt_lazyload_jsconfig', '<script' . $noptimize_flag . '>window.lazySizesConfig=window.lazySizesConfig||{};window.lazySizesConfig.loadMode=1;</script>' );
741
        echo '<script async' . $noptimize_flag . ' src=\'' . plugins_url( 'external/js/lazysizes.min.js', __FILE__ ) . '\'></script>';
742
743
        // And add webp detection and loading JS.
744
        if ( $this->should_webp() ) {
745
            $_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);";
746
            $_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,'))})});";
747
            echo apply_filters( 'autoptimize_filter_imgopt_webp_js', '<script' . $noptimize_flag . '>' . $_webp_detect . $_webp_load . '</script>' );
748
        }
749
    }
750
751
    public function get_lazyload_exclusions() {
752
        // returns array of strings that if found in an <img tag will stop the img from being lazy-loaded.
753
        static $exclude_lazyload_array = null;
754
755
        if ( null === $exclude_lazyload_array ) {
756
            $options = $this->options;
757
758
            // set default exclusions.
759
            $exclude_lazyload_array = array( 'skip-lazy', 'data-no-lazy', 'notlazy', 'data-src', 'data-srcset' );
760
761
            // add from setting.
762
            if ( array_key_exists( 'autoptimize_imgopt_text_field_5', $options ) ) {
763
                $exclude_lazyload_option = $options['autoptimize_imgopt_text_field_5'];
764 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...
765
                    $exclude_lazyload_array = array_merge( $exclude_lazyload_array, array_filter( array_map( 'trim', explode( ',', $options['autoptimize_imgopt_text_field_5'] ) ) ) );
766
                }
767
            }
768
769
            // and filter for developer-initiated changes.
770
            $exclude_lazyload_array = apply_filters( 'autoptimize_filter_imgopt_lazyload_exclude_array', $exclude_lazyload_array );
771
        }
772
773
        return $exclude_lazyload_array;
774
    }
775
776
    public function inject_classes_in_tag( $tag, $target_class ) {
777
        if ( strpos( $tag, 'class=' ) !== false ) {
778
            $tag = preg_replace( '/(\sclass\s?=\s?("|\'))/', '$1' . $target_class, $tag );
779
        } else {
780
            $tag = str_replace( '<img ', '<img class="' . trim( $target_class ) . '" ', $tag );
781
        }
782
783
        return $tag;
784
    }
785
786
    public function get_default_lazyload_placeholder( $imgopt_w, $imgopt_h ) {
787
        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';
788
    }
789
790
    public function should_webp() {
791
        static $webp_return = null;
792
793
        if ( is_null( $webp_return ) ) {
794
            // webp only works if imgopt and lazyload are also active.
795
            if ( ! empty( $this->options['autoptimize_imgopt_checkbox_field_4'] ) && ! empty( $this->options['autoptimize_imgopt_checkbox_field_3'] ) && $this->imgopt_active() ) {
796
                $webp_return = true;
797
            } else {
798
                $webp_return = false;
799
            }
800
        }
801
802
        return $webp_return;
803
    }
804
805
    /**
806
     * Admin page logic and related functions below.
807
     */
808
    public function imgopt_admin_menu()
809
    {
810
        add_submenu_page(
811
            null,
812
            'autoptimize_imgopt',
813
            'autoptimize_imgopt',
814
            'manage_options',
815
            'autoptimize_imgopt',
816
            array( $this, 'imgopt_options_page' )
817
        );
818
        register_setting( 'autoptimize_imgopt_settings', 'autoptimize_imgopt_settings' );
819
    }
820
821
    public function add_imgopt_tab( $in )
822
    {
823
        $in = array_merge( $in, array( 'autoptimize_imgopt' => __( 'Images', 'autoptimize' ) ) );
824
825
        return $in;
826
    }
827
828
    public function imgopt_options_page()
829
    {
830
        // Check querystring for "refreshCacheChecker" and call cachechecker if so.
831
        if ( array_key_exists( 'refreshImgProvStats', $_GET ) && 1 == $_GET['refreshImgProvStats'] ) {
832
            $this->query_img_provider_stats();
833
        }
834
835
        $options       = $this->fetch_options();
836
        $sp_url_suffix = $this->get_service_url_suffix();
837
        ?>
838
    <style>
839
        #ao_settings_form {background: white;border: 1px solid #ccc;padding: 1px 15px;margin: 15px 10px 10px 0;}
840
        #ao_settings_form .form-table th {font-weight: normal;}
841
        #autoptimize_imgopt_descr{font-size: 120%;}
842
    </style>
843
    <div class="wrap">
844
    <h1><?php _e( 'Autoptimize Settings', 'autoptimize' ); ?></h1>
845
        <?php echo autoptimizeConfig::ao_admin_tabs(); ?>
846
        <?php if ( 'down' === $options['availabilities']['extra_imgopt']['status'] ) { ?>
847
            <div class="notice-warning notice"><p>
848
            <?php
849
            // translators: "Autoptimize support forum" will appear in a "a href".
850
            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>' );
851
            ?>
852
            </p></div>
853
        <?php } ?>
854
855
        <?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...
856
            <div class="notice-warning notice"><p>
857
            <?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' ); ?>
858
            </p></div>
859
        <?php } ?>
860
861
        <?php if ( class_exists( 'Jetpack' ) && method_exists( 'Jetpack', 'get_active_modules' ) && in_array( 'photon', Jetpack::get_active_modules() ) ) { ?>
862
            <div class="notice-warning notice"><p>
863
            <?php
864
            // translators: "disable  Jetpack's site accelerator for images" will appear in a "a href" linking to the jetpack settings page.
865
            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>' );
866
            ?>
867
            </p></div>
868
        <?php } ?>
869
    <form id='ao_settings_form' action='options.php' method='post'>
870
        <?php settings_fields( 'autoptimize_imgopt_settings' ); ?>
871
        <h2><?php _e( 'Image optimization', 'autoptimize' ); ?></h2>
872
        <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>
873
        <table class="form-table">
874
            <tr>
875
                <th scope="row"><?php _e( 'Optimize Images', 'autoptimize' ); ?></th>
876
                <td>
877
                    <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>
878
                    <?php
879
                    // show shortpixel status.
880
                    $_notice = autoptimizeImages::instance()->get_status_notice();
881
                    if ( $_notice ) {
882
                        switch ( $_notice['status'] ) {
883
                            case 2:
884
                                $_notice_color = 'green';
885
                                break;
886
                            case 1:
887
                                $_notice_color = 'orange';
888
                                break;
889
                            case -1:
890
                                $_notice_color = 'red';
891
                                break;
892
                            case -2:
893
                                $_notice_color = 'red';
894
                                break;
895
                            default:
896
                                $_notice_color = 'green';
897
                        }
898
                        echo apply_filters( 'autoptimize_filter_imgopt_settings_status', '<p><strong><span style="color:' . $_notice_color . ';">' . __( 'Shortpixel status: ', 'autoptimize' ) . '</span></strong>' . $_notice['notice'] . '</p>' );
899
                    } else {
900
                        // translators: link points to shortpixel.
901
                        $upsell_msg_1 = '<p>' . sprintf( __( 'Get more Google love and improve your website\'s loading speed by having the 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>' );
902
                        if ( 'launch' === $options['availabilities']['extra_imgopt']['status'] ) {
903
                            $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' );
904
                        } else {
905
                            // translators: link points to shortpixel.
906
                            $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>' );
907
                        }
908
                        echo apply_filters( 'autoptimize_imgopt_imgopt_settings_copy', $upsell_msg_1 . ' ' . $upsell_msg_2 . '</p>' );
909
                    }
910
                    // translators: link points to shortpixel FAQ.
911
                    $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>' );
912
                    // translators: links points to shortpixel TOS & Privacy Policy.
913
                    $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>' );
914
                    echo apply_filters( 'autoptimize_imgopt_imgopt_settings_tos', '<p>' . $faqcopy . ' ' . $toscopy . '</p>' );
915
                    ?>
916
                </td>
917
            </tr>
918 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...
919
                <th scope="row"><?php _e( 'Image Optimization quality', 'autoptimize' ); ?></th>
920
                <td>
921
                    <label>
922
                    <select name='autoptimize_imgopt_settings[autoptimize_imgopt_select_field_2]'>
923
                        <?php
924
                        $_imgopt_array = autoptimizeImages::instance()->get_img_quality_array();
925
                        $_imgopt_val   = autoptimizeImages::instance()->get_img_quality_setting();
926
927
                        foreach ( $_imgopt_array as $key => $value ) {
928
                            echo '<option value="' . $key . '"';
929
                            if ( $_imgopt_val == $key ) {
930
                                echo ' selected';
931
                            }
932
                            echo '>' . ucfirst( $value ) . '</option>';
933
                        }
934
                        echo "\n";
935
                        ?>
936
                    </select>
937
                    </label>
938
                    <p>
939
                        <?php
940
                            // translators: link points to shortpixel image test page.
941
                            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>' ) );
942
                        ?>
943
                    </p>
944
                </td>
945
            </tr>
946 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...
947
                <th scope="row"><?php _e( 'Load WebP in supported browsers?', 'autoptimize' ); ?></th>
948
                <td>
949
                    <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>
950
                </td>
951
            </tr>
952
            <tr>
953
                <th scope="row"><?php _e( 'Lazy-load images?', 'autoptimize' ); ?></th>
954
                <td>
955
                    <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>
956
                </td>
957
            </tr>
958 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...
959
                <th scope="row"><?php _e( 'Lazy-load exclusions', 'autoptimize' ); ?></th>
960
                <td>
961
                    <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>
962
                </td>
963
            </tr>
964
        </table>
965
        <p class="submit"><input type="submit" name="submit" id="submit" class="button button-primary" value="<?php _e( 'Save Changes', 'autoptimize' ); ?>" /></p>
966
    </form>
967
    <script>
968
        jQuery(document).ready(function() {
969
            jQuery("#autoptimize_imgopt_checkbox").change(function() {
970
                if (this.checked) {
971
                    jQuery("#autoptimize_imgopt_quality").show("slow");
972
                    jQuery("#autoptimize_imgopt_webp").show("slow");
973
                } else {
974
                    jQuery("#autoptimize_imgopt_quality").hide("slow");
975
                    jQuery("#autoptimize_imgopt_webp").hide("slow");
976
                }
977
            });
978
            jQuery("#autoptimize_imgopt_webp_checkbox").change(function() {
979
                if (this.checked) {
980
                    jQuery("#autoptimize_imgopt_lazyload_checkbox")[0].checked = true;
981
                    jQuery("#autoptimize_imgopt_lazyload_exclusions").show("slow");
982
                }
983
            });
984
            jQuery("#autoptimize_imgopt_lazyload_checkbox").change(function() {
985
                if (this.checked) {
986
                    jQuery("#autoptimize_imgopt_lazyload_exclusions").show("slow");
987
                } else {
988
                    jQuery("#autoptimize_imgopt_lazyload_exclusions").hide("slow");
989
                    jQuery("#autoptimize_imgopt_webp_checkbox")[0].checked = false;
990
                }
991
            });
992
        });
993
    </script>
994
        <?php
995
    }
996
997
    /**
998
     * Ïmg opt status as used on dashboard.
999
     */
1000
    public function get_imgopt_status_notice() {
1001
        if ( $this->imgopt_active() ) {
1002
            $_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...
1003
            $_stat          = get_option( 'autoptimize_imgopt_provider_stat', '' );
1004
            $_site_host     = AUTOPTIMIZE_SITE_DOMAIN;
1005
            $_imgopt_upsell = 'https://shortpixel.com/aospai/af/GWRGFLW109483/' . $_site_host;
1006
1007
            if ( is_array( $_stat ) ) {
1008
                if ( 1 == $_stat['Status'] ) {
1009
                    // translators: "add more credits" will appear in a "a href".
1010
                    $_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>' );
1011
                } elseif ( -1 == $_stat['Status'] || -2 == $_stat['Status'] ) {
1012
                    // translators: "add more credits" will appear in a "a href".
1013
                    $_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>' );
1014
                    $_imgopt_stats_refresh_url = add_query_arg( array(
1015
                        'page'                => 'autoptimize_imgopt',
1016
                        'refreshImgProvStats' => '1',
1017
                    ), admin_url( 'options-general.php' ) );
1018
                    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...
1019
                        $_imgopt_stats_last_run = __( 'based on status at ', 'autoptimize' ) . date_i18n( get_option( 'time_format' ), $_stat['timestamp'] );
1020
                    } else {
1021
                        $_imgopt_stats_last_run = __( 'based on previously fetched data', 'autoptimize' );
1022
                    }
1023
                    $_imgopt_notice .= ' (' . $_imgopt_stats_last_run . ', ';
1024
                    // translators: "here to refresh" links to the Autoptimize Extra page and forces a refresh of the img opt stats.
1025
                    $_imgopt_notice .= sprintf( __( 'click %1$shere to refresh%2$s', 'autoptimize' ), '<a href="' . $_imgopt_stats_refresh_url . '">', '</a>).' );
1026
                } else {
1027
                    $_imgopt_upsell = 'https://shortpixel.com/g/af/GWRGFLW109483';
1028
                    // translators: "log in to check your account" will appear in a "a href".
1029
                    $_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>' );
1030
                }
1031
                $_imgopt_notice = apply_filters( 'autoptimize_filter_imgopt_notice', $_imgopt_notice );
1032
1033
                return array(
1034
                    'status' => $_stat['Status'],
1035
                    'notice' => $_imgopt_notice,
1036
                );
1037
            }
1038
        }
1039
        return false;
1040
    }
1041
1042
    public static function get_imgopt_status_notice_wrapper() {
1043
        // needed for notice being shown in autoptimizeCacheChecker.php.
1044
        $self = new self();
1045
        return $self->get_imgopt_status_notice();
1046
    }
1047
1048
    /**
1049
     * Get img provider stats (used to display notice).
1050
     */
1051
    public function query_img_provider_stats() {
1052
        if ( ! empty( $this->options['autoptimize_imgopt_checkbox_field_1'] ) ) {
1053
            $url      = '';
1054
            $endpoint = $this->get_imgopt_host() . 'read-domain/';
1055
            $domain   = AUTOPTIMIZE_SITE_DOMAIN;
1056
1057
            // make sure parse_url result makes sense, keeping $url empty if not.
1058
            if ( $domain && ! empty( $domain ) ) {
1059
                $url = $endpoint . $domain;
1060
            }
1061
1062
            $url = apply_filters(
1063
                'autoptimize_filter_imgopt_stat_url',
1064
                $url
1065
            );
1066
1067
            // only do the remote call if $url is not empty to make sure no parse_url
1068
            // weirdness results in useless calls.
1069 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...
1070
                $response = wp_remote_get( $url );
1071
                if ( ! is_wp_error( $response ) ) {
1072
                    if ( '200' == wp_remote_retrieve_response_code( $response ) ) {
1073
                        $stats = json_decode( wp_remote_retrieve_body( $response ), true );
1074
                        update_option( 'autoptimize_imgopt_provider_stat', $stats );
1075
                    }
1076
                }
1077
            }
1078
        }
1079
    }
1080
1081
    public static function get_img_provider_stats()
1082
    {
1083
        // wrapper around query_img_provider_stats() so we can get to $this->options from cronjob() in autoptimizeCacheChecker.
1084
        $self = new self();
1085
        return $self->query_img_provider_stats();
1086
    }
1087
1088
    /**
1089
     * Determines and returns the service launch status.
1090
     *
1091
     * @return bool
1092
     */
1093
    public function launch_ok()
1094
    {
1095
        static $launch_status = null;
1096
1097
        if ( null === $launch_status ) {
1098
            $avail_imgopt  = $this->options['availabilities']['extra_imgopt'];
1099
            $magic_number  = intval( substr( md5( parse_url( AUTOPTIMIZE_WP_SITE_URL, PHP_URL_HOST ) ), 0, 3 ), 16 );
1100
            $has_launched  = get_option( 'autoptimize_imgopt_launched', '' );
1101
            $launch_status = false;
1102
            if ( $has_launched || ( is_array( $avail_imgopt ) && array_key_exists( 'launch-threshold', $avail_imgopt ) && $magic_number < $avail_imgopt['launch-threshold'] ) ) {
1103
                $launch_status = true;
1104
                if ( ! $has_launched ) {
1105
                    update_option( 'autoptimize_imgopt_launched', 'on' );
1106
                }
1107
            }
1108
        }
1109
1110
        return $launch_status;
1111
    }
1112
1113
    public static function launch_ok_wrapper() {
1114
        // needed for "plug" notice in autoptimizeMain.php.
1115
        $self = new self();
1116
        return $self->launch_ok();
1117
    }
1118
1119
    public function get_imgopt_provider_userstatus() {
1120
        static $_provider_userstatus = null;
1121
1122
        if ( is_null( $_provider_userstatus ) ) {
1123
            $_stat = get_option( 'autoptimize_imgopt_provider_stat', '' );
1124
            if ( is_array( $_stat ) ) {
1125
                if ( array_key_exists( 'Status', $_stat ) ) {
1126
                    $_provider_userstatus['Status'] = $_stat['Status'];
1127
                } else {
1128
                    // if no stats then we assume all is well.
1129
                    $_provider_userstatus['Status'] = 2;
1130
                }
1131
                if ( array_key_exists( 'timestamp', $_stat ) ) {
1132
                    $_provider_userstatus['timestamp'] = $_stat['timestamp'];
1133
                } else {
1134
                    // if no timestamp then we return "".
1135
                    $_provider_userstatus['timestamp'] = '';
1136
                }
1137
            }
1138
        }
1139
1140
        return $_provider_userstatus;
1141
    }
1142
1143
    public function get_status_notice() {
1144
        if ( $this->imgopt_active() ) {
1145
            $notice = '';
0 ignored issues
show
Unused Code introduced by
$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...
1146
            $stat   = $this->get_imgopt_provider_userstatus();
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $stat is correct as $this->get_imgopt_provider_userstatus() (which targets autoptimizeImages::get_i...t_provider_userstatus()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
1147
            $upsell = 'https://shortpixel.com/aospai/af/GWRGFLW109483/' . AUTOPTIMIZE_SITE_DOMAIN;
1148
            $assoc  = 'https://shortpixel.helpscoutdocs.com/article/94-how-to-associate-a-domain-to-my-account';
1149
1150
            if ( is_array( $stat ) ) {
1151
                if ( 1 == $stat['Status'] ) {
1152
                    // translators: "add more credits" will appear in a "a href".
1153
                    $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 rel="noopener noreferrer" href="' . $upsell . '" target="_blank">', '</a>' );
1154
                } elseif ( -1 == $stat['Status'] || -2 == $stat['Status'] ) {
1155
                    // translators: "add more credits" will appear in a "a href".
1156
                    $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 rel="noopener noreferrer" href="' . $upsell . '" target="_blank">', '</a>' );
1157
                    // translators: "associate your domain" will appear in a "a href".
1158
                    $notice = $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="' . $assoc . '" target="_blank">', '</a>' );
1159
                } else {
1160
                    $upsell = 'https://shortpixel.com/g/af/GWRGFLW109483';
1161
                    // translators: "log in to check your account" will appear in a "a href".
1162
                    $notice = sprintf( __( 'Your ShortPixel image optimization and CDN quota are in good shape, %1$slog in to check your account%2$s.', 'autoptimize' ), '<a rel="noopener noreferrer" href="' . $upsell . '" target="_blank">', '</a>' );
1163
                }
1164
                $notice = apply_filters( 'autoptimize_filter_imgopt_notice', $notice );
1165
1166
                return array(
1167
                    'status' => $stat['Status'],
1168
                    'notice' => $notice,
1169
                );
1170
            }
1171
        }
1172
        return false;
1173
    }
1174
}
1175