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