Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
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
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ private function get_common_lcp_element_external_background_image( OD_URL_Metric
private function maybe_preload_external_lcp_background_image( OD_Tag_Visitor_Context $context ): void {
// Gather the tuples of URL Metric group and the common LCP element external background image.
// Note the groups of URL Metrics do not change across invocations, we just need to compute this once for all.
// TODO: Instead of populating this here, it could be done once per invocation during the od_start_template_optimization action since the page's OD_URL_Metric_Group_Collection is available there.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See #1921

if ( ! is_array( $this->group_common_lcp_element_external_background_images ) ) {
$this->group_common_lcp_element_external_background_images = array();
foreach ( $context->url_metric_group_collection as $group ) {
Expand Down
24 changes: 14 additions & 10 deletions plugins/optimization-detective/class-od-html-tag-processor.php
Original file line number Diff line number Diff line change
Expand Up @@ -702,29 +702,33 @@ public function is_admin_bar(): bool {
}

/**
* Append HTML to the HEAD.
* Appends raw HTML to the HEAD.
*
* The provided HTML must be valid! No validation is performed.
* The provided HTML must be valid for insertion in the HEAD. No validation is currently performed. However, in the
* future the HTML Processor may be used to ensure the validity of the provided HTML. At that time, when invalid
* HTML is provided, this method may emit a `_doing_it_wrong()` warning.
*
* @since 0.4.0
*
* @param string $html HTML to inject.
* @param string $raw_html Raw HTML to inject.
*/
public function append_head_html( string $html ): void {
$this->buffered_text_replacements[ self::END_OF_HEAD_BOOKMARK ][] = $html;
public function append_head_html( string $raw_html ): void {
$this->buffered_text_replacements[ self::END_OF_HEAD_BOOKMARK ][] = $raw_html;
}

/**
* Append HTML to the BODY.
* Appends raw HTML to the BODY.
*
* The provided HTML must be valid! No validation is performed.
* The provided HTML must be valid for insertion in the BODY. No validation is currently performed. However, in the
* future the HTML Processor may be used to ensure the validity of the provided HTML. At that time, when invalid
* HTML is provided, this method may emit a `_doing_it_wrong()` warning.
*
* @since 0.4.0
*
* @param string $html HTML to inject.
* @param string $raw_html Raw HTML to inject.
*/
public function append_body_html( string $html ): void {
$this->buffered_text_replacements[ self::END_OF_BODY_BOOKMARK ][] = $html;
public function append_body_html( string $raw_html ): void {
$this->buffered_text_replacements[ self::END_OF_BODY_BOOKMARK ][] = $raw_html;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ public function track_tag(): void {
* @throws Error When property is unknown.
*/
public function __get( string $name ) {
// Note that there is intentionally not a case for 'visited_tag_state'.
// Note: There is intentionally not a 'visited_tag_state' case to expose $this->visited_tag_state.
switch ( $name ) {
case 'processor':
return $this->processor;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
<?php
/**
* Optimization Detective: OD_Template_Optimization_Context class
*
* @package optimization-detective
* @since n.e.x.t
*/

// @codeCoverageIgnoreStart
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
// @codeCoverageIgnoreEnd

/**
* Context for optimizing a template.
*
* @since n.e.x.t
*
* @property-read OD_URL_Metric_Group_Collection $url_metric_group_collection URL Metric group collection.
* @property-read positive-int|null $url_metrics_id ID for the od_url_metrics post which provided the URL Metrics in the collection.
* @property-read array<string, mixed> $normalized_query_vars Normalized query vars.
* @property-read non-empty-string $url_metrics_slug Slug for the od_url_metrics post.
* @property-read OD_Link_Collection $link_collection Link collection.
*/
final class OD_Template_Optimization_Context {

/**
* URL Metric group collection.
*
* @since n.e.x.t
* @var OD_URL_Metric_Group_Collection
*/
private $url_metric_group_collection;

/**
* ID for the od_url_metrics post which provided the URL Metrics in the collection.
*
* May be null if no post has been created yet.
*
* @since n.e.x.t
* @var positive-int|null
*/
private $url_metrics_id;

/**
* Normalized query vars.
*
* @since n.e.x.t
* @var array<string, mixed>
*/
private $normalized_query_vars;

/**
* Slug for the od_url_metrics post.
*
* @since n.e.x.t
* @var non-empty-string
*/
private $url_metrics_slug;

/**
* Link collection.
*
* @since n.e.x.t
* @var OD_Link_Collection
*/
private $link_collection;

/**
* Constructor.
*
* @since n.e.x.t
* @access private
*
* @param OD_URL_Metric_Group_Collection $url_metric_group_collection URL Metric group collection.
* @param OD_Link_Collection $link_collection Link collection.
* @param array<string, mixed> $normalized_query_vars Normalized query vars.
* @param non-empty-string $url_metrics_slug Slug for the od_url_metrics post.
* @param positive-int|null $url_metrics_id ID for the od_url_metrics post which provided the URL Metrics in the collection. May be null if no post has been created yet.
*/
public function __construct( OD_URL_Metric_Group_Collection $url_metric_group_collection, OD_Link_Collection $link_collection, array $normalized_query_vars, string $url_metrics_slug, ?int $url_metrics_id ) {
$this->url_metric_group_collection = $url_metric_group_collection;
$this->link_collection = $link_collection;
$this->normalized_query_vars = $normalized_query_vars;
$this->url_metrics_slug = $url_metrics_slug;
$this->url_metrics_id = $url_metrics_id;
}

/**
* Gets a property.
*
* @since n.e.x.t
*
* @param string $name Property name.
* @return mixed Property value.
*
* @throws Error When property is unknown.
*/
public function __get( string $name ) {
// Note: There is intentionally not a 'processor' case to expose $this->processor.
switch ( $name ) {
case 'url_metrics_id':
return $this->url_metrics_id;
case 'url_metric_group_collection':
return $this->url_metric_group_collection;
case 'normalized_query_vars':
return $this->normalized_query_vars;
case 'url_metrics_slug':
return $this->url_metrics_slug;
case 'link_collection':
return $this->link_collection;
default:
throw new Error(
esc_html(
sprintf(
/* translators: %s is class member variable name */
__( 'Unknown property %s.', 'optimization-detective' ),
__CLASS__ . '::$' . $name
)
)
);
}
}
}
1 change: 1 addition & 0 deletions plugins/optimization-detective/load.php
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@
require_once __DIR__ . '/detection.php';

// Optimization logic.
require_once __DIR__ . '/class-od-template-optimization-context.php';

Check warning on line 127 in plugins/optimization-detective/load.php

View check run for this annotation

Codecov / codecov/patch

plugins/optimization-detective/load.php#L127

Added line #L127 was not covered by tests
require_once __DIR__ . '/class-od-link-collection.php';
require_once __DIR__ . '/class-od-tag-visitor-registry.php';
require_once __DIR__ . '/class-od-visited-tag-state.php';
Expand Down
60 changes: 48 additions & 12 deletions plugins/optimization-detective/optimization.php
Original file line number Diff line number Diff line change
Expand Up @@ -245,8 +245,16 @@ function od_optimize_template_output_buffer( string $buffer ): string {
return $buffer;
}

$slug = od_get_url_metrics_slug( od_get_normalized_query_vars() );
$post = OD_URL_Metrics_Post_Type::get_post( $slug );
$query_vars = od_get_normalized_query_vars();
$slug = od_get_url_metrics_slug( $query_vars );
$post = OD_URL_Metrics_Post_Type::get_post( $slug );

/**
* Post ID.
*
* @var positive-int|null $post_id
*/
$post_id = $post instanceof WP_Post ? $post->ID : null;

$tag_visitor_registry = new OD_Tag_Visitor_Registry();

Expand All @@ -259,22 +267,40 @@ function od_optimize_template_output_buffer( string $buffer ): string {
*/
do_action( 'od_register_tag_visitors', $tag_visitor_registry );

$current_etag = od_get_current_url_metrics_etag( $tag_visitor_registry, $wp_the_query, od_get_current_theme_template() );
$group_collection = new OD_URL_Metric_Group_Collection(
$current_etag = od_get_current_url_metrics_etag( $tag_visitor_registry, $wp_the_query, od_get_current_theme_template() );
$group_collection = new OD_URL_Metric_Group_Collection(
$post instanceof WP_Post ? OD_URL_Metrics_Post_Type::get_url_metrics_from_post( $post ) : array(),
$current_etag,
od_get_breakpoint_max_widths(),
od_get_url_metrics_breakpoint_sample_size(),
od_get_url_metric_freshness_ttl()
);
$link_collection = new OD_Link_Collection();
$link_collection = new OD_Link_Collection();

$template_optimization_context = new OD_Template_Optimization_Context(
$group_collection,
$link_collection,
$query_vars,
$slug,
$post_id
);

/**
* Fires before Optimization Detective starts optimizing the template.
*
* @since n.e.x.t
*
* @param OD_Template_Optimization_Context $template_optimization_context Template optimization context.
*/
do_action( 'od_start_template_optimization', $template_optimization_context );

$visited_tag_state = new OD_Visited_Tag_State();
$tag_visitor_context = new OD_Tag_Visitor_Context(
$processor,
$group_collection,
$link_collection,
$visited_tag_state,
$post instanceof WP_Post && $post->ID > 0 ? $post->ID : null
$post_id
);
$current_tag_bookmark = 'optimization_detective_current_tag';
$visitors = iterator_to_array( $tag_visitor_registry );
Expand Down Expand Up @@ -322,7 +348,23 @@ function od_optimize_template_output_buffer( string $buffer ): string {
$visited_tag_state->reset();
} while ( $processor->next_tag( array( 'tag_closers' => 'skip' ) ) );

// Inject detection script.
// TODO: When optimizing above, if we find that there is a stored LCP element but it fails to match, it should perhaps set $needs_detection to true and send the request with an override nonce. However, this would require backtracking and adding the data-od-xpath attributes.
if ( $needs_detection ) {
$processor->append_body_html( od_get_detection_script( $slug, $group_collection ) );
}

/**
* Fires after Optimization Detective finishes optimizing the template.
*
* @since n.e.x.t
*
* @param OD_Template_Optimization_Context $template_optimization_context Template optimization context.
*/
do_action( 'od_finish_template_optimization', $template_optimization_context );

// Send any preload links in a Link response header and in a LINK tag injected at the end of the HEAD.
// Additional links may have been added at the od_finish_template_optimization action, so this must come after.
if ( count( $link_collection ) > 0 ) {
$response_header_links = $link_collection->get_response_header();
if ( ! is_null( $response_header_links ) && ! headers_sent() ) {
Expand All @@ -331,11 +373,5 @@ function od_optimize_template_output_buffer( string $buffer ): string {
$processor->append_head_html( $link_collection->get_html() );
}

// Inject detection script.
// TODO: When optimizing above, if we find that there is a stored LCP element but it fails to match, it should perhaps set $needs_detection to true and send the request with an override nonce. However, this would require backtracking and adding the data-od-xpath attributes.
if ( $needs_detection ) {
$processor->append_body_html( od_get_detection_script( $slug, $group_collection ) );
}

return $processor->get_updated_html();
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading