Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,14 @@ public function add_link( array $attributes, ?int $minimum_viewport_width, ?int
}

/**
* Get adjacent-deduplicated links.
* Prepare links by deduplicating adjacent links and adding media attributes.
Comment thread
AhmarZaidi marked this conversation as resolved.
Outdated
*
* When two links are identical except for their minimum/maximum widths which are also consecutive, then merge them
* together.
* together. Also, add media attributes to the links.
*
* @return array<int, Link> Links with adjacent-duplicates merged together.
* @return array<int, Link> Prepared links with adjacent-duplicates merged together and media attributes added.
*/
private function get_adjacent_deduplicated_links(): array {
private function prepare_links(): array {
Comment thread
AhmarZaidi marked this conversation as resolved.
Outdated
$links = $this->links;

usort(
Expand All @@ -100,7 +100,8 @@ static function ( array $a, array $b ): int {
}
);

return array_reduce(
// Deduplicating adjacent links.
Comment thread
AhmarZaidi marked this conversation as resolved.
Outdated
$prepared_links = array_reduce(
$links,
/**
* Reducer.
Expand All @@ -125,7 +126,7 @@ static function ( array $carry, array $link ): array {
) {
$last_link['maximum_viewport_width'] = max( $last_link['maximum_viewport_width'], $link['maximum_viewport_width'] );

// Update the last link with the new maximum viewport with.
// Update the last link with the new maximum viewport width.
$carry[ count( $carry ) - 1 ] = $last_link;
} else {
$carry[] = $link;
Expand All @@ -134,6 +135,20 @@ static function ( array $carry, array $link ): array {
},
array()
);

// Add media attributes to the deduplicated links.
foreach ( $prepared_links as &$link ) {
$media_attributes = array( 'screen' );
if ( null !== $link['minimum_viewport_width'] && $link['minimum_viewport_width'] > 0 ) {
$media_attributes[] = sprintf( '(min-width: %dpx)', $link['minimum_viewport_width'] );
}
if ( null !== $link['maximum_viewport_width'] && PHP_INT_MAX !== $link['maximum_viewport_width'] ) {
$media_attributes[] = sprintf( '(max-width: %dpx)', $link['maximum_viewport_width'] );
}
$link['attributes']['media'] = implode( ' and ', $media_attributes );
}

return $prepared_links;
}

/**
Expand All @@ -144,16 +159,7 @@ static function ( array $carry, array $link ): array {
public function get_html(): string {
$link_tags = array();

foreach ( $this->get_adjacent_deduplicated_links() as $link ) {
$media_features = array( 'screen' );
if ( null !== $link['minimum_viewport_width'] && $link['minimum_viewport_width'] > 0 ) {
$media_features[] = sprintf( '(min-width: %dpx)', $link['minimum_viewport_width'] );
}
if ( null !== $link['maximum_viewport_width'] && PHP_INT_MAX !== $link['maximum_viewport_width'] ) {
$media_features[] = sprintf( '(max-width: %dpx)', $link['maximum_viewport_width'] );
}
$link['attributes']['media'] = implode( ' and ', $media_features );

foreach ( $this->prepare_links() as $link ) {
$link_tag = '<link data-od-added-tag rel="preload"';
foreach ( $link['attributes'] as $name => $value ) {
$link_tag .= sprintf( ' %s="%s"', $name, esc_attr( $value ) );
Expand All @@ -166,6 +172,31 @@ public function get_html(): string {
return implode( '', $link_tags );
}

/**
* Constructs the Link HTTP response header.
*
* @return string|null Link HTTP response header, or null if there are none.
*/
public function get_response_header(): ?string {
$link_headers = array();

foreach ( $this->prepare_links() as $link ) {
$link_header = '<' . esc_url_raw( $link['attributes']['href'] ?? '' ) . '>; rel="preload"';
Comment thread
AhmarZaidi marked this conversation as resolved.
Outdated
foreach ( $link['attributes'] as $name => $value ) {
if ( 'href' !== $name ) {
$link_header .= sprintf( '; %s="%s"', $name, rawurlencode( $value ) );
}
Comment thread
AhmarZaidi marked this conversation as resolved.
Outdated
}

$link_headers[] = $link_header;
}
if ( count( $link_headers ) === 0 ) {
return null;
}

Comment thread
AhmarZaidi marked this conversation as resolved.
return 'Link: ' . implode( ', ', $link_headers );
}

/**
* Counts the links.
*
Expand Down
6 changes: 5 additions & 1 deletion plugins/optimization-detective/optimization.php
Original file line number Diff line number Diff line change
Expand Up @@ -189,8 +189,12 @@ function od_optimize_template_output_buffer( string $buffer ): string {
$generator->next();
}

// Inject any preload links at the end of the HEAD.
// Send any preload links in a Link response header and in a LINK tag injected at the end of the HEAD.
if ( count( $preload_links ) > 0 ) {
$response_header_links = $preload_links->get_response_header();
if ( ! is_null( $response_header_links ) && ! headers_sent() ) {
header( $response_header_links, false );
}
$walker->append_head_html( $preload_links->get_html() );
}

Expand Down