Skip to content

Instantly share code, notes, and snippets.

@westonruter
Last active October 26, 2022 15:02
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save westonruter/8a171f6c561fd69ac33a032983dafaf3 to your computer and use it in GitHub Desktop.
Save westonruter/8a171f6c561fd69ac33a032983dafaf3 to your computer and use it in GitHub Desktop.
Prototype to land as part of AMP Optimizer. See https://github.com/ampproject/amp-toolbox-php/issues/176
<?php
/**
* AMP Elevate Resource Hints plugin bootstrap.
*
* @package Google\AmpElevateResourceHints
* @author Weston Ruter, Google
* @license GPL-2.0-or-later
* @copyright 2021 Google Inc.
*
* @wordpress-plugin
* Plugin Name: AMP Elevate Resource Hints
* Plugin URI: https://gist.github.com/westonruter/8a171f6c561fd69ac33a032983dafaf3
* Description: Prototype transformer which elevates <code>link</code> elements to <code>Link</code> response headers. This functionality will be incorporated into the Optimizer via <a href="https://github.com/ampproject/amp-toolbox-php/issues/176">amp-toolbox-php#176</a>.
* Version: 0.1.1
* Author: Weston Ruter, Google
* 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
* Gist Plugin URI: https://gist.github.com/westonruter/8a171f6c561fd69ac33a032983dafaf3
*/
namespace Google\AmpElevateResourceHints;
use AmpProject\Optimizer;
/**
* Filter configuration array to register the transformer.
*
* @param array $configuration Associative array of configuration data.
* @return array Configuration.
*/
function filter_amp_optimizer_config( array $configuration ) {
require_once __DIR__ . '/ElevateResourceHints.php';
$configuration[ Optimizer\Configuration::KEY_TRANSFORMERS ][] = ElevateResourceHints::class;
return $configuration;
}
if ( empty( $_GET['amp_disable_elevate_resource_hints'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
add_filter( 'amp_optimizer_config', __NAMESPACE__ . '\filter_amp_optimizer_config', PHP_INT_MAX );
}
<?php
/**
* Elevate resource hints from link elements to headers.
*
* @link https://github.com/ampproject/amp-toolbox-php/issues/176
* @package Google\AmpElevateResourceHints
*/
namespace Google\AmpElevateResourceHints;
use AmpProject\Dom\Document;
use AmpProject\Optimizer\ErrorCollection;
use AmpProject\Optimizer\Transformer;
use AmpProject\Attribute;
use AmpProject\Dom\Element;
use DOMAttr;
/**
* This is a hack to send headers as part of a transformer! This is only needed because the transformer doesn't currently
* take a collection for mutating the HTTP headers. See <https://github.com/ampproject/amp-toolbox-php/issues/176#issuecomment-812142640>.
*/
final class ElevateResourceHints implements Transformer {
/**
* Attributes from link elements (other than href) to surface to Link header.
*
* @var string[]
*/
const ELEVATED_LINK_ATTRIBUTES = [
Attribute::AS_,
Attribute::CROSSORIGIN,
Attribute::IMAGESIZES,
Attribute::IMAGESRCSET,
Attribute::REFERRERPOLICY,
Attribute::REL,
'integrity',
'prefetch',
];
/**
* Link relations that should get elevated to Link headers.
*
* @var string[]
*/
const ELEVATED_LINK_RELATIONS = [
Attribute::REL_DNS_PREFETCH,
Attribute::REL_PRECONNECT,
Attribute::REL_PRELOAD,
Attribute::REL_MODULEPRELOAD,
];
/**
* Apply transformations to the provided DOM document.
*
* @param Document $document DOM document to apply the transformations to.
* @param ErrorCollection $errors Collection of errors that are collected during transformation.
*/
public function transform( Document $document, ErrorCollection $errors ) {
$links = $document->xpath->query( './link[ @rel and @href ]', $document->head );
foreach ( $links as $link ) {
/** @var Element $link */
$relations = preg_split( '/\s+/', trim( $link->getAttribute( Attribute::REL ) ) );
if ( 0 === count( array_intersect( $relations, self::ELEVATED_LINK_RELATIONS ) ) ) {
continue;
}
$header = sprintf( 'Link: <%s>', esc_url_raw( $link->getAttribute( Attribute::HREF ) ) );
foreach ( $link->attributes as $attribute ) {
/** @var DOMAttr $attribute */
if ( in_array( $attribute->nodeName, self::ELEVATED_LINK_ATTRIBUTES, true ) ) {
$header .= sprintf( '; %s="%s"', $attribute->nodeName, addcslashes( $attribute->nodeValue, '"\\' ) );
}
}
header( $header, false );
$link->parentNode->removeChild( $link );
}
}
}
@westonruter
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment