Completed
Push — master ( 7911d3...f93cb2 )
by frank
04:06 queued 42s
created

autoptimizeImages::get_imgopt_host_wrapper()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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