Skip to content

Commit

Permalink
Prepare for rocket head ordering (#7304)
Browse files Browse the repository at this point in the history
  • Loading branch information
wordpressfan authored Feb 20, 2025
2 parents d4ad534 + 11d44ef commit fc03b07
Show file tree
Hide file tree
Showing 29 changed files with 1,176 additions and 235 deletions.
139 changes: 139 additions & 0 deletions inc/Engine/Common/Head/ElementTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
<?php
declare(strict_types=1);
namespace WP_Rocket\Engine\Common\Head;

/**
* Element trait.
*/
trait ElementTrait {
/**
* Preload link.
*
* @param array $args Element args.
* @return array|string[]
*/
protected function preload_link( array $args = [] ) {
$args['rel'] = 'preload';
$args[] = 'data-rocket-preload';
return $this->link( $args );
}

/**
* Preconnect link.
*
* @param array $args Element args.
* @return array|string[]
*/
protected function preconnect_link( array $args = [] ) {
$args['rel'] = 'preconnect';
return $this->link( $args );
}

/**
* Dns_prefetch link.
*
* @param array $args Element args.
* @return array|string[]
*/
protected function dns_prefetch_link( array $args = [] ) {
$args['rel'] = 'dns-prefetch';
return $this->link( $args );
}

/**
* Prefetch link.
*
* @param array $args Element args.
* @return array|string[]
*/
protected function prefetch_link( array $args = [] ) {
$args['rel'] = 'prefetch';
return $this->link( $args );
}

/**
* Prerender link.
*
* @param array $args Element args.
* @return array|string[]
*/
protected function prerender_link( array $args = [] ) {
$args['rel'] = 'prerender';
return $this->link( $args );
}

/**
* Stylesheet link.
*
* @param array $args Element args.
* @return array|string[]
*/
protected function stylesheet_link( array $args = [] ) {
$args['rel'] = 'stylesheet';
return $this->link( $args );
}

/**
* Style tag.
*
* @param string $css CSS content.
* @param array $args Element args.
* @return array|string[]
*/
protected function style_tag( string $css = '', array $args = [] ) {
$element = [
'open_tag' => '<style',
];
$element += wp_parse_args(
$args,
[
'inner_content' => $css,
]
);
$element['close_tag'] = '</style>';

return $element;
}

/**
* Noscript tag.
*
* @param string $content Element contents.
* @param array $args Element args.
* @return array|string[]
*/
protected function noscript_tag( string $content = '', array $args = [] ) {
$element = [
'open_tag' => '<noscript',
];
$element += wp_parse_args(
$args,
[
'inner_content' => $content,
]
);
$element['close_tag'] = '</noscript>';

return $element;
}

/**
* Generic link tag.
*
* @param array $args Element args.
* @return array|string[]
*/
private function link( array $args = [] ) {
$element = [
'open_tag' => '<link',
];
$element += wp_parse_args(
$args,
[
'href' => '',
]
);

return $element;
}
}
39 changes: 39 additions & 0 deletions inc/Engine/Common/Head/ServiceProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

namespace WP_Rocket\Engine\Common\Head;

use WP_Rocket\Dependencies\League\Container\ServiceProvider\AbstractServiceProvider;

/**
* Service provider.
*/
class ServiceProvider extends AbstractServiceProvider {
/**
* Array of services provided by this service provider
*
* @var array
*/
protected $provides = [
'common_head_subscriber',
];

/**
* Check if the service provider provides a specific service.
*
* @param string $id The id of the service.
*
* @return bool
*/
public function provides( string $id ): bool {
return in_array( $id, $this->provides, true );
}

/**
* Registers items with the container
*
* @return void
*/
public function register(): void {
$this->getContainer()->addShared( 'common_head_subscriber', Subscriber::class );
}
}
168 changes: 168 additions & 0 deletions inc/Engine/Common/Head/Subscriber.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
<?php
namespace WP_Rocket\Engine\Common\Head;

use WP_Rocket\Event_Management\Subscriber_Interface;

/**
* Head subscriber class.
*/
class Subscriber implements Subscriber_Interface {

/**
* Head elements array.
*
* @var array
*/
private $head_items = [];

/**
* Returns an array of events that this subscriber wants to listen to.
*
* The array key is the event name. The value can be:
*
* * The method name
* * An array with the method name and priority
* * An array with the method name, priority and number of accepted arguments
*
* For instance:
*
* * array('hook_name' => 'method_name')
* * array('hook_name' => array('method_name', $priority))
* * array('hook_name' => array('method_name', $priority, $accepted_args))
* * array('hook_name' => array(array('method_name_1', $priority_1, $accepted_args_1)), array('method_name_2', $priority_2, $accepted_args_2)))
*
* @return array
*/
public static function get_subscribed_events() {
return [
'rocket_buffer' => [ 'insert_rocket_head', 100000 ],
'rocket_head' => 'print_head_elements',
];
}

/**
* Print all head elements.
*
* @param string $content Head elements HTML.
* @return string
*/
public function print_head_elements( $content ) {
/**
* Filter Head elements array.
*
* @param array $head_items Elements to be added to head after closing of title tag.
*
* Priority 10: preconnect
* Priority 30: preload
* Priority 50: styles
* @returns array
*/
$items = wpm_apply_filters_typed( 'array', 'rocket_head_items', [] );
if ( empty( $items ) ) {
return $content;
}

$this->head_items = [];
// Combine elements.
$elements = '';
foreach ( $items as $item ) {
// Make sure that we don't have duplication based on `href` inside each `rel`.
if ( $this->is_duplicate( $item ) ) {
continue;
}
$elements .= "\n" . $this->prepare_element( $item );
}

return $content . $elements;
}

/**
* Check if the item is duplicate.
*
* @param array $item Item to check.
* @return bool
*/
private function is_duplicate( $item ) {
if ( empty( $item['rel'] ) || empty( $item['href'] ) ) {
return false;
}

if ( ! isset( $this->head_items[ $item['rel'] ] ) ) {
$this->head_items[ $item['rel'] ] = [];
}

if ( ! isset( $this->head_items[ $item['rel'] ][ $item['href'] ] ) ) {
$this->head_items[ $item['rel'] ][ $item['href'] ] = true;
return false;
}

return true;
}

/**
* Prepare element HTML from the item array.
*
* @param array $element Item element.
* @return string
*/
private function prepare_element( $element ) {
$open_tag = '';
if ( ! empty( $element['open_tag'] ) ) {
$open_tag = $element['open_tag'];
unset( $element['open_tag'] );
}

$close_tag = '';
if ( ! empty( $element['close_tag'] ) ) {
$close_tag = $element['close_tag'];
unset( $element['close_tag'] );
}

$inner_content = '';
if ( ! empty( $element['inner_content'] ) ) {
$inner_content = $element['inner_content'];
unset( $element['inner_content'] );
}

$attributes = [];

ksort( $element, SORT_NATURAL );

foreach ( $element as $key => $value ) {
if ( is_int( $key ) ) {
$attributes[] = $value;
continue;
}
$attributes[] = $key . '="' . esc_attr( $value ) . '"';
}

$attributes_html = ! empty( $attributes ) ? ' ' . implode( ' ', $attributes ) : '';

return $open_tag . $attributes_html . '>' . $inner_content . $close_tag;
}

/**
* Insert rocket_head into the buffer HTML
*
* @param string $html Buffer HTML.
* @return string
*/
public function insert_rocket_head( $html ) {
if ( empty( $html ) ) {
return $html;
}

$filtered_buffer = preg_replace(
'#</title>#iU',
'</title>' . wpm_apply_filters_typed( 'string', 'rocket_head', '' ),
$html,
1
);

if ( empty( $filtered_buffer ) ) {
return $html;
}

return $filtered_buffer;
}
}
Loading

0 comments on commit fc03b07

Please sign in to comment.