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_Tag_Visitor_Context::track_tag() method as alternative for returning true in tag visitor callback #1821

Merged
merged 2 commits into from
Jan 24, 2025

Conversation

westonruter
Copy link
Member

@westonruter westonruter commented Jan 23, 2025

Something that has bugged me for awhile with how tag visitor callbacks work is the aspect of returning a boolean for whether or not the currently-visited tag should be tracked in the elements of the stored URL Metrics. For simple cases, the boolean works fine but it can get hard to track when the callback returns in multiple places. If you have to short-circuit anywhere, then you have to remember which return value is appropriate at a given time.

The other problem with returning a boolean is having to remember what the boolean means. This was difficult to explain when reviews were performed when developing Optimization Detective, and it never totally felt right. Additionally, when developing a tag visitor which doesn't actually need URL Metrics to perform its optimizations, it is confusing that in this case there is no need to return true since there is no need to refer to the URL Metrics to apply the optimization. For example:

function add_decoding_async_to_img_tags( OD_Tag_Visitor_Context $context ): bool {
	if ( $context->processor->get_tag() !== 'IMG' ) {
		return false;
	}
	$context->processor->set_attribute( 'decoding', 'async' );
	return false;
}

So this PR retains the ability to return a boolean, but it also introduces a new track_tag method exposed on the supplied OD_Tag_Visitor_Context object which is equivalent to return true. Having a method makes the decision to opt-in to tracking much more explicit. This simplifies the above to:

function add_decoding_async_to_img_tags( OD_Tag_Visitor_Context $context ): void {
	if ( $context->processor->get_tag() !== 'IMG' ) {
		return;
	}
	$context->processor->set_attribute( 'decoding', 'async' );
}

And these are equivalent tag visitors:

function optimize_img( OD_Tag_Visitor_Context $context ): bool {
	if ( $context->processor->get_tag() !== 'IMG' ) {
		return false;
	}
	
	// Do optimizations based on $context->url_metric_group_collection...
	return true;
}
function optimize_img( OD_Tag_Visitor_Context $context ) {
	if ( $context->processor->get_tag() !== 'IMG' ) {
		return;
	}

	$context->track_tag();

	// Do optimizations based on $context->url_metric_group_collection...
}

For a bigger example of how this improves the DX, see this PR for the Intrinsic Dimensions PR: https://github.com/westonruter/od-intrinsic-dimensions/pull/1/files (cf. #10)

@westonruter westonruter added [Type] Enhancement A suggestion for improvement of an existing feature [Plugin] Optimization Detective Issues for the Optimization Detective plugin labels Jan 23, 2025
Copy link

codecov bot commented Jan 23, 2025

Codecov Report

Attention: Patch coverage is 95.45455% with 1 line in your changes missing coverage. Please review.

Project coverage is 65.54%. Comparing base (b7c4475) to head (cb89ea6).
Report is 3 commits behind head on trunk.

Files with missing lines Patch % Lines
plugins/optimization-detective/load.php 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##            trunk    #1821      +/-   ##
==========================================
+ Coverage   65.47%   65.54%   +0.07%     
==========================================
  Files          85       86       +1     
  Lines        6748     6766      +18     
==========================================
+ Hits         4418     4435      +17     
- Misses       2330     2331       +1     
Flag Coverage Δ
multisite 65.54% <95.45%> (+0.07%) ⬆️
single 37.77% <0.00%> (-0.11%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link

github-actions bot commented Jan 23, 2025

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 props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: westonruter <[email protected]>
Co-authored-by: felixarntz <[email protected]>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

Copy link
Member

@felixarntz felixarntz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@westonruter I like the more explicit approach here. That said, I'm not sure we need a new class - even for my taste a class for a single boolean may be a bit too verbose :)

* @since n.e.x.t
* @access private
*/
final class OD_Visited_Tag_State {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm all for classes where reasonable, but maybe this one goes a bit too far, since it's only a wrapper for a single boolean value? Some more verbosely architected PHP projects could work with this, but it seems excessive for WordPress.

Is there any reason a class is helpful here? Or could this simply be a bool property in OD_Tag_Visitor_Context? Since it's that class that's passed around to the callbacks and since it's already for a specific tag, couldn't we put the methods into that class?

If we do that, I think only the reset() method wouldn't work well at least per its name, since then that name would be too generic. But we might as well use track_tag() as a setter with a boolean parameter, e.g. like track_tag( $track = true ).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason why I did it this way is that the tag visitors shouldn't have access to the underlying state. They shouldn't be able to override whether or not another tag visitor requested to track the tag. So in this way, od_optimize_template_output_buffer() instanties the OD_Visited_Tag_State object and it has access to its internal state, but then it passes this into OD_Tag_Visitor_Context which does not expose any of that state to the tag visitors.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I see, that makes sense. Thanks!

@westonruter westonruter merged commit 082bf01 into trunk Jan 24, 2025
27 checks passed
@westonruter westonruter deleted the add/track-tag-method branch January 24, 2025 00:00
@westonruter westonruter mentioned this pull request Jan 26, 2025
5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Plugin] Optimization Detective Issues for the Optimization Detective plugin [Type] Enhancement A suggestion for improvement of an existing feature
Projects
Status: Done 😃
Development

Successfully merging this pull request may close these issues.

2 participants