Optimization Detective Documentation:
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';
// ...
} );
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 theHEAD
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 theod_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.
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.
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.
Fires whenever a URL Metric was successfully stored.
The supplied context object includes these properties:
$request
: TheWP_REST_Request
for storing the URL Metric.$post_id
: The post ID for theod_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
: TheOD_URL_Metric_Group_Collection
instance to which the URL Metric was added.
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.
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.
Filters whether the current response can be optimized. By default, detection and optimization are only performed when:
- It’s not a search template (
is_search()
). - It’s not a post embed template (
is_embed()
). - It’s not the Customizer preview (
is_customize_preview()
) - It’s not the response to a
POST
request. - 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' );
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;
} );
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' );
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;
} );
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;
} );
Filters the template output buffer prior to sending to the client. This filter is added to implement #43258 in WordPress core.
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.
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.
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.
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:
- The active theme and current version (for both parent and child themes).
- The queried object ID, post type, and modified date.
- The list of registered tag visitors.
- The IDs and modified times of posts in The Loop.
- The current theme template used to render the page.
- 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.
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' );