From 2892386b74f455fef225c0731f8b6fa7a6dfe699 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Thu, 9 Jan 2025 11:55:50 -0800 Subject: [PATCH 1/8] Flesh out inline phpdoc for od_init and od_register_tag_visitors actions --- plugins/optimization-detective/helper.php | 16 +++++ .../optimization-detective/optimization.php | 71 +++++++++++++++++++ 2 files changed, 87 insertions(+) diff --git a/plugins/optimization-detective/helper.php b/plugins/optimization-detective/helper.php index 27073205d1..2159e18746 100644 --- a/plugins/optimization-detective/helper.php +++ b/plugins/optimization-detective/helper.php @@ -19,6 +19,22 @@ function od_initialize_extensions(): void { /** * Fires when extensions to Optimization Detective can be loaded and initialized. * + * 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'; + * // ... + * } ); + * * @since 0.7.0 * * @param string $version Optimization Detective version. diff --git a/plugins/optimization-detective/optimization.php b/plugins/optimization-detective/optimization.php index fa72a6194e..de5c8c8d26 100644 --- a/plugins/optimization-detective/optimization.php +++ b/plugins/optimization-detective/optimization.php @@ -204,6 +204,77 @@ function od_optimize_template_output_buffer( string $buffer ): string { /** * 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 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. + * + * Note that you are free to call `next_tag()` in the callback (such as to walk over any child elements) since the + * cursor will be reset to the tag after the callback finishes. + * + * A tag visitor callback returns a boolean value. When it returns `true`, then Optimization Detective will mark the + * tag as needing to be included among the elements stored in URL Metrics. The element data includes properties such + * as intersectionRatio, intersectionRect, and boundingClientRect as well as whether it is the LCP element. 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, it can just return `false`. An + * element will be tracked in URL Metrics if _any_ tag visitor callback returns `true` when visiting the tag. + * + * 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 ): bool { + * if ( $context->processor->get_tag() !== 'IMG' ) { + * return false; // Tag is not relevant for this tag visitor. + * } + * + * // Make sure fetchpriority=high is added to LCP IMG elements. + * $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' ); + * } + * + * // Must return true so that the tag is included among the elements stored in URL Metrics. + * return true; + * } + * ); + * + * 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 false; // 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' ); + * } + * + * // There's no need to query OD_URL_Metric_Group_Collection, so this element + * // doesn't need to be tracked in URL Metrics and the callback can return false. + * return false; + * } + * ); + * + * Refer to [Image Prioritizer](https://github.com/WordPress/performance/tree/trunk/plugins/image-prioritizer) and + * [Embed Optimizer](https://github.com/WordPress/performance/tree/trunk/plugins/embed-optimizer) for additional + * examples of how tag visitors are used. + * * @since 0.3.0 * * @param OD_Tag_Visitor_Registry $tag_visitor_registry Tag visitor registry. From 9c938e14ac787570b2f91b7168844dae6e7fe234 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Thu, 9 Jan 2025 13:06:23 -0800 Subject: [PATCH 2/8] Remove private access tags for public interfaces --- .../class-od-data-validation-exception.php | 1 - plugins/optimization-detective/class-od-element.php | 1 - plugins/optimization-detective/class-od-html-tag-processor.php | 1 - plugins/optimization-detective/class-od-link-collection.php | 1 - plugins/optimization-detective/class-od-tag-visitor-context.php | 1 - plugins/optimization-detective/class-od-tag-visitor-registry.php | 1 - .../class-od-url-metric-group-collection.php | 1 - plugins/optimization-detective/class-od-url-metric-group.php | 1 - plugins/optimization-detective/class-od-url-metric.php | 1 - .../storage/class-od-url-metric-store-request-context.php | 1 - 10 files changed, 10 deletions(-) diff --git a/plugins/optimization-detective/class-od-data-validation-exception.php b/plugins/optimization-detective/class-od-data-validation-exception.php index 2efab6fc9c..39539197fa 100644 --- a/plugins/optimization-detective/class-od-data-validation-exception.php +++ b/plugins/optimization-detective/class-od-data-validation-exception.php @@ -15,6 +15,5 @@ * Exception thrown when failing to validate URL Metrics data. * * @since 0.1.0 - * @access private */ final class OD_Data_Validation_Exception extends Exception {} diff --git a/plugins/optimization-detective/class-od-element.php b/plugins/optimization-detective/class-od-element.php index c7f243d99b..b08da92726 100644 --- a/plugins/optimization-detective/class-od-element.php +++ b/plugins/optimization-detective/class-od-element.php @@ -20,7 +20,6 @@ * @todo The above implements tag should account for additional undefined keys which can be supplied by extending the element schema. May depend on . * * @since 0.7.0 - * @access private */ class OD_Element implements ArrayAccess, JsonSerializable { diff --git a/plugins/optimization-detective/class-od-html-tag-processor.php b/plugins/optimization-detective/class-od-html-tag-processor.php index 48ad11d126..8f2c23a5ca 100644 --- a/plugins/optimization-detective/class-od-html-tag-processor.php +++ b/plugins/optimization-detective/class-od-html-tag-processor.php @@ -15,7 +15,6 @@ * Extension to WP_HTML_Tag_Processor that supports injecting HTML and obtaining XPath for the current tag. * * @since 0.1.1 - * @access private */ final class OD_HTML_Tag_Processor extends WP_HTML_Tag_Processor { diff --git a/plugins/optimization-detective/class-od-link-collection.php b/plugins/optimization-detective/class-od-link-collection.php index 4e41a7af74..33eef580a0 100644 --- a/plugins/optimization-detective/class-od-link-collection.php +++ b/plugins/optimization-detective/class-od-link-collection.php @@ -36,7 +36,6 @@ * * @since 0.3.0 * @since 0.4.0 Renamed from OD_Preload_Link_Collection. - * @access private */ final class OD_Link_Collection implements Countable { diff --git a/plugins/optimization-detective/class-od-tag-visitor-context.php b/plugins/optimization-detective/class-od-tag-visitor-context.php index 4dd1279f53..01dcde5a0a 100644 --- a/plugins/optimization-detective/class-od-tag-visitor-context.php +++ b/plugins/optimization-detective/class-od-tag-visitor-context.php @@ -15,7 +15,6 @@ * Context for tag visitors invoked for each tag while walking over a document. * * @since 0.4.0 - * @access private * * @property-read OD_URL_Metric_Group_Collection $url_metrics_group_collection Deprecated property accessed via magic getter. Use the url_metric_group_collection property instead. */ diff --git a/plugins/optimization-detective/class-od-tag-visitor-registry.php b/plugins/optimization-detective/class-od-tag-visitor-registry.php index e6b57527cd..bb16696bc8 100644 --- a/plugins/optimization-detective/class-od-tag-visitor-registry.php +++ b/plugins/optimization-detective/class-od-tag-visitor-registry.php @@ -19,7 +19,6 @@ * @implements IteratorAggregate * * @since 0.3.0 - * @access private */ final class OD_Tag_Visitor_Registry implements Countable, IteratorAggregate { diff --git a/plugins/optimization-detective/class-od-url-metric-group-collection.php b/plugins/optimization-detective/class-od-url-metric-group-collection.php index 56d257402e..7879473b90 100644 --- a/plugins/optimization-detective/class-od-url-metric-group-collection.php +++ b/plugins/optimization-detective/class-od-url-metric-group-collection.php @@ -17,7 +17,6 @@ * @implements IteratorAggregate * * @since 0.1.0 - * @access private */ final class OD_URL_Metric_Group_Collection implements Countable, IteratorAggregate, JsonSerializable { diff --git a/plugins/optimization-detective/class-od-url-metric-group.php b/plugins/optimization-detective/class-od-url-metric-group.php index 1e81641fdc..6f6a46f0fe 100644 --- a/plugins/optimization-detective/class-od-url-metric-group.php +++ b/plugins/optimization-detective/class-od-url-metric-group.php @@ -17,7 +17,6 @@ * @implements IteratorAggregate * * @since 0.1.0 - * @access private */ final class OD_URL_Metric_Group implements IteratorAggregate, Countable, JsonSerializable { diff --git a/plugins/optimization-detective/class-od-url-metric.php b/plugins/optimization-detective/class-od-url-metric.php index 0a164edc9e..fda24ac510 100644 --- a/plugins/optimization-detective/class-od-url-metric.php +++ b/plugins/optimization-detective/class-od-url-metric.php @@ -46,7 +46,6 @@ * } * * @since 0.1.0 - * @access private */ class OD_URL_Metric implements JsonSerializable { diff --git a/plugins/optimization-detective/storage/class-od-url-metric-store-request-context.php b/plugins/optimization-detective/storage/class-od-url-metric-store-request-context.php index 8d89872ab0..7181547987 100644 --- a/plugins/optimization-detective/storage/class-od-url-metric-store-request-context.php +++ b/plugins/optimization-detective/storage/class-od-url-metric-store-request-context.php @@ -15,7 +15,6 @@ * Context for when a URL Metric is successfully stored via the REST API. * * @since 0.7.0 - * @access private */ final class OD_URL_Metric_Store_Request_Context { From 32a95ab68fef18e9bda385b7de708f08ea1341ae Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Thu, 9 Jan 2025 13:06:41 -0800 Subject: [PATCH 3/8] Remove redundant private access tags --- .../optimization-detective/storage/class-od-storage-lock.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/plugins/optimization-detective/storage/class-od-storage-lock.php b/plugins/optimization-detective/storage/class-od-storage-lock.php index d9c0d85dc8..186dd9189b 100644 --- a/plugins/optimization-detective/storage/class-od-storage-lock.php +++ b/plugins/optimization-detective/storage/class-od-storage-lock.php @@ -23,7 +23,6 @@ final class OD_Storage_Lock { * Gets the TTL (in seconds) for the URL Metric storage lock. * * @since 0.1.0 - * @access private * * @return int TTL in seconds, greater than or equal to zero. A value of zero means that the storage lock should be disabled and thus that transients must not be used. */ @@ -65,7 +64,6 @@ public static function get_transient_key(): string { * seconds. Otherwise, if the current TTL is zero, then any transient is deleted. * * @since 0.1.0 - * @access private */ public static function set_lock(): void { $ttl = self::get_ttl(); @@ -81,7 +79,6 @@ public static function set_lock(): void { * Checks whether URL Metric storage is locked (for the current IP). * * @since 0.1.0 - * @access private * * @return bool Whether locked. */ From 4298d32c055fc894d47d2b0042fa60b5e620c347 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sat, 8 Feb 2025 16:13:02 -0800 Subject: [PATCH 4/8] Update od_register_tag_visitors docs for track_tag method --- .../optimization-detective/optimization.php | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/plugins/optimization-detective/optimization.php b/plugins/optimization-detective/optimization.php index 5b68f738ff..a793a67594 100644 --- a/plugins/optimization-detective/optimization.php +++ b/plugins/optimization-detective/optimization.php @@ -259,30 +259,37 @@ function od_optimize_template_output_buffer( string $buffer ): string { * 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 tag. + * - `$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 `next_tag()` in the callback (such as to walk over any child elements) since the - * cursor will be reset to the tag after the callback finishes. + * 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. * - * A tag visitor callback returns a boolean value. When it returns `true`, then Optimization Detective will mark the - * tag as needing to be included among the elements stored in URL Metrics. The element data includes properties such - * as intersectionRatio, intersectionRect, and boundingClientRect as well as whether it is the LCP element. 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, it can just return `false`. An - * element will be tracked in URL Metrics if _any_ tag visitor callback returns `true` when visiting the tag. + * 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 ): bool { + * static function ( OD_Tag_Visitor_Context $context ): void { * if ( $context->processor->get_tag() !== 'IMG' ) { - * return false; // Tag is not relevant for this tag visitor. + * return; // Tag is not relevant for this tag visitor. * } * - * // Make sure fetchpriority=high is added to LCP IMG elements. + * // 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 @@ -291,9 +298,6 @@ function od_optimize_template_output_buffer( string $buffer ): string { * ) { * $context->processor->set_attribute( 'fetchpriority', 'high' ); * } - * - * // Must return true so that the tag is included among the elements stored in URL Metrics. - * return true; * } * ); * @@ -306,17 +310,13 @@ function od_optimize_template_output_buffer( string $buffer ): string { * 'img-decoding-async', * static function ( OD_Tag_Visitor_Context $context ): bool { * if ( $context->processor->get_tag() !== 'IMG' ) { - * return false; // Tag is not relevant for this tag visitor. + * 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' ); * } - * - * // There's no need to query OD_URL_Metric_Group_Collection, so this element - * // doesn't need to be tracked in URL Metrics and the callback can return false. - * return false; * } * ); * From 9b154af6ce8661d101a057d49c073d1a98139a68 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sun, 9 Feb 2025 14:06:43 -0800 Subject: [PATCH 5/8] Update Image Prioritizer docs --- plugins/image-prioritizer/readme.txt | 2 +- plugins/optimization-detective/docs/extensions.md | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/plugins/image-prioritizer/readme.txt b/plugins/image-prioritizer/readme.txt index e7ca146c2f..e96f606069 100644 --- a/plugins/image-prioritizer/readme.txt +++ b/plugins/image-prioritizer/readme.txt @@ -15,7 +15,7 @@ This plugin optimizes the loading of images (and videos) with prioritization to The current optimizations include: -1. Add breakpoint-specific `fetchpriority=high` preload links (`LINK[rel=preload]`) for image URLs of LCP elements: +1. Add breakpoint-specific `fetchpriority=high` preload links (both as `LINK[rel=preload]` elements and `Link` response headers) for image URLs of LCP elements: 1. An `IMG` element, including the `srcset`/`sizes` attributes supplied as `imagesrcset`/`imagesizes` on the `LINK`. 2. The first `SOURCE` element with a `type` attribute in a `PICTURE` element. (Art-directed `PICTURE` elements using media queries are not supported.) 3. An element with a CSS `background-image` inline `style` attribute. diff --git a/plugins/optimization-detective/docs/extensions.md b/plugins/optimization-detective/docs/extensions.md index 25bd782cb8..f62e74d467 100644 --- a/plugins/optimization-detective/docs/extensions.md +++ b/plugins/optimization-detective/docs/extensions.md @@ -10,7 +10,7 @@ As mentioned above, this plugin is a dependency that doesn't provide features on **[Image Prioritizer](https://wordpress.org/plugins/image-prioritizer/) ([GitHub](https://github.com/WordPress/performance/tree/trunk/plugins/image-prioritizer)):** -1. Add breakpoint-specific `fetchpriority=high` preload links (`LINK[rel=preload]`) for image URLs of LCP elements: +1. Add breakpoint-specific `fetchpriority=high` preload links (both as `LINK[rel=preload]` elements and `Link` response headers) for image URLs of LCP elements: 1. An `IMG` element, including the `srcset`/`sizes` attributes supplied as `imagesrcset`/`imagesizes` on the `LINK`. ([1](https://github.com/WordPress/performance/blob/f5f50f9179c26deadeef966734367d199ba6de6f/plugins/image-prioritizer/class-image-prioritizer-img-tag-visitor.php#L167-L177), [2](https://github.com/WordPress/performance/blob/f5f50f9179c26deadeef966734367d199ba6de6f/plugins/image-prioritizer/class-image-prioritizer-img-tag-visitor.php#L304-L349)) 2. The first `SOURCE` element with a `type` attribute in a `PICTURE` element. (Art-directed `PICTURE` elements using media queries are not supported.) ([1](https://github.com/WordPress/performance/blob/f5f50f9179c26deadeef966734367d199ba6de6f/plugins/image-prioritizer/class-image-prioritizer-img-tag-visitor.php#L192-L275), [2](https://github.com/WordPress/performance/blob/f5f50f9179c26deadeef966734367d199ba6de6f/plugins/image-prioritizer/class-image-prioritizer-img-tag-visitor.php#L304-L349)) 3. An element with a CSS `background-image` inline `style` attribute. ([1](https://github.com/WordPress/performance/blob/f5f50f9179c26deadeef966734367d199ba6de6f/plugins/image-prioritizer/class-image-prioritizer-background-image-styled-tag-visitor.php#L62-L92), [2](https://github.com/WordPress/performance/blob/f5f50f9179c26deadeef966734367d199ba6de6f/plugins/image-prioritizer/class-image-prioritizer-background-image-styled-tag-visitor.php#L182-L203)) @@ -22,7 +22,9 @@ As mentioned above, this plugin is a dependency that doesn't provide features on 1. Apply lazy loading to `IMG` tags based on whether they appear in any breakpoint’s initial viewport. ([1](https://github.com/WordPress/performance/blob/f5f50f9179c26deadeef966734367d199ba6de6f/plugins/image-prioritizer/class-image-prioritizer-img-tag-visitor.php#L124-L133)) 2. Implement lazy loading of CSS background images added via inline `style` attributes. ([1](https://github.com/WordPress/performance/blob/f5f50f9179c26deadeef966734367d199ba6de6f/plugins/image-prioritizer/class-image-prioritizer-background-image-styled-tag-visitor.php#L205-L238), [2](https://github.com/WordPress/performance/blob/f5f50f9179c26deadeef966734367d199ba6de6f/plugins/image-prioritizer/helper.php#L365-L380), [3](https://github.com/WordPress/performance/blob/f5f50f9179c26deadeef966734367d199ba6de6f/plugins/image-prioritizer/lazy-load-bg-image.js)) 3. Lazy-load `VIDEO` tags by setting the appropriate attributes based on whether they appear in the initial viewport. If a `VIDEO` is the LCP element, it gets `preload=auto`; if it is in an initial viewport, the `preload=metadata` default is left; if it is not in an initial viewport, it gets `preload=none`. Lazy-loaded videos also get initial `preload`, `autoplay`, and `poster` attributes restored when the `VIDEO` is going to enter the viewport. ([1](https://github.com/WordPress/performance/blob/f5f50f9179c26deadeef966734367d199ba6de6f/plugins/image-prioritizer/class-image-prioritizer-video-tag-visitor.php#L163-L246), [2](https://github.com/WordPress/performance/blob/f5f50f9179c26deadeef966734367d199ba6de6f/plugins/image-prioritizer/helper.php#L365-L380), [3](https://github.com/WordPress/performance/blob/f5f50f9179c26deadeef966734367d199ba6de6f/plugins/image-prioritizer/lazy-load-video.js)) -5. Ensure that [`sizes=auto`](https://make.wordpress.org/core/2024/10/18/auto-sizes-for-lazy-loaded-images-in-wordpress-6-7/) is added to all lazy-loaded `IMG` elements. ([1](https://github.com/WordPress/performance/blob/f5f50f9179c26deadeef966734367d199ba6de6f/plugins/image-prioritizer/class-image-prioritizer-img-tag-visitor.php#L148-L163)) +5. Responsive image sizes: + 1. Compute the `sizes` attribute using the widths of an image collected from URL Metrics for each breakpoint (when not lazy-loaded since then handled by `sizes=auto`). ([1](https://github.com/WordPress/performance/blob/6459571471b26aee4f63f00e2ba9dfe6f5ce2f39/plugins/image-prioritizer/class-image-prioritizer-img-tag-visitor.php#L170-L184), [2](https://github.com/WordPress/performance/blob/6459571471b26aee4f63f00e2ba9dfe6f5ce2f39/plugins/image-prioritizer/class-image-prioritizer-img-tag-visitor.php#L412-L444)) + 2. Ensure [`sizes=auto`](https://make.wordpress.org/core/2024/10/18/auto-sizes-for-lazy-loaded-images-in-wordpress-6-7/) is set on `IMG` tags after setting correct lazy-loading (above). ([1](https://github.com/WordPress/performance/blob/6459571471b26aee4f63f00e2ba9dfe6f5ce2f39/plugins/image-prioritizer/class-image-prioritizer-img-tag-visitor.php#L156-L168)) 6. Reduce the size of the `poster` image of a `VIDEO` from full size to the size appropriate for the maximum width of the video (on desktop). ([1](https://github.com/WordPress/performance/blob/f5f50f9179c26deadeef966734367d199ba6de6f/plugins/image-prioritizer/class-image-prioritizer-video-tag-visitor.php#L84-L125)) **[Embed Optimizer](https://wordpress.org/plugins/embed-optimizer/) ([GitHub](https://github.com/WordPress/performance/tree/trunk/plugins/embed-optimizer)):** From 732e47298dc302e1ab0af50d8708797dededc0a8 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sun, 9 Feb 2025 14:08:10 -0800 Subject: [PATCH 6/8] Update OD plugin description --- plugins/optimization-detective/load.php | 2 +- plugins/optimization-detective/readme.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/optimization-detective/load.php b/plugins/optimization-detective/load.php index d2a2d72b29..e8cfc0ee81 100644 --- a/plugins/optimization-detective/load.php +++ b/plugins/optimization-detective/load.php @@ -2,7 +2,7 @@ /** * Plugin Name: Optimization Detective * Plugin URI: https://github.com/WordPress/performance/tree/trunk/plugins/optimization-detective - * Description: Provides an API for leveraging real user metrics to detect optimizations to apply on the frontend to improve page performance. + * Description: Provides a framework for leveraging real user metrics to detect optimizations for improving page performance. * Requires at least: 6.6 * Requires PHP: 7.2 * Version: 1.0.0-beta2 diff --git a/plugins/optimization-detective/readme.txt b/plugins/optimization-detective/readme.txt index 9c5e6d5448..e013f1c2de 100644 --- a/plugins/optimization-detective/readme.txt +++ b/plugins/optimization-detective/readme.txt @@ -7,7 +7,7 @@ License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html Tags: performance, optimization, rum -Provides an API for leveraging real user metrics to detect optimizations to apply on the frontend to improve page performance. +Provides a framework for leveraging real user metrics to detect optimizations for improving page performance. == Description == From 04b70b6175a8d678772951a1582ae59e5ac3d107 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Mon, 10 Feb 2025 16:35:14 -0800 Subject: [PATCH 7/8] Update phpdoc --- .../class-od-link-collection.php | 14 ++++++++++++++ .../class-od-tag-visitor-registry.php | 14 ++++++++++++++ .../class-od-url-metric-group-collection.php | 7 +++++++ .../class-od-url-metric-group.php | 2 ++ .../optimization-detective/class-od-url-metric.php | 7 ++++++- .../class-od-visited-tag-state.php | 2 ++ .../storage/class-od-storage-lock.php | 5 ++--- .../storage/class-od-url-metrics-post-type.php | 3 +++ .../optimization-detective/storage/rest-api.php | 4 ++++ 9 files changed, 54 insertions(+), 4 deletions(-) diff --git a/plugins/optimization-detective/class-od-link-collection.php b/plugins/optimization-detective/class-od-link-collection.php index 6a655f5fa0..cebc04e7de 100644 --- a/plugins/optimization-detective/class-od-link-collection.php +++ b/plugins/optimization-detective/class-od-link-collection.php @@ -43,6 +43,8 @@ final class OD_Link_Collection implements Countable { /** * Links grouped by rel type. * + * @since 0.4.0 + * * @var array */ private $links_by_rel = array(); @@ -50,6 +52,8 @@ final class OD_Link_Collection implements Countable { /** * Adds link. * + * @since 0.3.0 + * * @phpstan-param LinkAttributes $attributes * * @param array $attributes Attributes. @@ -110,6 +114,8 @@ public function add_link( array $attributes, ?int $minimum_viewport_width = null * When two links are identical except for their minimum/maximum widths which are also consecutive, then merge them * together. Also, add media attributes to the links. * + * @since 0.4.0 + * * @return LinkAttributes[] Prepared links with adjacent-duplicates merged together and media attributes added. */ private function get_prepared_links(): array { @@ -132,6 +138,8 @@ function ( array $links ): array { /** * Merges consecutive links. * + * @since 0.4.0 + * * @param Link[] $links Links. * @return LinkAttributes[] Merged consecutive links. */ @@ -227,6 +235,8 @@ static function ( array $link ): array { /** * Gets the HTML for the link tags. * + * @since 0.3.0 + * * @return string Link tags HTML. */ public function get_html(): string { @@ -248,6 +258,8 @@ public function get_html(): string { /** * Constructs the Link HTTP response header. * + * @since 0.4.0 + * * @return non-empty-string|null Link HTTP response header, or null if there are none. */ public function get_response_header(): ?string { @@ -301,6 +313,8 @@ static function ( $matches ) { /** * Counts the links. * + * @since 0.3.0 + * * @return non-negative-int Link count. */ public function count(): int { diff --git a/plugins/optimization-detective/class-od-tag-visitor-registry.php b/plugins/optimization-detective/class-od-tag-visitor-registry.php index 2e27e5de6d..01b10712ae 100644 --- a/plugins/optimization-detective/class-od-tag-visitor-registry.php +++ b/plugins/optimization-detective/class-od-tag-visitor-registry.php @@ -26,6 +26,8 @@ final class OD_Tag_Visitor_Registry implements Countable, IteratorAggregate { /** * Visitors. * + * @since 0.3.0 + * * @var array */ private $visitors = array(); @@ -33,6 +35,8 @@ final class OD_Tag_Visitor_Registry implements Countable, IteratorAggregate { /** * Registers a tag visitor. * + * @since 0.3.0 + * * @phpstan-param TagVisitorCallback $tag_visitor_callback * * @param non-empty-string $id Identifier for the tag visitor. @@ -45,6 +49,8 @@ public function register( string $id, callable $tag_visitor_callback ): void { /** * Determines if a visitor has been registered. * + * @since 0.3.0 + * * @param non-empty-string $id Identifier for the tag visitor. * @return bool Whether registered. */ @@ -55,6 +61,8 @@ public function is_registered( string $id ): bool { /** * Gets a registered visitor. * + * @since 0.3.0 + * * @param non-empty-string $id Identifier for the tag visitor. * @return TagVisitorCallback|null Whether registered. */ @@ -68,6 +76,8 @@ public function get_registered( string $id ): ?callable { /** * Unregisters a tag visitor. * + * @since 0.3.0 + * * @param non-empty-string $id Identifier for the tag visitor. * @return bool Whether a tag visitor was unregistered. */ @@ -82,6 +92,8 @@ public function unregister( string $id ): bool { /** * Returns an iterator for the URL Metrics in the group. * + * @since 0.3.0 + * * @return ArrayIterator ArrayIterator for tag visitors. */ public function getIterator(): ArrayIterator { @@ -91,6 +103,8 @@ public function getIterator(): ArrayIterator { /** * Counts the URL Metrics in the group. * + * @since 0.3.0 + * * @return int<0, max> URL Metric count. */ public function count(): int { diff --git a/plugins/optimization-detective/class-od-url-metric-group-collection.php b/plugins/optimization-detective/class-od-url-metric-group-collection.php index 3a061cb61f..29b5d8f8f1 100644 --- a/plugins/optimization-detective/class-od-url-metric-group-collection.php +++ b/plugins/optimization-detective/class-od-url-metric-group-collection.php @@ -30,6 +30,7 @@ final class OD_URL_Metric_Group_Collection implements Countable, IteratorAggrega * even to when there are zero breakpoints: there will still be one group * in this case, in which every single URL Metric is added. * + * @since 0.1.0 * @var OD_URL_Metric_Group[] * @phpstan-var non-empty-array */ @@ -53,6 +54,7 @@ final class OD_URL_Metric_Group_Collection implements Countable, IteratorAggrega * This array may be empty in which case there are no responsive breakpoints and all URL Metrics are collected in a * single group. * + * @since 0.1.0 * @var positive-int[] */ private $breakpoints; @@ -60,6 +62,7 @@ final class OD_URL_Metric_Group_Collection implements Countable, IteratorAggrega /** * Sample size for URL Metrics for a given breakpoint. * + * @since 0.1.0 * @var int<1, max> */ private $sample_size; @@ -69,6 +72,7 @@ final class OD_URL_Metric_Group_Collection implements Countable, IteratorAggrega * * A freshness age of zero means a URL Metric will always be considered stale. * + * @since 0.1.0 * @var int<0, max> */ private $freshness_ttl; @@ -76,6 +80,7 @@ final class OD_URL_Metric_Group_Collection implements Countable, IteratorAggrega /** * Result cache. * + * @since 0.3.0 * @var array{ * get_group_for_viewport_width?: array, * is_every_group_populated?: bool, @@ -93,6 +98,8 @@ final class OD_URL_Metric_Group_Collection implements Countable, IteratorAggrega /** * Constructor. * + * @since 0.1.0 + * * @throws InvalidArgumentException When an invalid argument is supplied. * * @phpstan-param positive-int[] $breakpoints diff --git a/plugins/optimization-detective/class-od-url-metric-group.php b/plugins/optimization-detective/class-od-url-metric-group.php index 24ca9b5fbd..d1f1201f49 100644 --- a/plugins/optimization-detective/class-od-url-metric-group.php +++ b/plugins/optimization-detective/class-od-url-metric-group.php @@ -94,6 +94,8 @@ final class OD_URL_Metric_Group implements IteratorAggregate, Countable, JsonSer * * This class should never be directly constructed. It should only be constructed by the {@see OD_URL_Metric_Group_Collection::create_groups()}. * + * @since 0.1.0 + * * @access private * @throws InvalidArgumentException If arguments are invalid. * diff --git a/plugins/optimization-detective/class-od-url-metric.php b/plugins/optimization-detective/class-od-url-metric.php index 34d63e95c1..0a687a67b5 100644 --- a/plugins/optimization-detective/class-od-url-metric.php +++ b/plugins/optimization-detective/class-od-url-metric.php @@ -66,6 +66,7 @@ class OD_URL_Metric implements JsonSerializable { /** * Data. * + * @since 0.1.0 * @var Data */ protected $data; @@ -73,6 +74,7 @@ class OD_URL_Metric implements JsonSerializable { /** * Elements. * + * @since 0.7.0 * @var OD_Element[] */ protected $elements; @@ -88,6 +90,8 @@ class OD_URL_Metric implements JsonSerializable { /** * Constructor. * + * @since 0.1.0 + * * @phpstan-param Data|array $data Valid data or invalid data (in which case an exception is thrown). * * @throws OD_Data_Validation_Exception When the input is invalid. @@ -104,6 +108,8 @@ public function __construct( array $data ) { /** * Prepares data with validation and sanitization. * + * @since 0.6.0 + * * @throws OD_Data_Validation_Exception When the input is invalid. * * @param array $data Data to validate. @@ -349,7 +355,6 @@ public static function get_json_schema(): array { * @param array $properties_schema Properties schema to extend. * @param array $additional_properties Additional properties. * @param string $filter_name Filter name used to extend. - * * @return array Extended schema. */ protected static function extend_schema_with_optional_properties( array $properties_schema, array $additional_properties, string $filter_name ): array { diff --git a/plugins/optimization-detective/class-od-visited-tag-state.php b/plugins/optimization-detective/class-od-visited-tag-state.php index 41b6460cde..62c487c2b3 100644 --- a/plugins/optimization-detective/class-od-visited-tag-state.php +++ b/plugins/optimization-detective/class-od-visited-tag-state.php @@ -30,6 +30,8 @@ final class OD_Visited_Tag_State { /** * Constructor. + * + * @since 1.0.0 */ public function __construct() { $this->reset(); diff --git a/plugins/optimization-detective/storage/class-od-storage-lock.php b/plugins/optimization-detective/storage/class-od-storage-lock.php index 8f3ed4f2ac..a75124a486 100644 --- a/plugins/optimization-detective/storage/class-od-storage-lock.php +++ b/plugins/optimization-detective/storage/class-od-storage-lock.php @@ -24,7 +24,6 @@ final class OD_Storage_Lock { * Capability for being able to store a URL Metric now. * * @since n.e.x.t - * @access private * @var string */ const STORE_URL_METRIC_NOW_CAPABILITY = 'od_store_url_metric_now'; @@ -33,7 +32,6 @@ final class OD_Storage_Lock { * Adds hooks. * * @since n.e.x.t - * @access private */ public static function add_hooks(): void { add_filter( 'user_has_cap', array( __CLASS__, 'filter_user_has_cap' ) ); @@ -43,7 +41,6 @@ public static function add_hooks(): void { * Filters `user_has_cap` to grant the `od_store_url_metric_now` capability to users who can `manage_options` by default. * * @since n.e.x.t - * @access private * * @param array|mixed $allcaps Capability names mapped to boolean values for whether the user has that capability. * @return array Capability names mapped to boolean values for whether the user has that capability. @@ -94,6 +91,8 @@ public static function get_ttl(): int { /** * Gets transient key for locking URL Metric storage (for the current IP). * + * @since 0.1.0 + * * @todo Should the URL be included in the key? Or should a user only be allowed to store one metric? * @return non-empty-string Transient key. */ diff --git a/plugins/optimization-detective/storage/class-od-url-metrics-post-type.php b/plugins/optimization-detective/storage/class-od-url-metrics-post-type.php index 264d08b3ad..fb724e8b2f 100644 --- a/plugins/optimization-detective/storage/class-od-url-metrics-post-type.php +++ b/plugins/optimization-detective/storage/class-od-url-metrics-post-type.php @@ -23,6 +23,7 @@ class OD_URL_Metrics_Post_Type { /** * Post type slug. * + * @since 0.1.0 * @var string */ const SLUG = 'od_url_metrics'; @@ -30,6 +31,7 @@ class OD_URL_Metrics_Post_Type { /** * Event name (hook) for garbage collection of stale URL Metrics posts. * + * @since 0.1.0 * @var string */ const GC_CRON_EVENT_NAME = 'od_url_metrics_gc'; @@ -37,6 +39,7 @@ class OD_URL_Metrics_Post_Type { /** * Recurrence for garbage collection of stale URL Metrics posts. * + * @since 0.1.0 * @var string */ const GC_CRON_RECURRENCE = 'daily'; diff --git a/plugins/optimization-detective/storage/rest-api.php b/plugins/optimization-detective/storage/rest-api.php index 8f4c390a8e..8ccacfd5fa 100644 --- a/plugins/optimization-detective/storage/rest-api.php +++ b/plugins/optimization-detective/storage/rest-api.php @@ -15,6 +15,8 @@ /** * Namespace for optimization-detective. * + * @since 0.1.0 + * @access private * @var string */ const OD_REST_API_NAMESPACE = 'optimization-detective/v1'; @@ -26,6 +28,8 @@ * that does not strictly follow the standard usage. Namely, submitting a POST request to this endpoint will either * create a new `od_url_metrics` post, or it will update an existing post if one already exists for the provided slug. * + * @since 0.1.0 + * @access private * @link https://google.aip.dev/136 * @var string */ From 3f62502483d3ad65d4234867f50ac0f5ddcf33b1 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Mon, 10 Feb 2025 17:13:38 -0800 Subject: [PATCH 8/8] Remove obsolete todos --- .../optimization-detective/storage/class-od-storage-lock.php | 1 - plugins/optimization-detective/storage/data.php | 2 -- 2 files changed, 3 deletions(-) diff --git a/plugins/optimization-detective/storage/class-od-storage-lock.php b/plugins/optimization-detective/storage/class-od-storage-lock.php index a75124a486..4a91e272dc 100644 --- a/plugins/optimization-detective/storage/class-od-storage-lock.php +++ b/plugins/optimization-detective/storage/class-od-storage-lock.php @@ -93,7 +93,6 @@ public static function get_ttl(): int { * * @since 0.1.0 * - * @todo Should the URL be included in the key? Or should a user only be allowed to store one metric? * @return non-empty-string Transient key. */ public static function get_transient_key(): string { diff --git a/plugins/optimization-detective/storage/data.php b/plugins/optimization-detective/storage/data.php index 05bd90157e..fb67b6ae91 100644 --- a/plugins/optimization-detective/storage/data.php +++ b/plugins/optimization-detective/storage/data.php @@ -58,8 +58,6 @@ function od_get_url_metric_freshness_ttl(): int { * * This is used as a cache key for stored URL Metrics. * - * TODO: For non-singular requests, consider adding the post IDs from The Loop to ensure publishing a new post will invalidate the cache. - * * @since 0.1.0 * @access private *