Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prepare for rocket head ordering #7304

Merged
merged 30 commits into from
Feb 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
a415242
WIP
wordpressfan Jan 29, 2025
7ea59ac
make it works
wordpressfan Jan 29, 2025
2cd1412
add ElementTrait to make it easier to add elements into head
wordpressfan Feb 3, 2025
740b8e4
add docs for priorities and start refactoring
wordpressfan Feb 4, 2025
b71d24e
refactor RUCSS
wordpressfan Feb 4, 2025
838dafa
refactor google fonts
wordpressfan Feb 5, 2025
27c7d74
finish all instances, before testing
wordpressfan Feb 12, 2025
83e78ce
remove not used method anymore
wordpressfan Feb 12, 2025
e3c99db
minor fixes with testing
wordpressfan Feb 12, 2025
aedcec7
move the new line before element
wordpressfan Feb 12, 2025
2cfd353
fix phpcs
wordpressfan Feb 12, 2025
db9547b
start fixing unit tests with another approach
wordpressfan Feb 17, 2025
7df16c8
continue fixing unit tests
wordpressfan Feb 18, 2025
2b1b2b9
continue fixing unit tests 2
wordpressfan Feb 18, 2025
830c406
fix all unit tests
wordpressfan Feb 18, 2025
e572c35
fix phpcs
wordpressfan Feb 18, 2025
fcd2721
change the way we add rocket_head to be inside rocket_buffer
wordpressfan Feb 18, 2025
bc3e7e1
start fixing integration tests
wordpressfan Feb 19, 2025
515d1e2
fixing most integration tests
wordpressfan Feb 19, 2025
3ab899a
fix more tests
wordpressfan Feb 19, 2025
b29742e
fix unit
wordpressfan Feb 19, 2025
1e3d3cf
fix phpcs
wordpressfan Feb 19, 2025
cfea5ad
fix unit again :(
wordpressfan Feb 19, 2025
1c24182
try fixing the last test
wordpressfan Feb 20, 2025
31eb97f
fix phpstan
wordpressfan Feb 20, 2025
8249b73
skip this specific test for now
wordpressfan Feb 20, 2025
9133ab3
skip this specific test for now
wordpressfan Feb 20, 2025
09d17c6
fix phpstan
wordpressfan Feb 20, 2025
55ff16e
last touch
wordpressfan Feb 20, 2025
11d44ef
Merge branch 'feature/3.19' into feature/add-rocket-head
wordpressfan Feb 20, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading