Skip to content

Commit 8a4987d

Browse files
authored
Merge pull request #1979 from ShyamGadde/update/od-meta-generator-tag
Enhance Optimization Detective meta generator tag with all disabled reasons
2 parents cba9665 + 804d8ac commit 8a4987d

File tree

5 files changed

+240
-83
lines changed

5 files changed

+240
-83
lines changed

plugins/optimization-detective/docs/hooks.md

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
Fires when the Optimization Detective is initializing.
1010

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

1414
Example:
1515

@@ -115,7 +115,7 @@ It is important to note that this action fires _after_ the entire template has b
115115
other words, it will fire after the `wp_footer` action.
116116

117117
This action runs before any of the registered tag visitors have been invoked in the current response. It is useful for
118-
an extension to gather the required information from the currently-stored URL Metrics for tag visitors to later leverage.
118+
an extension to gather the required information from the currently stored URL Metrics for tag visitors to later leverage.
119119
See [example](https://github.com/WordPress/performance/pull/1921) from the Image Prioritizer plugin where it can be used
120120
to determine what the common external LCP background-image is for each viewport group up front so that this doesn't have
121121
to be computed when a tag visitor is invoked.
@@ -187,32 +187,46 @@ The additional attribution data is made available to client-side extension scrip
187187
Filters the breakpoint max widths to group URL Metrics for various viewports.
188188

189189
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).
190-
This array may be empty in which case there are no responsive breakpoints and all URL Metrics are collected in a single group.
190+
This array may be empty, in which case there are no responsive breakpoints, and all URL Metrics are collected in a single group.
191191
A breakpoint must be greater than zero or else a usage warning will occur.
192192

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

195195
### Filter: `od_can_optimize_response` (default: boolean condition, see below)
196196

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

199199
1. It’s not a search template (`is_search()`).
200200
2. It’s not a post embed template (`is_embed()`).
201-
3. It’s not the Customizer preview (`is_customize_preview()`)
202-
4. It’s not the response to a `POST` request.
203-
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.
201+
3. It’s not a preview (`is_preview()`).
202+
4. It’s not the Customizer preview (`is_customize_preview()`).
203+
5. It’s not the response to a `POST` request.
204+
6. 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.
204205

205-
To force every response to be optimized regardless of the conditions above, you can do:
206+
The filter now receives an additional `$disabled_flags` parameter that contains information about which specific conditions are preventing optimization. This allows for more granular control.
207+
208+
For example, to enable optimization specifically for search pages:
206209

207210
```php
208-
add_filter( 'od_can_optimize_response', '__return_true' );
211+
add_filter( 'od_can_optimize_response', function( $can_optimize, array $disabled_flags ): bool {
212+
if ( ! $can_optimize && $disabled_flags['is_search'] ) {
213+
unset( $disabled_flags['is_search'] );
214+
$can_optimize = count( array_filter( $disabled_flags ) ) === 0;
215+
}
216+
return $can_optimize;
217+
}, 10, 2 );
209218
```
210219

220+
Note that this filter cannot override some conditions. Even if the filter returns `true`, optimization will still be disabled when:
221+
222+
1. The REST API for storing URL Metrics is not available.
223+
2. The URL has the `optimization_detective_disabled` query parameter.
224+
211225
### Filter: `od_url_metrics_breakpoint_sample_size` (default: 3)
212226

213227
Filters the sample size for a breakpoint's URL Metrics on a given URL.
214228

215-
The filtered value must be greater than zero; otherwise it will be ignored and a usage warning will result.
229+
The filtered value must be greater than zero; otherwise it will be ignored, and a usage warning will result.
216230

217231
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:
218232

@@ -226,7 +240,7 @@ add_filter( 'od_url_metrics_breakpoint_sample_size', function (): int {
226240

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

229-
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:
243+
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:
230244

231245
```php
232246
add_filter( 'od_metrics_storage_lock_ttl', function ( int $ttl ): int {
@@ -236,7 +250,7 @@ add_filter( 'od_metrics_storage_lock_ttl', function ( int $ttl ): int {
236250

237251
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.
238252

239-
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:
253+
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:
240254

241255
```php
242256
add_filter( 'od_metrics_storage_lock_ttl', function ( int $ttl ): int {
@@ -274,7 +288,7 @@ add_filter( 'od_url_metric_freshness_ttl', '__return_zero' );
274288

275289
Filters the minimum allowed viewport aspect ratio for URL Metrics.
276290

277-
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:
291+
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:
278292

279293
```php
280294
add_filter( 'od_minimum_viewport_aspect_ratio', static function (): int {
@@ -298,13 +312,15 @@ add_filter( 'od_maximum_viewport_aspect_ratio', static function (): int {
298312

299313
### Filter: `od_template_output_buffer` (default: the HTML response)
300314

301-
Filters the template output buffer prior to sending to the client. This filter is added to implement [\#43258](https://core.trac.wordpress.org/ticket/43258) in WordPress core.
315+
Filters the template output buffer before sending it to the client.
316+
317+
This filter is added to implement [\#43258](https://core.trac.wordpress.org/ticket/43258) in WordPress core.
302318

303319
### Filter: `od_url_metric_schema_element_item_additional_properties` (default: empty array)
304320

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

307-
For example to add a `resizedBoundingClientRect` property:
323+
For example, to add a `resizedBoundingClientRect` property:
308324

309325
```php
310326
<?php
@@ -396,7 +412,7 @@ add_filter( 'od_url_metric_garbage_collection_ttl', '__return_zero' );
396412

397413
Filters the maximum allowed size in bytes for a URL Metric serialized to JSON.
398414

399-
The filtered value must be greater than zero; otherwise it will be ignored and a usage warning will result.
415+
The filtered value must be greater than zero; otherwise it will be ignored, and a usage warning will result.
400416

401417
### Filter: `od_gzip_url_metric_store_request_payloads` (default: `true` if the `gzdecode()` function exists)
402418

plugins/optimization-detective/helper.php

Lines changed: 122 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,122 @@ function od_generate_media_query( ?int $minimum_viewport_width, ?int $maximum_vi
6161
}
6262

6363
/**
64-
* Displays the HTML generator meta tag for the Optimization Detective plugin.
64+
* Gets the reasons why Optimization Detective is disabled for the current response.
65+
*
66+
* @since n.e.x.t
67+
* @access private
68+
*
69+
* @return array{
70+
* is_search?: string,
71+
* is_embed?: string,
72+
* is_preview?: string,
73+
* is_customize_preview?: string,
74+
* non_get_request?: string,
75+
* no_cache_purge_post_id?: string,
76+
* filter_disabled?: string,
77+
* rest_api_unavailable?: string,
78+
* query_param_disabled?: string
79+
* } Array of disabled reason codes and their messages.
80+
*/
81+
function od_get_disabled_reasons(): array {
82+
$disabled_flags = array(
83+
'is_search' => false,
84+
'is_embed' => false,
85+
'is_preview' => false,
86+
'is_customize_preview' => false,
87+
'non_get_request' => false,
88+
'no_cache_purge_post_id' => false,
89+
);
90+
91+
// Disable the search template since there is no predictability in whether posts in the loop will have featured images assigned or not. If a
92+
// theme template for search results doesn't even show featured images, then this wouldn't be an issue.
93+
if ( is_search() ) {
94+
$disabled_flags['is_search'] = true;
95+
}
96+
97+
// Avoid optimizing embed responses because the Post Embed iframes include a sandbox attribute with the value of
98+
// "allow-scripts" but without "allow-same-origin". This can result in an error in the console:
99+
// > Access to script at '.../detect.js?ver=0.4.1' from origin 'null' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
100+
// So it's better to just avoid attempting to optimize Post Embed responses (which don't need optimization anyway).
101+
if ( is_embed() ) {
102+
$disabled_flags['is_embed'] = true;
103+
}
104+
105+
// Skip posts that aren't published yet.
106+
if ( is_preview() ) {
107+
$disabled_flags['is_preview'] = true;
108+
}
109+
110+
// Disable in Customizer preview since injection of inline-editing controls can interfere with XPath. Optimization is also not necessary in this context.
111+
if ( is_customize_preview() ) {
112+
$disabled_flags['is_customize_preview'] = true;
113+
}
114+
115+
// Disable for POST responses since they cannot, by definition, be cached.
116+
if ( isset( $_SERVER['REQUEST_METHOD'] ) && 'GET' !== $_SERVER['REQUEST_METHOD'] ) {
117+
$disabled_flags['non_get_request'] = true;
118+
}
119+
120+
// Disable when there is no post ID available for cache purging. Page caching plugins can only reliably be told to invalidate a cached page when a post is available to trigger
121+
// the relevant actions on.
122+
if ( null === od_get_cache_purge_post_id() ) {
123+
$disabled_flags['no_cache_purge_post_id'] = true;
124+
}
125+
126+
// Check if any flags are set to true.
127+
$has_disabled_flags = count( array_filter( $disabled_flags ) ) > 0;
128+
129+
/**
130+
* Filters whether the current response can be optimized.
131+
*
132+
* @since 0.1.0
133+
* @since n.e.x.t Added $disabled_flags parameter
134+
* @link https://github.com/WordPress/performance/blob/trunk/plugins/optimization-detective/docs/hooks.md#:~:text=Filter%3A%20od_can_optimize_response
135+
*
136+
* @param bool $can_optimize Whether response can be optimized.
137+
* @param array{
138+
* is_search: bool,
139+
* is_embed: bool,
140+
* is_preview: bool,
141+
* is_customize_preview: bool,
142+
* non_get_request: bool,
143+
* no_cache_purge_post_id: bool
144+
* } $disabled_flags Flags indicating which conditions are disabling optimization.
145+
*/
146+
$can_optimize = (bool) apply_filters( 'od_can_optimize_response', ! $has_disabled_flags, $disabled_flags );
147+
148+
$reasons = array();
149+
if ( ! $can_optimize ) {
150+
$reason_messages = array(
151+
'is_search' => __( 'Page is not optimized because it is a search results page.', 'optimization-detective' ),
152+
'is_embed' => __( 'Page is not optimized because it is an embed.', 'optimization-detective' ),
153+
'is_preview' => __( 'Page is not optimized because it is a preview.', 'optimization-detective' ),
154+
'is_customize_preview' => __( 'Page is not optimized because it is a customize preview.', 'optimization-detective' ),
155+
'non_get_request' => __( 'Page is not optimized because it is not a GET request.', 'optimization-detective' ),
156+
'no_cache_purge_post_id' => __( 'Page is not optimized because there is no post ID available for cache purging.', 'optimization-detective' ),
157+
);
158+
159+
$reasons = wp_array_slice_assoc( $reason_messages, array_keys( array_filter( $disabled_flags ) ) );
160+
161+
// If no technical reasons but optimization still disabled, it's because of the filter.
162+
if ( 0 === count( $reasons ) ) {
163+
$reasons['filter_disabled'] = __( 'Page is not optimized because the od_can_optimize_response filter returned false.', 'optimization-detective' );
164+
}
165+
}
166+
167+
if ( od_is_rest_api_unavailable() && ! ( wp_get_environment_type() === 'local' && ! function_exists( 'tests_add_filter' ) ) ) {
168+
$reasons['rest_api_unavailable'] = __( 'Page is not optimized because the REST API for storing URL Metrics is not available.', 'optimization-detective' );
169+
}
170+
171+
if ( isset( $_GET['optimization_detective_disabled'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
172+
$reasons['query_param_disabled'] = __( 'Page is not optimized because the URL has the optimization_detective_disabled query parameter.', 'optimization-detective' );
173+
}
174+
175+
return $reasons;
176+
}
177+
178+
/**
179+
* Displays the HTML generator META tag for the Optimization Detective plugin.
65180
*
66181
* See {@see 'wp_head'}.
67182
*
@@ -72,9 +187,11 @@ function od_render_generator_meta_tag(): void {
72187
// Use the plugin slug as it is immutable.
73188
$content = 'optimization-detective ' . OPTIMIZATION_DETECTIVE_VERSION;
74189

75-
// Indicate that the plugin will not be doing anything because the REST API is unavailable.
76-
if ( od_is_rest_api_unavailable() ) {
77-
$content .= '; rest_api_unavailable';
190+
// Add any reasons why Optimization Detective is disabled.
191+
$disabled_reasons = od_get_disabled_reasons();
192+
if ( count( $disabled_reasons ) > 0 ) {
193+
$flags = array_keys( $disabled_reasons );
194+
$content .= '; ' . implode( '; ', $flags );
78195
}
79196

80197
echo '<meta name="generator" content="' . esc_attr( $content ) . '">' . "\n";
@@ -86,7 +203,7 @@ function od_render_generator_meta_tag(): void {
86203
* @since 0.9.0
87204
* @access private
88205
*
89-
* @param string $src_path Source path, relative to plugin root.
206+
* @param string $src_path Source path, relative to the plugin root.
90207
* @param string|null $min_path Minified path. If not supplied, then '.min' is injected before the file extension in the source path.
91208
* @return string URL to script or stylesheet.
92209
*

0 commit comments

Comments
 (0)