New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Introduce ILO_URL_Metric to encapsulate core data structure #988
Introduce ILO_URL_Metric to encapsulate core data structure #988
Conversation
modules/images/image-loading-optimization/class-ilo-url-metric.php
Outdated
Show resolved
Hide resolved
$this->assertArrayHasKey( 'timestamp', $url_metrics[0] ); | ||
$this->assertIsFloat( $url_metrics[0]['timestamp'] ); | ||
$this->assertLessThanOrEqual( microtime( true ), $url_metrics[0]['timestamp'] ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Irrelevant to test now that the timestamp
is required at the time of instantiation of ILO_URL_Metric
. Previously it would get added by ilo_unshift_url_metrics()
.
Same goes with the deleted assertions below.
if ( ! isset( $a['timestamp'] ) || ! isset( $b['timestamp'] ) ) { | ||
return 0; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for this now that the timestamp is guaranteed to be set.
*/ | ||
function ilo_unshift_url_metrics( array $url_metrics, array $validated_url_metric, array $breakpoints, int $sample_size ): array { | ||
$validated_url_metric['timestamp'] = microtime( true ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is now set more naturally in ilo_handle_rest_request()
if ( ! isset( $url_metric['viewport']['width'] ) ) { | ||
continue; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for this anymore since the property is guaranteed to exist.
} | ||
|
||
return array_values( | ||
array_filter( | ||
$url_metrics, | ||
static function ( $url_metric ) use ( $trigger_error ) { | ||
// TODO: If we wanted, we could use the JSON Schema to validate the stored metrics. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This TODO is now done
return true; | ||
}, | ||
), | ||
'viewport' => array( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Schema definition is moved to ILO_URL_Metrics
.
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.
To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
} | ||
|
||
try { | ||
// TODO: This is re-validating the data which has been stored in the post type. This ensures it remains valid, but is it overkill? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
feels fine to me, defensive coding
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My only hesitation is that this additional validation adds latency, so it negatively impacts TTFB. However, I haven't profiled the JSON Schema validation logic, so this may not be a big deal.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, left one comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@westonruter This mostly looks great. Just a few small points of feedback.
modules/images/image-loading-optimization/class-ilo-url-metric.php
Outdated
Show resolved
Hide resolved
modules/images/image-loading-optimization/storage/post-type.php
Outdated
Show resolved
Hide resolved
modules/images/image-loading-optimization/class-ilo-url-metric.php
Outdated
Show resolved
Hide resolved
Co-authored-by: Felix Arntz <felixarntz@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@westonruter A few additional thoughts for consideration.
final class ILO_URL_Metric implements JsonSerializable { | ||
|
||
/** | ||
* Data. | ||
* | ||
* @var array{ | ||
* timestamp: int, | ||
* viewport: array{ width: int, height: int }, | ||
* elements: array<array{ | ||
* isLCP: bool, | ||
* isLCPCandidate: bool, | ||
* xpath: string, | ||
* intersectionRatio: float, | ||
* intersectionRect: array{ width: int, height: int }, | ||
* boundingClientRect: array{ width: int, height: int }, | ||
* }> | ||
* } | ||
*/ | ||
private $data; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't it be valuable to store the $url
and $slug
in this class as well? Maybe they could be separate properties from $data
, but I find it a bit odd to have a class for a URL metric exclude those identifying variables.
I think it could simplify some things by putting the responsibility for this information purely into the class representing the entity. We could still use associative arrays keyed by URL or slug if we need it, but I think it would be cleaner to have it covered by this class in its entirety - unless I'm missing something.
Alternatively, if there are scenarios where URL and slug should be decoupled from the actual metric data, maybe we could separately introduce a class that is a value object representing URL and slug, and it could also provide the methods to create and validate the nonces etc. Just an idea.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, the thing is that there are multiple URL metrics stored for a given response. The post type stores this list of URL metrics, based on the number of breakpoints and the sample size. So if we store the URL and slug with each ILO_URL_Metric
we're storing multiple copies of the same data. The URL/slug is more higher level metadata which is only needed for the post type. We don't need the URL or slug except to get/set the post. I'd prefer to not include the URL or slug in this class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, let me think about this some more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we merge this as-is and I propose the change in another PR? I want to revisit how the URL is handled because in reality it is the request args really that matter, not the end URL. The slug is computed from the request args, not the URL.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added this as a todo item on #869
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ultimately, I think it would actually make sense to store a url
with each URL Metric because in actuality the URL can vary while the request args change. This could be important for debugging, for example if it turns out that a template is modified by direct access to $_GET
and not for a registered query var.
But I want to address this in a subsequent PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh I see you added more comments in the meantime. Let me know what you think about the above proposal.
But in any case, this would be okay to do in a follow up PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, let's do in a separate PR. It would actually be useful to duplicate the URL and even the request args for each URL metric since it reveals the underlying data for the response that created the URL metric. The slug
used in the post type is computed from a normalized set of request args, so having the original request args would still be useful to have on hand.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds great!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See #1043
* @return int|WP_Error Post ID or WP_Error otherwise. | ||
*/ | ||
function ilo_store_url_metric( string $url, string $slug, array $validated_url_metric ) { | ||
function ilo_store_url_metric( string $url, string $slug, ILO_URL_Metric $new_url_metric ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See my above comment: I think a function like this could be made more intuitive if ILO_URL_Metric
covered the URL and slug as well. This way you'd only pass around the object, not the other two values in addition.
'url' => array( | ||
'type' => 'string', | ||
'description' => __( 'The URL for which the metric was obtained.', 'performance-lab' ), | ||
'required' => true, | ||
'format' => 'uri', | ||
'validate_callback' => static function ( $url ) { | ||
if ( ! wp_validate_redirect( $url ) ) { | ||
return new WP_Error( 'non_origin_url', __( 'URL for another site provided.', 'performance-lab' ) ); | ||
} | ||
// TODO: This is not validated as corresponding to the slug in any way. True it is not used for anything but metadata. | ||
return true; | ||
}, | ||
), | ||
'slug' => array( | ||
'type' => 'string', | ||
'description' => __( 'An MD5 hash of the query args.', 'performance-lab' ), | ||
'required' => true, | ||
'pattern' => '^[0-9a-f]{32}$', | ||
// This is validated via the nonce validate_callback, as it is provided as input to create the nonce by the server | ||
// which then is verified to match in the REST API request. | ||
), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See above, this could be tied to the ILO_URL_Metric
class.
47a1d0a
into
feature/image-loading-optimization
Summary
This implements these aspects of #933:
Subsequent points will be addressed in follow-up PRs.
Relevant technical choices
ILO_URL_Metric
is used instead of passing around an array of the same data.ILO_URL_Metric
class contains the validation logic to ensure that the data is as expected.ILO_URL_Metric
class to validate requests.ILO_URL_Metric
class now requires that atimestamp
be provided at construction time, which removes this concern fromilo_unshift_url_metrics()
.ilo_url_metrics
post type. This ensures that if the data format changes, any prior entries will just be omitted.Checklist
[Focus]
orInfrastructure
label.[Type]
label.no milestone
label.