Skip to content
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 OD_Element class and improve PHP API #1585

Merged
merged 21 commits into from
Oct 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
7c96ba0
Add OD_Element class
westonruter Oct 9, 2024
c3e55f9
Change get_all_denormalized_elements into get_all_elements
westonruter Oct 9, 2024
57964fb
Implement getter methods
westonruter Oct 16, 2024
5dfcb07
Ensure get('elements') and get_elements() return the same cached elem…
westonruter Oct 16, 2024
9213455
Fix remaining XPath indices to be 1-based for #1191
westonruter Oct 16, 2024
b5c9703
Add tests for OD_Element
westonruter Oct 16, 2024
f2c2d31
Add todos for future improved typing
westonruter Oct 16, 2024
9c7b154
Reuse helper methods
westonruter Oct 16, 2024
20f4ccb
Improve exception messages
westonruter Oct 17, 2024
c5c3884
Use instance check rather than null check
westonruter Oct 17, 2024
8992957
Add missing since tags
westonruter Oct 17, 2024
a99208b
Add typing for resizedBoundingClientRect
westonruter Oct 17, 2024
344d6e1
Add getter/setter methods
westonruter Oct 17, 2024
7cfdcac
Remove readonly tag since member is now protected
westonruter Oct 17, 2024
98d3fd8
Rename get_all_elements to get_xpath_elements_map
westonruter Oct 17, 2024
904c641
Reuse is_viewport_width_in_range method
westonruter Oct 17, 2024
feed71b
Merge branch 'trunk' of https://github.com/WordPress/performance into…
westonruter Oct 17, 2024
13c7d53
Work around PHPStan limitation
westonruter Oct 17, 2024
26637ed
Merge branch 'trunk' into add/od-element-class
westonruter Oct 18, 2024
61f53df
Merge branch 'trunk' of https://github.com/WordPress/performance into…
westonruter Oct 18, 2024
a070c01
Use get_xpath instead of offset
westonruter Oct 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 17 additions & 5 deletions plugins/embed-optimizer/class-embed-optimizer-tag-visitor.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
/**
* Tag visitor that optimizes embeds.
*
* @phpstan-import-type DOMRect from OD_URL_Metric
*
* @since 0.2.0
* @access private
*/
Expand Down Expand Up @@ -221,21 +223,31 @@ private function reduce_layout_shifts( OD_Tag_Visitor_Context $context ): void {
*/
$minimums = array();

$denormalized_elements = $context->url_metric_group_collection->get_all_denormalized_elements()[ $embed_wrapper_xpath ] ?? array();
foreach ( $denormalized_elements as list( $group, $url_metric, $element ) ) {
if ( ! isset( $element['resizedBoundingClientRect'] ) ) {
$elements = $context->url_metric_group_collection->get_xpath_elements_map()[ $embed_wrapper_xpath ] ?? array();
foreach ( $elements as $element ) {
/**
* Resized bounding client rect.
*
* @var DOMRect|null $resized_bounding_client_rect
*/
$resized_bounding_client_rect = $element->get( 'resizedBoundingClientRect' );
if ( ! is_array( $resized_bounding_client_rect ) ) {
continue;
}
$group = $element->get_url_metric_group();
if ( null === $group ) {
continue; // Technically could be null but in practice it never will be.
}
$group_min_width = $group->get_minimum_viewport_width();
if ( ! isset( $minimums[ $group_min_width ] ) ) {
$minimums[ $group_min_width ] = array(
'group' => $group,
'height' => $element['resizedBoundingClientRect']['height'],
'height' => $resized_bounding_client_rect['height'],
);
} else {
$minimums[ $group_min_width ]['height'] = min(
$minimums[ $group_min_width ]['height'],
$element['resizedBoundingClientRect']['height']
$resized_bounding_client_rect['height']
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public function __invoke( OD_Tag_Visitor_Context $context ): bool {
* will also be added. Additionally, ensure that this common LCP element is never lazy-loaded.
*/
$common_lcp_element = $context->url_metric_group_collection->get_common_lcp_element();
if ( ! is_null( $common_lcp_element ) && $xpath === $common_lcp_element['xpath'] ) {
if ( $common_lcp_element instanceof OD_Element && $xpath === $common_lcp_element->get_xpath() ) {
$updated_fetchpriority = 'high';
} elseif (
'high' === $current_fetchpriority
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,8 @@ private function reduce_poster_image_size( string $poster, OD_Tag_Visitor_Contex
$max_element_width = 0;
foreach ( $context->url_metric_group_collection->get_last_group() as $url_metric ) {
foreach ( $url_metric->get_elements() as $element ) {
if ( $element['xpath'] === $xpath ) {
$max_element_width = max( $max_element_width, $element['boundingClientRect']['width'] );
if ( $element->get_xpath() === $xpath ) {
$max_element_width = max( $max_element_width, $element->get_bounding_client_rect()['width'] );
break; // Move on to the next URL Metric.
}
}
Expand Down Expand Up @@ -210,7 +210,7 @@ private function lazy_load_videos( ?string $poster, OD_Tag_Visitor_Context $cont
* TODO: The above paragraph.
*/
$common_lcp_element = $context->url_metric_group_collection->get_common_lcp_element();
if ( null !== $common_lcp_element && $xpath === $common_lcp_element['xpath'] ) {
if ( $common_lcp_element instanceof OD_Element && $xpath === $common_lcp_element->get_xpath() ) {
if ( 'auto' !== $initial_preload ) {
$processor->set_attribute( 'preload', 'auto' );
}
Expand Down
233 changes: 233 additions & 0 deletions plugins/optimization-detective/class-od-element.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
<?php
/**
* Optimization Detective: OD_Element class
*
* @package optimization-detective
* @since n.e.x.t
*/

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}

/**
* Data for a single element in a URL metric.
*
* @phpstan-import-type ElementData from OD_URL_Metric
* @phpstan-import-type DOMRect from OD_URL_Metric
* @implements ArrayAccess<key-of<ElementData>, ElementData[key-of<ElementData>]>
* @todo The above implements tag should account for additional undefined keys which can be supplied by extending the element schema. May depend on <https://github.com/phpstan/phpstan/issues/8438>.
*
* @since n.e.x.t
* @access private
*/
class OD_Element implements ArrayAccess, JsonSerializable {

/**
* Data.
*
* @since n.e.x.t
* @var ElementData
*/
protected $data;

/**
* URL metric that this element belongs to.
*
* @since n.e.x.t
* @var OD_URL_Metric
*/
protected $url_metric;

/**
* Constructor.
*
* @since n.e.x.t
*
* @phpstan-param ElementData $data
*
* @param array<string, mixed> $data Element data.
* @param OD_URL_Metric $url_metric URL metric.
*/
public function __construct( array $data, OD_URL_Metric $url_metric ) {
$this->data = $data;
$this->url_metric = $url_metric;
}

/**
* Gets the URL metric that this element belongs to.
*
* @since n.e.x.t
*
* @return OD_URL_Metric URL Metric.
*/
public function get_url_metric(): OD_URL_Metric {
return $this->url_metric;
}

/**
* Gets the group that this element's URL metric is a part of (which may not be any).
*
* @since n.e.x.t
*
* @return OD_URL_Metric_Group|null Group.
*/
public function get_url_metric_group(): ?OD_URL_Metric_Group {
return $this->url_metric->get_group();
}

/**
* Gets property value for an arbitrary key.
*
* This is particularly useful in conjunction with the `od_url_metric_schema_element_item_additional_properties` filter.
*
* @since n.e.x.t
*
* @param string $key Property.
* @return mixed|null The property value, or null if not set.
*/
public function get( string $key ) {
return $this->data[ $key ] ?? null;
}

/**
* Determines whether element was detected as LCP.
*
* @since n.e.x.t
*
* @return bool Whether LCP.
*/
public function is_lcp(): bool {
return $this->data['isLCP'];
}

/**
* Determines whether element was detected as an LCP candidate.
*
* @since n.e.x.t
*
* @return bool Whether LCP candidate.
*/
public function is_lcp_candidate(): bool {
return $this->data['isLCPCandidate'];
}

/**
* Gets XPath for element.
*
* @since n.e.x.t
*
* @return non-empty-string XPath.
*/
public function get_xpath(): string {
return $this->data['xpath'];
}

/**
* Gets intersectionRatio for element.
*
* @since n.e.x.t
*
* @return float Intersection ratio.
*/
public function get_intersection_ratio(): float {
return $this->data['intersectionRatio'];
}

/**
* Gets intersectionRect for element.
*
* @since n.e.x.t
*
* @phpstan-return DOMRect
*
* @return array Intersection rect.
*/
public function get_intersection_rect(): array {
return $this->data['intersectionRect'];
}

/**
* Gets boundingClientRect for element.
*
* @since n.e.x.t
*
* @phpstan-return DOMRect
*
* @return array Bounding client rect.
*/
public function get_bounding_client_rect(): array {
return $this->data['boundingClientRect'];
}

/**
* Checks whether an offset exists.
*
* @since n.e.x.t
*
* @param mixed $offset Key.
* @return bool Whether the offset exists.
*/
public function offsetExists( $offset ): bool {
return isset( $this->data[ $offset ] );
}

/**
* Retrieves an offset.
*
* @since n.e.x.t
*
* @template T of key-of<ElementData>
* @phpstan-param T $offset
* @phpstan-return ElementData[T]|null
* @todo This should account for additional undefined keys which can be supplied by extending the element schema. May depend on <https://github.com/phpstan/phpstan/issues/8438>.
*
* @param mixed $offset Key.
* @return mixed May return any value from ElementData including possible extensions.
*/
public function offsetGet( $offset ) {
return $this->data[ $offset ] ?? null;
}

/**
* Sets an offset.
*
* This is disallowed. Attempting to set a property will throw an error.
*
* @since n.e.x.t
*
* @param mixed $offset Key.
* @param mixed $value Value.
*
* @throws Exception When attempting to set a property.
*/
public function offsetSet( $offset, $value ): void {
throw new Exception( 'Element data may not be set.' );
}

/**
* Offset to unset.
*
* This is disallowed. Attempting to unset a property will throw an error.
*
* @since n.e.x.t
*
* @param mixed $offset Offset.
*
* @throws Exception When attempting to unset a property.
*/
public function offsetUnset( $offset ): void {
throw new Exception( 'Element data may not be unset.' );
}

/**
* Specifies data which should be serialized to JSON.
*
* @since n.e.x.t
* @return ElementData Exports to be serialized by json_encode().
*/
public function jsonSerialize(): array {
return $this->data;
}
}
Loading