laipower/wp-content/plugins/w3-total-cache/UserExperience_LazyLoad_Mut...

270 lines
5.9 KiB
PHP

<?php
namespace W3TC;
class UserExperience_LazyLoad_Mutator {
private $config;
private $modified = false;
private $excludes;
private $posts_by_url;
public function __construct( $config, $posts_by_url ) {
$this->config = $config;
$this->posts_by_url = $posts_by_url;
}
public function run( $buffer ) {
$this->excludes = apply_filters( 'w3tc_lazyload_excludes',
$this->config->get_array( 'lazyload.exclude' ) );
$r = apply_filters( 'w3tc_lazyload_mutator_before', array(
'buffer' => $buffer,
'modified' => $this->modified
) );
$buffer = $r['buffer'];
$this->modified = $r['modified'];
$unmutable = new UserExperience_LazyLoad_Mutator_Unmutable();
$buffer = $unmutable->remove_unmutable( $buffer );
if ( $this->config->get_boolean( 'lazyload.process_img' ) ) {
$buffer = preg_replace_callback(
'~<picture(\s[^>]+)*>(.*?)</picture>~is',
array( $this, 'tag_picture' ), $buffer
);
$buffer = preg_replace_callback(
'~<img\s[^>]+>~is',
array( $this, 'tag_img' ), $buffer
);
}
if ( $this->config->get_boolean( 'lazyload.process_background' ) ) {
$buffer = preg_replace_callback(
'~<[^>]+background(-image)?:\s*url[^>]+>~is',
array( $this, 'tag_with_background' ), $buffer
);
}
$buffer = $unmutable->restore_unmutable( $buffer );
return $buffer;
}
public function content_modified() {
return $this->modified;
}
public function tag_picture( $matches ) {
$content = $matches[0];
if ( $this->is_content_excluded( $content ) ) {
return $content;
}
$m = new UserExperience_LazyLoad_Mutator_Picture( $this );
return $m->run( $content );
}
public function tag_img( $matches ) {
$content = $matches[0];
if ( $this->is_content_excluded( $content ) ) {
return $content;
}
// get image dimensions
$dim = $this->tag_get_dimensions( $content );
return $this->tag_img_content_replace( $content, $dim );
}
/**
* Common replace code for picture and img tags
*/
public function tag_img_content_replace( $content, $dim ) {
// do replace
$count = 0;
$content = preg_replace( '~(\s)src=~is',
'$1src="' . $this->placeholder( $dim['w'], $dim['h'] ) .
'" data-src=', $content, -1, $count );
if ( $count > 0 ) {
$content = preg_replace( '~(\s)(srcset|sizes)=~is',
'$1data-$2=', $content );
$content = $this->add_class_lazy( $content );
$content = $this->remove_native_lazy( $content );
$this->modified = true;
}
return $content;
}
/**
* Common get dimensions of image
*/
public function tag_get_dimensions( $content ) {
$dim = array( 'w' => 1, 'h' => 1 );
$m = null;
if ( preg_match( '~\swidth=[\s\'"]*([0-9]+)~is', $content, $m ) ) {
$dim['h'] = $dim['w'] = (int)$m[1];
if ( preg_match( '~\sheight=[\s\'"]*([0-9]+)~is', $content, $m ) ) {
$dim['h'] = (int)$m[1];
return $dim;
}
}
// if not in attributes - try to find via url
if ( !preg_match( '~\ssrc=(\'([^\']*)\'|"([^"]*)"|([^\'"][^\\s]*))~is',
$content, $m ) ) {
return $dim;
}
$url = ( !empty( $m[4] ) ? $m[4] : ( ( !empty( $m[3] ) ? $m[3] : $m[2] ) ) );
// full url found
if ( isset( $this->posts_by_url[$url] ) ) {
$post_id = $this->posts_by_url[$url];
$image = wp_get_attachment_image_src( $post_id, 'full' );
if ( $image ) {
$dim['w'] = $image[1];
$dim['h'] = $image[2];
}
return $dim;
}
// try resized url by format
static $base_url = null;
if ( is_null( $base_url ) ) {
$base_url = wp_get_upload_dir()['baseurl'];
}
if ( substr( $url, 0, strlen( $base_url ) ) == $base_url &&
preg_match( '~(.+)-(\\d+)x(\\d+)(\\.[a-z0-9]+)$~is', $url, $m ) ) {
$dim['w'] = (int)$m[2];
$dim['h'] = (int)$m[3];
}
return $dim;
}
public function tag_with_background( $matches ) {
$content = $matches[0];
if ( $this->is_content_excluded( $content ) ) {
return $content;
}
$quote_match = null;
if ( !preg_match( '~\s+style\s*=\s*([\"\'])~is', $content, $quote_match ) ) {
return $content;
}
$quote = $quote_match[1];
$count = 0;
$content = preg_replace_callback(
'~(\s+)(style\s*=\s*[' . $quote . '])(.*?)([' . $quote . '])~is',
array( $this, 'style_offload_background' ), $content, -1, $count
);
if ( $count > 0 ) {
$content = $this->add_class_lazy( $content );
$this->modified = true;
}
return $content;
}
public function style_offload_background( $matches ) {
list( $match, $v1, $v2, $v, $quote ) = $matches;
$url_match = null;
preg_match( '~background(-image)?:\s*(url\([^>]+\))~is', $v, $url_match );
$v = preg_replace( '~background(-image)?:\s*url\([^>]+\)[;]?\s*~is', '', $v );
return $v1 . $v2 . $v . $quote . ' data-bg=' . $quote . ( isset( $url_match[2] ) ? $url_match[2] : '' ) . $quote;
}
private function add_class_lazy( $content ) {
$count = 0;
$content = preg_replace_callback(
'~(\s+)(class=)([\"\'])(.*?)([\"\'])~is',
array( $this, 'class_process' ), $content, -1, $count
);
if ( $count <= 0) {
$content = preg_replace(
'~<(\S+)(\s+)~is', '<$1$2class="lazy" ', $content
);
}
return $content;
}
/**
* In safari javascript lazy-loaded image with loading="lazy"
* dont fire events, i.e. image not loaded
*/
public function remove_native_lazy( $content ) {
return preg_replace(
'~(\s+)loading=[\'"]lazy[\'"]~is', '', $content
);
}
public function class_process( $matches ) {
list( $match, $v1, $v2, $quote, $v ) = $matches;
if ( preg_match( '~(^|\\s)lazy(\\s|$)~is', $v ) ) {
return $match;
}
$v .= ' lazy';
return $v1 . $v2 . $quote . $v . $quote;
}
private function is_content_excluded( $content ) {
foreach ( $this->excludes as $w ) {
if ( !empty($w) ) {
if ( strpos( $content, $w ) !== FALSE ) {
return true;
}
}
}
return false;
}
public function placeholder( $w, $h ) {
return 'data:image/svg+xml,%3Csvg%20xmlns=\'http://www.w3.org/2000/svg\'%20viewBox=\'0%200%20' .
$w . '%20'. $h . '\'%3E%3C/svg%3E';
}
}