Skip to content

Instantly share code, notes, and snippets.

@westonruter
Last active November 13, 2023 12:54
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save westonruter/1f008d14c214f852eec658d60542ea6d to your computer and use it in GitHub Desktop.
Save westonruter/1f008d14c214f852eec658d60542ea6d to your computer and use it in GitHub Desktop.
<?php
/**
* Vary LCP Image Optimizations WordPress Plugin.
*
* @package VaryLcpImageOptimizations
* @author Weston Ruter, Google
* @license GPL-2.0-or-later
* @copyright 2023 Google Inc.
*
* @wordpress-plugin
* Plugin Name: Vary LCP Image Optimizations
* Description: Try toggling various optimizations for the LCP image, including the fetchpriority attribute, lazy-loading, and a preload link.
* Plugin URI: https://gist.github.com/westonruter/1f008d14c214f852eec658d60542ea6d
* Version: 0.1.2
* Author: Weston Ruter
* Author URI: https://weston.ruter.net/
* License: GNU General Public License v2 (or later)
* License URI: http://www.gnu.org/licenses/gpl-2.0.html
* Update URI: https://gist.github.com/westonruter/1f008d14c214f852eec658d60542ea6d
*/
namespace VaryLcpImageOptimizations;
use WP_HTML_Tag_Processor;
const QUERY_ARG = 'vary_lcp_image_optimizations';
function get_varied_optimizations(): array {
$optimizations = array(
'preload' => array(
'label' => 'LINK[rel=preload]',
'default' => false,
),
'img_fetchpriority' => array(
'label' => 'IMG[fetchpriority=high]',
'default' => true,
),
'img_lazy_loading' => array(
'label' => 'IMG[loading=lazy]',
'default' => false,
),
);
foreach ( $optimizations as $key => $optimization ) {
$optimizations[ $key ]['enabled'] = isset( $_GET[ QUERY_ARG ][ $key ] )
? rest_sanitize_boolean( $_GET[ QUERY_ARG ][ $key ] )
: $optimization['default'];
}
return $optimizations;
}
add_action(
'wp_footer',
static function () {
?>
<style>
#vary-lcp-image-optimizations {
padding: 20px;
}
</style>
<nav id="vary-lcp-image-optimizations">
<h5><?php esc_html_e( 'Vary LCP Image Optimizations', 'vary-lcp-image-optimizations' ) ?></h5>
<ul>
<?php
$varied_optimizations = get_varied_optimizations();
$base_url = remove_query_arg( QUERY_ARG );
$existing_args = array();
foreach ( $varied_optimizations as $key => $optimization ) {
if ( $optimization['default'] !== $optimization['enabled'] ) {
$existing_args[ $key ] = wp_json_encode( $optimization['enabled'] );
}
}
?>
<?php foreach ( get_varied_optimizations() as $key => $optimization ) : ?>
<?php
$toggle_args = $existing_args;
$toggle_args[ $key ] = wp_json_encode( ! $optimization['enabled'] );
$url = add_query_arg(
array( QUERY_ARG => $toggle_args ),
$base_url
);
?>
<li>
<code><?php echo esc_html( $optimization['label'] ); ?></code>:
<?php if ( $optimization['default'] !== $optimization['enabled'] ) { echo '<strong>'; } ?>
<a href="<?php echo esc_url( $url ); ?>" title="<?php echo esc_attr( $optimization['enabled'] ? __( 'Disable', 'vary-lcp-image-optimizations' ) : __( 'Enable', 'vary-lcp-image-optimizations' ) ) ?>">
<?php echo esc_html( $optimization['enabled'] ? __( 'Enabled', 'vary-lcp-image-optimizations' ) : __( 'Disabled', 'vary-lcp-image-optimizations' ) ) ?>
</a>
<?php if ( $optimization['default'] !== $optimization['enabled'] ) { echo '</strong>'; } ?>
</li>
<?php endforeach; ?>
</ul>
</nav>
<?php
}
);
add_action(
'template_redirect',
static function () {
ob_start( static function ( string $buffer ): string {
return optimize( $buffer );
} );
},
PHP_INT_MAX
);
function optimize( string $buffer ): string {
$p = new WP_HTML_Tag_Processor( $buffer );
$optimizations = get_varied_optimizations();
$img_attributes = array(
'src' => null,
'srcset' => null,
'sizes' => null,
'crossorigin' => null,
'integrity' => null,
);
while ( $p->next_tag( 'img' ) ) {
if ( $p->get_attribute( 'fetchpriority' ) ) {
foreach ( array_keys( $img_attributes ) as $name ) {
$img_attributes[ $name ] = $p->get_attribute( $name );
}
if ( ! $optimizations['img_fetchpriority']['enabled'] ) {
$p->remove_attribute( 'fetchpriority' );
}
if ( $optimizations['img_lazy_loading']['enabled'] ) {
$p->set_attribute( 'loading', 'lazy' );
}
break;
}
}
$buffer = $p->get_updated_html();
// TODO: Eventually this can use the HTML API.
if ( $optimizations['preload']['enabled'] && ( $img_attributes['src'] || $img_attributes['srcset'] ) ) {
// Prevent preloading src for browsers that don't support imagesrcset on the link element.
if ( isset( $img_attributes['src'], $img_attributes['srcset'] ) ) {
unset( $img_attributes['src'] );
}
// Construct preload link.
$link_tag = '<link rel="preload" fetchpriority="high" as="image"';
foreach ( array_filter( $img_attributes ) as $name => $value ) {
// Map img attribute name to link attribute name.
if ( 'srcset' === $name || 'sizes' === $name ) {
$name = 'image' . $name;
} elseif ( 'src' === $name ) {
$name = 'href';
}
$link_tag .= sprintf( ' %s="%s"', $name, esc_attr( $value ) );
}
$link_tag .= '>';
// Inject preload link.
// TODO: This should eventually use the HTML API.
$count = 0;
$buffer = preg_replace(
'/(<meta[^>]*?\sname=["\']?viewport[^>]+?>)/i',
'$1' . "\n" . $link_tag,
$buffer,
-1,
$count
);
if ( $count === 0 ) {
$buffer = preg_replace(
'#(?=</head>)#',
$link_tag . "\n",
$buffer
);
}
}
return $buffer;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment