Skip to content

Latest commit

 

History

History
385 lines (278 loc) · 18.3 KB

File metadata and controls

385 lines (278 loc) · 18.3 KB

Optimization Detective Documentation:

Optimization Detective Hooks

Actions

Action: od_init (argument: plugin version)

Fires when the Optimization Detective is initializing.

This action is useful for loading extension code that depends on Optimization Detective to be running. The version of the plugin is passed as the sole argument so that if the required version is not present, the callback can short circuit.

Example:

add_action( 'od_init', function ( string $version ) {
	if ( version_compare( $version, '1.0', '<' ) ) {
		add_action( 'admin_notices', 'my_plugin_warn_optimization_plugin_outdated' );
		return;
	}

	// Bootstrap the Optimization Detective extension.
	require_once __DIR__ . '/functions.php';
	// ...
} );

Action: od_register_tag_visitors (argument: OD_Tag_Visitor_Registry)

Fires to register tag visitors before walking over the document to perform optimizations.

Once a page has finished rendering and the output buffer is processed, the page contents are loaded into an HTML Tag Processor instance. It then iterates over each tag in the document, and at each open tag it will invoke all registered tag visitors. A tag visitor is simply a callable (such as a regular function, closure, or even a class with an __invoke method defined). The tag visitor callback is invoked by passing an instance of the OD_Tag_Visitor_Context object which includes the following read-only properties:

  • $processor (OD_HTML_Tag_Processor): The processor with the cursor at the current open tag.
  • $url_metric_group_collection (OD_URL_Metric_Group_Collection): The URL Metrics which may include information about the current tag to inform what optimizations the callback performs.
  • $link_collection (OD_Link_Collection): Collection of links which will be added to the HEAD when the page is served. This allows you to add preload links and preconnect links as needed.
  • $url_metrics_id (positive-int|null): The post ID for the od_url_metrics post from which the URL Metrics were loaded (if any). For advanced usage.

Note that you are free to call $processor->next_tag() in the callback (such as to walk over any child elements) since the tag processor's cursor will be reset to the tag after the callback finishes.

When a tag visitor sees it is at a relevant open tag (e.g. by checking $processor->get_tag()), it can call the $context->track_tag() method to indicate that the tag should be measured during detection. This will cause the tag to be included among the elements in the stored URL Metrics. The element data includes properties such as intersectionRatio, intersectionRect, and boundingClientRect (provided by an IntersectionObserver) as well as whether the tag is the LCP element (isLCP) or LCP element candidate (isLCPCandidate). This method should not be called if the current tag is not relevant for the tag visitor or if the tag visitor callback does not need to query the provided OD_URL_Metric_Group_Collection instance to apply the desired optimizations. (In addition to calling the $context->track_tag(), a callback may also return true to indicate the tag should be tracked.)

Here's an example tag visitor that depends on URL Metrics data:

$tag_visitor_registry->register(
	'lcp-img-fetchpriority-high',
	static function ( OD_Tag_Visitor_Context $context ): void {
		if ( $context->processor->get_tag() !== 'IMG' ) {
			return; // Tag is not relevant for this tag visitor.
		}

		// Mark the tag for measurement during detection so it is included among the elements stored in URL Metrics.
		$context->track_tag();

		// Make sure fetchpriority=high is added to LCP IMG elements based on the captured URL Metrics.
		$common_lcp_element = $context->url_metric_group_collection->get_common_lcp_element();
		if (
			null !== $common_lcp_element
			&&
			$common_lcp_element->get_xpath() === $context->processor->get_xpath()
		) {
			$context->processor->set_attribute( 'fetchpriority', 'high' );
		}
	}
);

Please note this implementation of setting fetchpriority=high on the LCP IMG element is simplified. Please see the Image Prioritizer extension for a more robust implementation.

Here's an example tag visitor that does not depend on any URL Metrics data:

$tag_visitor_registry->register(
	'img-decoding-async',
	static function ( OD_Tag_Visitor_Context $context ): bool {
		if ( $context->processor->get_tag() !== 'IMG' ) {
			return; // Tag is not relevant for this tag visitor.
		}

		// Set the decoding attribute if it is absent.
		if ( null === $context->processor->get_attribute( 'decoding' ) ) {
			$context->processor->set_attribute( 'decoding', 'async' );
		}
	}
);

Refer to Image Prioritizer and Embed Optimizer for additional examples of how tag visitors are used.

Action: od_start_template_optimization (argument: OD_Template_Optimization_Context)

Fires before Optimization Detective starts iterating over the document in the output buffer.

This is before any of the registered tag visitors have been invoked.

It is important to note that this action fires after the entire template has been rendered into the output buffer. In other words, it will fire after the wp_footer action.

This action runs before any of the registered tag visitors have been invoked in the current response. It is useful for an extension to gather the required information from the currently-stored URL Metrics for tag visitors to later leverage. See example from the Image Prioritizer plugin where it can be used to determine what the common external LCP background-image is for each viewport group up front so that this doesn't have to be computed when a tag visitor is invoked.

This action can be used if a site wants to prevent storing a response in the page cache until it has collected URL Metrics from both mobile and desktop:

add_action(
	'od_start_template_optimization',
	static function ( OD_Template_Optimization_Context $context ) {
		if ( 
			$context->url_metric_group_collection->get_first_group()->count() === 0
			||
			$context->url_metric_group_collection->get_last_group()->count() === 0
		 ) {
			header( 'Cache-Control: private' );
		}
	}
);

This could just as well be done at od_finish_template_optimization since the headers are not sent until after that action completes and the output buffer is returned.

Action: od_finish_template_optimization (argument: OD_Template_Optimization_Context)

Fires after Optimization Detective has finished iterating over the document in the output buffer.

This is after all the registered tag visitors have been invoked.

This action runs after all the tags in a document have been visited and so no additional tag visitor will be invoked. This action has limited usefulness at the moment, but see #1931 for possibilities for what it could be used for in the future.

Action: od_url_metric_stored (argument: OD_URL_Metric_Store_Request_Context)

Fires whenever a URL Metric was successfully stored.

The supplied context object includes these properties:

  • $request: The WP_REST_Request for storing the URL Metric.
  • $post_id: The post ID for the od_url_metric post.
  • $url_metric: The newly-stored URL Metric.
  • $url_metric_group: The viewport group that the URL Metric was added to.
  • $url_metric_group_collection: The OD_URL_Metric_Group_Collection instance to which the URL Metric was added.

Filters

Filter: od_use_web_vitals_attribution_build (default: false)

Filters whether to use the web-vitals.js build with attribution.

When using the attribution build of web-vitals, the metric object passed to report callbacks registered via onTTFB, onFCP, onLCP, onCLS, and onINP will include an additional attribution property. For details, please refer to the web-vitals documentation.

For example, to opt in to using the attribution build:

add_filter( 'od_use_web_vitals_attribution_build', '__return_true' );

Note that the attribution build is slightly larger than the standard build, so this is why it is not used by default. The additional attribution data is made available to client-side extension script modules registered via the od_extension_module_urls filter.

Filter: od_breakpoint_max_widths (default: array(480, 600, 782))

Filters the breakpoint max widths to group URL Metrics for various viewports. Each number represents the maximum width (inclusive) for a given breakpoint. So if there is one number, 480, then this means there will be two viewport groupings, one for 0<=480, and another >480. If instead there are the two breakpoints defined, 480 and 782, then this means there will be three viewport groups of URL Metrics, one for 0<=480 (i.e. mobile), another 481<=782 (i.e. phablet/tablet), and another >782 (i.e. desktop).

These default breakpoints are reused from Gutenberg which appear to be used the most in media queries that affect frontend styles.

Filter: od_can_optimize_response (default: boolean condition, see below)

Filters whether the current response can be optimized. By default, detection and optimization are only performed when:

  1. It’s not a search template (is_search()).
  2. It’s not a post embed template (is_embed()).
  3. It’s not the Customizer preview (is_customize_preview())
  4. It’s not the response to a POST request.
  5. There is at least one queried post on the page. This is used to facilitate the purging of page caches after a new URL Metric is stored.

To force every response to be optimized regardless of the conditions above, you can do:

add_filter( 'od_can_optimize_response', '__return_true' );

Filter: od_url_metrics_breakpoint_sample_size (default: 3)

Filters the sample size for a breakpoint's URL Metrics on a given URL. The sample size must be greater than zero. You can increase the sample size if you want better guarantees that the applied optimizations will be accurate. During development, it may be helpful to reduce the sample size to 1 (along with setting the od_url_metric_storage_lock_ttl and od_url_metric_freshness_ttl filters below) so that you don't have to keep reloading the page to collect new URL Metrics to flush out stale ones during active development:

add_filter( 'od_url_metrics_breakpoint_sample_size', function (): int {
	return 1;
} );

Filter: od_url_metric_storage_lock_ttl (default: 60 seconds, except 0 for authorized logged-in users)

Filters how long the current IP is locked from submitting another URL metric storage REST API request.

Filtering the TTL to zero will disable any URL Metric storage locking. This is useful, for example, to disable locking when a user is logged-in with code like the following:

add_filter( 'od_metrics_storage_lock_ttl', function ( int $ttl ): int {
	return is_user_logged_in() ? 0 : $ttl;
} );

By default, the TTL is zero (0) for authorized users and sixty (60) for everyone else. Whether the current user is authorized is determined by whether the user has the od_store_url_metric_now capability. This custom capability by default maps to the manage_options primitive capability via the user_has_cap filter.

During development this is useful to set to zero so you can quickly collect new URL Metrics by reloading the page without having to wait for the storage lock to release:

add_filter( 'od_metrics_storage_lock_ttl', function ( int $ttl ): int {
	return 0;
} );

Filter: od_url_metric_freshness_ttl (default: 1 week in seconds)

Filters age (TTL) for which a URL Metric can be considered fresh.

The freshness TTL (time to live) value can be one of the following values:

  • A positive integer (e.g. 3600, HOUR_IN_SECONDS) allows a URL Metric to be fresh for a given period of time into the future.
  • A negative integer (-1) disables timestamp-based freshness checks, making URL Metrics stay fresh indefinitely unless the current ETag changes.
  • A value of zero (0) considers URL Metrics to always be stale, which is useful during development. Never do this on a production site since this can cause a database write for every visitor!

The default value is WEEK_IN_SECONDS since changes to the post/page (or the site overall) will cause a change to the current ETag used for URL Metrics. This causes the relevant existing URL Metrics with the previous ETag to be considered stale, allowing new URL Metrics to be collected before the freshness TTL has expired. See the od_current_url_metrics_etag_data filter to customize the ETag data.

For sites where content doesn't change frequently, you can disable the timestamp-based staleness check as follows:

add_filter( 'od_url_metric_freshness_ttl', static function (): int {
    return -1;
} );

As noted above, during development you can set the freshness TTL to zero so that you don't have to wait for new URL Metrics to be requested when developing a new optimization:

add_filter( 'od_url_metric_freshness_ttl', '__return_zero' );

Filter: od_minimum_viewport_aspect_ratio (default: 0.4)

Filters the minimum allowed viewport aspect ratio for URL Metrics.

The 0.4 value is intended to accommodate the phone with the greatest known aspect ratio at 21:9 when rotated 90 degrees to 9:21 (0.429). During development when you have the DevTools console open on the right, the viewport aspect ratio will be smaller than normal. In this case, you may want to set this to 0:

add_filter( 'od_minimum_viewport_aspect_ratio', static function (): int {
	return 0;
} );

Filter: od_maximum_viewport_aspect_ratio (default: 2.5)

Filters the maximum allowed viewport aspect ratio for URL Metrics.

The 2.5 value is intended to accommodate the phone with the greatest known aspect ratio at 21:9 (2.333).

During development when you have the DevTools console open on the bottom, for example, the viewport aspect ratio will be larger than normal. In this case, you may want to increase the maximum aspect ratio:

add_filter( 'od_maximum_viewport_aspect_ratio', static function (): int {
	return 5;
} );

Filter: od_template_output_buffer (default: the HTML response)

Filters the template output buffer prior to sending to the client. This filter is added to implement #43258 in WordPress core.

Filter: od_url_metric_schema_element_item_additional_properties (default: empty array)

Filters additional schema properties which should be allowed for an element's item in a URL Metric.

For example to add a resizedBoundingClientRect property:

<?php
add_filter(
	'od_url_metric_schema_element_item_additional_properties',
	static function ( array $additional_properties ): array {
		$additional_properties['resizedBoundingClientRect'] = array(
			'type'       => 'object',
			'properties' => array_fill_keys(
				array(
					'width',
					'height',
					'x',
					'y',
					'top',
					'right',
					'bottom',
					'left',
				),
				array(
					'type'     => 'number',
					'required' => true,
				)
			),
		);
		return $additional_properties;
	}
);

See also example usage in Embed Optimizer.

Filter: od_url_metric_schema_root_additional_properties (default: empty array)

Filters additional schema properties which should be allowed at the root of a URL Metric.

The usage here is the same as the previous filter, except it allows new properties to be added to the root of the URL Metric and not just to one of the object items in the elements property.

Filter: od_extension_module_urls (default: empty array of strings)

Filters the list of extension script module URLs to import when performing detection.

For example:

add_filter(
	'od_extension_module_urls',
	static function ( array $extension_module_urls ): array {
		$extension_module_urls[] = add_query_arg( 'ver', '1.0', plugin_dir_url( __FILE__ ) . 'detect.js' );
		return $extension_module_urls;
	}
);

See also example usage in Embed Optimizer. Note in particular the structure of the plugin’s detect.js script module, how it exports initialize and finalize functions which Optimization Detective then calls when the page loads and when the page unloads, at which time the URL Metric is constructed and sent to the server for storage. Refer also to the TypeScript type definitions.

Filter: od_current_url_metrics_etag_data (default: array<string, mixed>)

Filters the data that goes into computing the current ETag for URL Metrics.

The ETag is a unique identifier that changes whenever the underlying data used to generate it changes. By default, the ETag calculation includes:

  1. The active theme and current version (for both parent and child themes).
  2. The queried object ID, post type, and modified date.
  3. The list of registered tag visitors.
  4. The IDs and modified times of posts in The Loop.
  5. The current theme template used to render the page.
  6. The list of active plugins.

A change in ETag means that any previously-collected URL Metrics will be immediately considered stale. When the ETag for URL Metrics in a complete viewport group no longer matches the current environment's ETag, new URL Metrics will then begin to be collected until there are no more stored URL Metrics with the old ETag.

Filter: od_url_metric_garbage_collection_ttl (default: 3 months in seconds)

Filters the expiration age (TTL) after which an od_url_metrics post will be garbage collected if it has not been modified since that time.

add_filter( 'od_url_metric_garbage_collection_ttl', function (): int {
	return 6 * MONTH_IN_SECONDS;
} );

To prevent garbage collection of od_url_metrics posts, add a filter that returns zero:

add_filter( 'od_url_metric_garbage_collection_ttl', '__return_zero' );