Diff: STRATO-apps/wordpress_03/app/wp-content/plugins/wp-rocket/inc/Dependencies/RocketLazyload/Image.php

Keine Baseline-Datei – Diff nur gegen leer.
Zur Liste
1 -
1 + <?php
2 + declare(strict_types=1);
3 +
4 + /**
5 + * Handles lazyloading of images
6 + *
7 + * @package WP_Rocket\Dependencies\RocketLazyload
8 + */
9 +
10 + namespace WP_Rocket\Dependencies\RocketLazyload;
11 +
12 + /**
13 + * A class to provide the methods needed to lazyload images in WP Rocket and Lazyload by WP Rocket
14 + */
15 + class Image {
16 +
17 + /**
18 + * Finds the images to be lazyloaded and call the callback method to replace them.
19 + *
20 + * @param string $html Original HTML.
21 + * @param string $buffer Content to parse.
22 + * @param bool $use_native Use native lazyload.
23 + * @return string
24 + */
25 + public function lazyloadImages( $html, $buffer, $use_native = true ) {
26 + if ( ! preg_match_all( '#<img(?<atts>\s.+)\s?/?>#iUs', $buffer, $images, PREG_SET_ORDER ) ) {
27 + return $html;
28 + }
29 +
30 + $images = array_unique( $images, SORT_REGULAR );
31 +
32 + foreach ( $images as $image ) {
33 + $original_image = $image;
34 + $image = $this->canLazyload( $image );
35 +
36 + if ( ! $image ) {
37 + $image_no_lazy = preg_replace( '/loading=["\']lazy["\']/i', '', $original_image );
38 +
39 + if ( null === $image_no_lazy ) {
40 + continue;
41 + }
42 +
43 + $html = str_replace( $original_image, $image_no_lazy, $html );
44 +
45 + continue;
46 + }
47 +
48 + $image_lazyload = $this->replaceImage( $image, $use_native );
49 +
50 + if ( ! $use_native && $this->noscriptEnabled() ) {
51 + $image_lazyload .= $this->noscript( $image[0] );
52 + }
53 +
54 + $html = str_replace( $image[0], $image_lazyload, $html );
55 +
56 + unset( $image_lazyload );
57 + }
58 +
59 + return $html;
60 + }
61 +
62 + /**
63 + * Applies lazyload on background images defined in style attributes
64 + *
65 + * @param string $html Original HTML.
66 + * @param string $buffer Content to parse.
67 + * @return string
68 + */
69 + public function lazyloadBackgroundImages( $html, $buffer ) {
70 + if ( ! preg_match_all( '#<(?<tag>div|figure|section|aside|span|li|a)\s+(?<before>[^>]+[\'"\s])?style\s*=\s*([\'"])(?<styles>.*?)\3(?<after>[^>]*)>#is', $buffer, $elements, PREG_SET_ORDER ) ) {
71 + return $html;
72 + }
73 +
74 + foreach ( $elements as $element ) {
75 + if ( $this->isExcluded( $element['before'] . $element['after'], $this->getExcludedAttributes() ) ) {
76 + continue;
77 + }
78 +
79 + /**
80 + * Regex to detect bg images inside CSS.
81 + *
82 + * @param string $regex regex to detect.
83 + * @return string
84 + */
85 + $regex = apply_filters( 'rocket_lazyload_bg_images_regex', 'background-image\s*:\s*(?<attr>\s*url\s*\((?<url>[^)]+)\))\s*;?' );
86 +
87 + if ( @preg_match( "#$regex#is", '' ) === false ) {// phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
88 + $regex = 'background-image\s*:\s*(?<attr>\s*url\s*\((?<url>[^)]+)\))\s*;?';
89 + }
90 +
91 + if ( ! preg_match( "#$regex#is", $element['styles'], $url ) ) {
92 + continue;
93 + }
94 +
95 + if ( preg_match( '#data:image#is', $url['url'], $img ) ) {
96 + continue;
97 + }
98 + $url['url'] = esc_url(
99 + trim(
100 + wp_strip_all_tags(
101 + html_entity_decode(
102 + $url['url'],
103 + ENT_QUOTES | ENT_HTML5
104 + )
105 + ),
106 + '\'" '
107 + )
108 + );
109 +
110 + if ( $this->isExcluded( $url['url'], $this->getExcludedSrc() ) ) {
111 + continue;
112 + }
113 +
114 + $lazy_bg = $this->addLazyCLass( $element[0] );
115 + $lazy_bg = str_replace( $url[0], '', $lazy_bg );
116 + $lazy_bg = str_replace( '<' . $element['tag'], '<' . $element['tag'] . ' data-bg="' . esc_attr( $url['url'] ) . '"', $lazy_bg );
117 +
118 + $html = str_replace( $element[0], $lazy_bg, $html );
119 + unset( $lazy_bg );
120 + }
121 +
122 + return $html;
123 + }
124 +
125 + /**
126 + * Add the identifier class to the element
127 + *
128 + * @param string $element Element to add the class to.
129 + * @return string
130 + */
131 + private function addLazyClass( $element ) {
132 + $class = $this->getClasses( $element );
133 +
134 + if ( ! $class ) {
135 + $result = preg_replace( '#<(img|div|figure|section|aside|li|span|a)([^>]*)>#is', '<\1 class="rocket-lazyload"\2>', $element );
136 +
137 + if ( ! $result ) {
138 + return $element;
139 + }
140 +
141 + return $result;
142 + }
143 +
144 + if ( empty( $class['attribute'] ) || empty( $class['classes'] ) ) {
145 + return str_replace( $class['attribute'], 'class="rocket-lazyload"', $element );
146 + }
147 +
148 + $quotes = $this->getAttributeQuotes( $class['classes'] );
149 + $classes = $this->trimOuterQuotes( $class['classes'], $quotes );
150 +
151 + if ( empty( $classes ) ) {
152 + return str_replace( $class['attribute'], 'class="rocket-lazyload"', $element );
153 + }
154 +
155 + $classes .= ' rocket-lazyload';
156 +
157 + return str_replace(
158 + $class['attribute'],
159 + 'class=' . $this->normalizeClasses( $classes, $quotes ),
160 + $element
161 + );
162 + }
163 +
164 + /**
165 + * Gets the attribute value's outer quotation mark, if one exists, i.e. " or '.
166 + *
167 + * @param string $attribute_value The target attribute's value.
168 + *
169 + * @return false|string quotation character; else false when no quotation mark.
170 + */
171 + private function getAttributeQuotes( $attribute_value ) {
172 + $attribute_value = trim( $attribute_value );
173 + $first_char = $attribute_value[0];
174 +
175 + if ( '"' === $first_char || "'" === $first_char ) {
176 + return $first_char;
177 + }
178 +
179 + return false;
180 + }
181 +
182 + /**
183 + * Gets the class attribute and values from the given element, if it exists.
184 + *
185 + * @param string $element Given HTML element to extract classes from.
186 + *
187 + * @return false|string[] {
188 + * @type string $attribute Class attribute and value, e.g. class="value"
189 + * @type string $classes String of class attribute's value(s)
190 + * }; else, false when no class attribute exists.
191 + */
192 + private function getClasses( $element ) {
193 + if ( ! preg_match( '#class\s*=\s*(?<classes>["\'].*?["\']|[^\s]+)#is', $element, $class ) ) {
194 + return false;
195 + }
196 +
197 + if ( empty( $class['classes'] ) ) {
198 + return false;
199 + }
200 +
201 + return [
202 + 'attribute' => $class[0],
203 + 'classes' => $class['classes'],
204 + ];
205 + }
206 +
207 + /**
208 + * Removes outer single or double quotations.
209 + *
210 + * @param string $string String to strip quotes from.
211 + * @param false|string $quotes The outer quotes to remove.
212 + *
213 + * @return string string without quotes.
214 + */
215 + private function trimOuterQuotes( $string, $quotes ) {
216 + $string = trim( $string );
217 +
218 + if ( empty( $string ) ) {
219 + return '';
220 + }
221 +
222 + if ( empty( $quotes ) ) {
223 + return $string;
224 + }
225 +
226 + $string = ltrim( $string, $quotes );
227 + $string = rtrim( $string, $quotes );
228 + return trim( $string );
229 + }
230 +
231 + /**
232 + * Normalizes the class attribute values to ensure well-formed.
233 + *
234 + * @param string $classes String of class attribute value(s).
235 + * @param string|bool $quotes Optional. Quotation mark to wrap around the classes.
236 + *
237 + * @return string well-formed class attributes.
238 + */
239 + private function normalizeClasses( $classes, $quotes = '"' ) {
240 + $array_of_classes = $this->stringToArray( $classes );
241 + $classes = implode( ' ', $array_of_classes );
242 +
243 + if ( false === $quotes ) {
244 + $quotes = '"';
245 + }
246 +
247 + return $quotes . $classes . $quotes;
248 + }
249 +
250 + /**
251 + * Converts the given string into an array of strings.
252 + *
253 + * Note:
254 + * 1. Removes empties.
255 + * 2. Trims each string.
256 + *
257 + * @param string $string The target string to convert.
258 + * @param non-empty-string $delimiter Optional. Default: ' ' (one space).
259 + *
260 + * @return array<string> An array of trimmed strings.
261 + */
262 + private function stringToArray( $string, $delimiter = ' ' ) {
263 + if ( empty( $string ) ) {
264 + return [];
265 + }
266 +
267 + $array = explode( $delimiter, $string );
268 +
269 + if ( ! $array ) {
270 + return [];
271 + }
272 +
273 + $array = array_map( 'trim', $array );
274 +
275 + // Remove empties.
276 + return array_filter( $array );
277 + }
278 +
279 + /**
280 + * Applies lazyload on picture elements found in the HTML.
281 + *
282 + * @param string $html Original HTML.
283 + * @param string $buffer Content to parse.
284 + * @return string
285 + */
286 + public function lazyloadPictures( $html, $buffer ) {
287 + if ( ! preg_match_all( '#<picture(?:.*)?>(?<sources>.*)</picture>#iUs', $buffer, $pictures, PREG_SET_ORDER ) ) {
288 + return $html;
289 + }
290 +
291 + $pictures = array_unique( $pictures, SORT_REGULAR );
292 + $excluded = array_merge( $this->getExcludedAttributes(), $this->getExcludedSrc() );
293 +
294 + foreach ( $pictures as $picture ) {
295 + if ( $this->isExcluded( $picture[0], $excluded ) ) {
296 + if ( ! preg_match( '#<img(?<atts>\s.+)\s?/?>#iUs', $picture[0], $img ) ) {
297 + continue;
298 + }
299 +
300 + $img = $this->canLazyload( $img );
301 +
302 + if ( ! $img ) {
303 + continue;
304 + }
305 +
306 + $nolazy_picture = str_replace( '<img', '<img data-no-lazy=""', $picture[0] );
307 + $html = str_replace( $picture[0], $nolazy_picture, $html );
308 +
309 + continue;
310 + }
311 +
312 + if ( preg_match_all( '#<source(?<atts>\s.+)>#iUs', $picture['sources'], $sources, PREG_SET_ORDER ) ) {
313 + $lazy_sources = 0;
314 + $sources = array_unique( $sources, SORT_REGULAR );
315 + $lazy_picture = $picture[0];
316 +
317 + foreach ( $sources as $source ) {
318 + $lazyload_srcset = preg_replace( '/([\s"\'])srcset/i', '\1data-lazy-srcset', $source[0] );
319 +
320 + if ( ! $lazyload_srcset ) {
321 + continue;
322 + }
323 +
324 + $lazy_picture = str_replace( $source[0], $lazyload_srcset, $lazy_picture );
325 +
326 + unset( $lazyload_srcset );
327 + $lazy_sources++;
328 + }
329 +
330 + if ( 0 === $lazy_sources ) {
331 + continue;
332 + }
333 +
334 + $html = str_replace( $picture[0], $lazy_picture, $html );
335 + }
336 +
337 + if ( ! preg_match( '#<img(?<atts>\s.+)\s?/?>#iUs', $picture[0], $img ) ) {
338 + continue;
339 + }
340 +
341 + $img = $this->canLazyload( $img );
342 +
343 + if ( ! $img ) {
344 + continue;
345 + }
346 +
347 + $img_lazy = $this->replaceImage( $img, false );
348 +
349 + if ( $this->noscriptEnabled() ) {
350 + $img_lazy .= $this->noscript( $img[0] );
351 + }
352 +
353 + $safe_img = str_replace( '/', '\/', preg_quote( $img[0], '#' ) );
354 +
355 + $new_html = preg_replace( '#<noscript[^>]*>.*' . $safe_img . '.*<\/noscript>(*SKIP)(*FAIL)|' . $safe_img . '#i', $img_lazy, $html );
356 +
357 + if ( ! $new_html ) {
358 + continue;
359 + }
360 +
361 + $html = $new_html;
362 +
363 + unset( $img_lazy );
364 + }
365 +
366 + return $html;
367 + }
368 +
369 + /**
370 + * Checks if the image can be lazyloaded
371 + *
372 + * @param array<string> $image Array of image data coming from Regex.
373 + *
374 + * @return false|array<string>
375 + */
376 + private function canLazyload( $image ) {
377 + if ( $this->isExcluded( $image['atts'], $this->getExcludedAttributes() ) ) {
378 + return false;
379 + }
380 +
381 + // Given the previous regex pattern, $image['atts'] starts with a whitespace character.
382 + if ( ! preg_match( '@\ssrc\s*=\s*(\'|")(?<src>.*)\1@iUs', $image['atts'], $atts ) ) {
383 + return false;
384 + }
385 +
386 + $image['src'] = trim( $atts['src'] );
387 +
388 + if ( '' === $image['src'] ) {
389 + return false;
390 + }
391 +
392 + if ( $this->isExcluded( $image['src'], $this->getExcludedSrc() ) ) {
393 + return false;
394 + }
395 +
396 + return $image;
397 + }
398 +
399 + /**
400 + * Checks if the provided string matches with the provided excluded patterns
401 + *
402 + * @param string $string String to check.
403 + * @param array<string> $excluded_values Patterns to match against.
404 + *
405 + * @return bool
406 + */
407 + public function isExcluded( $string, $excluded_values ) {
408 + if ( ! is_array( $excluded_values ) ) {
409 + $excluded_values = (array) $excluded_values;
410 + }
411 +
412 + $excluded_values = array_filter( $excluded_values );
413 +
414 + if ( empty( $excluded_values ) ) {
415 + return false;
416 + }
417 +
418 + foreach ( $excluded_values as $excluded_value ) {
419 + if ( strpos( $string, $excluded_value ) !== false ) {
420 + return true;
421 + }
422 + }
423 +
424 + return false;
425 + }
426 +
427 + /**
428 + * Returns the list of excluded attributes
429 + *
430 + * @return array<string>
431 + */
432 + public function getExcludedAttributes() {
433 + /**
434 + * Filters the attributes used to prevent lazylad from being applied
435 + *
436 + * @since 1.0
437 + *
438 + * @param array $excluded_attributes An array of excluded attributes.
439 + */
440 + return apply_filters(
441 + 'rocket_lazyload_excluded_attributes',
442 + [
443 + 'data-src=',
444 + 'data-no-lazy=',
445 + 'data-lazy-original=',
446 + 'data-lazy-src=',
447 + 'data-lazysrc=',
448 + 'data-lazyload=',
449 + 'data-bgposition=',
450 + 'data-envira-src=',
451 + 'fullurl=',
452 + 'lazy-slider-img=',
453 + 'data-srcset=',
454 + 'class="ls-l',
455 + 'class="ls-bg',
456 + 'soliloquy-image',
457 + 'loading="eager"',
458 + 'swatch-img',
459 + 'data-height-percentage',
460 + 'data-large_image',
461 + 'avia-bg-style-fixed',
462 + 'data-skip-lazy',
463 + 'skip-lazy',
464 + 'image-compare__',
465 + ]
466 + );
467 + }
468 +
469 + /**
470 + * Returns the list of excluded src
471 + *
472 + * @return array<string>
473 + */
474 + public function getExcludedSrc() {
475 + /**
476 + * Filters the src used to prevent lazylad from being applied
477 + *
478 + * @since 1.0
479 + *
480 + * @param array $excluded_src An array of excluded src.
481 + */
482 + return apply_filters(
483 + 'rocket_lazyload_excluded_src',
484 + [
485 + '/wpcf7_captcha/',
486 + 'timthumb.php?src',
487 + 'woocommerce/assets/images/placeholder.png',
488 + ]
489 + );
490 + }
491 +
492 + /**
493 + * Replaces the original image by the lazyload one
494 + *
495 + * @param array<string> $image Array of matches elements.
496 + * @param bool $use_native Use native lazyload.
497 + *
498 + * @return string
499 + */
500 + private function replaceImage( $image, $use_native = true ) {
501 + if ( empty( $image ) ) {
502 + return '';
503 + }
504 +
505 + $native_pattern = '@\sloading\s*=\s*(\'|")(?:lazy|auto)\1@i';
506 + $image_lazyload = $image[0];
507 +
508 + if ( $use_native ) {
509 + if ( preg_match( $native_pattern, $image[0] ) ) {
510 + return $image[0];
511 + }
512 +
513 + $image_lazyload = str_replace( '<img', '<img loading="lazy"', $image_lazyload );
514 + } else {
515 + $width = 0;
516 + $height = 0;
517 +
518 + if ( preg_match( '@[\s"\']width\s*=\s*(\'|")(?<width>.*)\1@iUs', $image['atts'], $atts ) ) {
519 + $width = absint( $atts['width'] );
520 + }
521 +
522 + if ( preg_match( '@[\s"\']height\s*=\s*(\'|")(?<height>.*)\1@iUs', $image['atts'], $atts ) ) {
523 + $height = absint( $atts['height'] );
524 + }
525 +
526 + // Only match src attributes with safe values (no spaces, quotes, or angle brackets).
527 + $placeholder_atts = preg_replace(
528 + '@\ssrc\s*=\s*(\'|")(?<src>[^\s"\'>]+)\1@iUs',
529 + ' src="' . $this->getPlaceholder( $width, $height ) . '"',
530 + $image['atts']
531 + );
532 +
533 + $image_lazyload = str_replace(
534 + $image['atts'],
535 + $placeholder_atts . ' data-lazy-src="' . esc_url( $image['src'] ) . '"',
536 + $image_lazyload
537 + );
538 +
539 + if ( preg_match( $native_pattern, $image_lazyload ) ) {
540 + $result = preg_replace( $native_pattern, '', $image_lazyload );
541 +
542 + if ( is_string( $result ) ) {
543 + $image_lazyload = $result;
544 + }
545 + }
546 + }
547 +
548 + /**
549 + * Filter the LazyLoad HTML output
550 + *
551 + * @since 1.0
552 + *
553 + * @param string $html Output that will be printed
554 + */
555 + $image_lazyload = apply_filters( 'rocket_lazyload_html', $image_lazyload );
556 +
557 + return $image_lazyload;
558 + }
559 +
560 + /**
561 + * Checks if the noscript tag is enabled
562 + *
563 + * @return mixed
564 + */
565 + private function noscriptEnabled() {
566 + /**
567 + * Filter to enable or disable noscript tag
568 + *
569 + * @param bool $enable_noscript Enable or disable noscript tag.
570 + */
571 + return wpm_apply_filters_typed( 'boolean', 'rocket_lazyload_noscript', true );
572 + }
573 +
574 + /**
575 + * Returns the HTML tag wrapped inside noscript tags
576 + *
577 + * @param string $element Element to wrap.
578 + * @return string
579 + */
580 + private function noscript( $element ) {
581 + return '<noscript>' . $element . '</noscript>';
582 + }
583 +
584 + /**
585 + * Applies lazyload on srcset and sizes attributes
586 + *
587 + * @param string $html HTML image tag.
588 + * @return string
589 + */
590 + public function lazyloadResponsiveAttributes( $html ) {
591 + $data_srcset = preg_replace( '/[\s|"|\'](srcset)\s*=\s*("|\')([^"|\']+)\2/i', ' data-lazy-$1=$2$3$2', $html );
592 +
593 + if ( ! $data_srcset ) {
594 + return $html;
595 + }
596 +
597 + $html = $data_srcset;
598 +
599 + $lazy_responsive = preg_replace( '/[\s|"|\'](sizes)\s*=\s*("|\')([^"|\']+)\2/i', ' data-lazy-$1=$2$3$2', $html );
600 +
601 + if ( ! $lazy_responsive ) {
602 + return $html;
603 + }
604 +
605 + return $lazy_responsive;
606 + }
607 +
608 + /**
609 + * Finds patterns matching smiley and call the callback method to replace them with the image
610 + *
611 + * @param string $text Content to search in.
612 + * @return string
613 + */
614 + public function convertSmilies( $text ) {
615 + global $wp_smiliessearch;
616 +
617 + if ( empty( $text ) || ! is_string( $text ) ) {
618 + return $text;
619 + }
620 +
621 + if ( ! get_option( 'use_smilies' ) || empty( $wp_smiliessearch ) ) {
622 + return $text;
623 + }
624 +
625 + $output = '';
626 + // HTML loop taken from texturize function, could possible be consolidated.
627 + $textarr = preg_split( '/(<.*>)/U', $text, -1, PREG_SPLIT_DELIM_CAPTURE ); // capture the tags as well as in between.
628 +
629 + if ( ! $textarr ) {
630 + return $text;
631 + }
632 +
633 + $stop = count( $textarr );// loop stuff.
634 +
635 + // Ignore processing of specific tags.
636 + $tags_to_ignore = 'code|pre|style|script|textarea';
637 + $ignore_block_element = '';
638 +
639 + for ( $i = 0; $i < $stop; $i++ ) {
640 + $content = $textarr[ $i ];
641 +
642 + // If we're in an ignore block, wait until we find its closing tag.
643 + if ( '' === $ignore_block_element && preg_match( '/^<(' . $tags_to_ignore . ')>/', $content, $matches ) ) {
644 + $ignore_block_element = $matches[1];
645 + }
646 +
647 + // If it's not a tag and not in ignore block.
648 + if ( '' === $ignore_block_element && strlen( $content ) > 0 && '<' !== $content[0] ) {
649 + $content = preg_replace_callback( $wp_smiliessearch, [ $this, 'translateSmiley' ], $content );
650 + }
651 +
652 + // did we exit ignore block.
653 + if ( '' !== $ignore_block_element && '</' . $ignore_block_element . '>' === $content ) {
654 + $ignore_block_element = '';
655 + }
656 +
657 + $output .= $content;
658 + }
659 +
660 + return $output;
661 + }
662 +
663 + /**
664 + * Replace matches by smiley image, lazyloaded
665 + *
666 + * @param array<string> $matches Array of matches.
667 + *
668 + * @return string
669 + */
670 + private function translateSmiley( $matches ) {
671 + global $wpsmiliestrans;
672 +
673 + if ( count( $matches ) === 0 ) {
674 + return '';
675 + }
676 +
677 + $smiley = trim( reset( $matches ) );
678 + $img = $wpsmiliestrans[ $smiley ];
679 +
680 + $matches = [];
681 + $ext = preg_match( '/\.([^.]+)$/', $img, $matches ) ? strtolower( $matches[1] ) : false;
682 + $image_exts = [ 'jpg', 'jpeg', 'jpe', 'gif', 'png' ];
683 +
684 + // Don't convert smilies that aren't images - they're probably emoji.
685 + if ( ! in_array( $ext, $image_exts, true ) ) {
686 + return $img;
687 + }
688 +
689 + /**
690 + * Filter the Smiley image URL before it's used in the image element.
691 + *
692 + * @since 2.9.0
693 + *
694 + * @param string $smiley_url URL for the smiley image.
695 + * @param string $img Filename for the smiley image.
696 + * @param string $site_url Site URL, as returned by site_url().
697 + */
698 + $src_url = apply_filters( 'smilies_src', includes_url( "images/smilies/$img" ), $img, site_url() ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
699 +
700 + // Don't LazyLoad if process is stopped for these reasons.
701 + if ( is_feed() || is_preview() ) {
702 + return sprintf( ' <img src="%s" alt="%s" class="wp-smiley" /> ', esc_url( $src_url ), esc_attr( $smiley ) );
703 + }
704 +
705 + return sprintf( ' <img src="%s" data-lazy-src="%s" alt="%s" class="wp-smiley" /> ', $this->getPlaceholder(), esc_url( $src_url ), esc_attr( $smiley ) );
706 + }
707 +
708 + /**
709 + * Returns the placeholder for the src attribute
710 + *
711 + * @since 1.2
712 + *
713 + * @param int $width Width of the placeholder image. Default 0.
714 + * @param int $height Height of the placeholder image. Default 0.
715 + * @return string
716 + */
717 + public function getPlaceholder( $width = 0, $height = 0 ) {
718 + $width = 0 === $width ? 0 : absint( $width );
719 + $height = 0 === $height ? 0 : absint( $height );
720 +
721 + $placeholder = str_replace( ' ', '%20', "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 $width $height'%3E%3C/svg%3E" );
722 + /**
723 + * Filter the image lazyLoad placeholder on src attribute
724 + *
725 + * @since 1.1
726 + *
727 + * @param string $placeholder Placeholder that will be printed.
728 + * @param int $width Placeholder width.
729 + * @param int $height Placeholder height.
730 + */
731 + return apply_filters( 'rocket_lazyload_placeholder', $placeholder, $width, $height );
732 + }
733 + }
734 +