Skip to content

Server Timing - Allow description and make duration optional#2379

Open
the-hercules wants to merge 6 commits intoWordPress:trunkfrom
the-hercules:fix/allow-desc-dur-optional-879
Open

Server Timing - Allow description and make duration optional#2379
the-hercules wants to merge 6 commits intoWordPress:trunkfrom
the-hercules:fix/allow-desc-dur-optional-879

Conversation

@the-hercules
Copy link
Contributor

@the-hercules the-hercules commented Feb 11, 2026

Summary

Fixes #879

Relevant technical choices

Enhance Server-Timing API for MDN Specification Compliance

Enhances the Server-Timing API to be fully compliant with the MDN Server-Timing specification by making duration optional and adding support for a desc (description) field.


Changes

class-perflab-server-timing-metric.php

  • Added $description property to Perflab_Server_Timing_Metric
  • Added set_description() and get_description() methods
  • Used native PHP string type hint for set_description() instead of string|mixed with manual type checking
  • Explicitly initialised $description = null for clarity

class-perflab-server-timing.php

  • Updated format_metric_header_value() to conditionally include dur= and desc= parameters
  • Added sanitization for description values to prevent header injection:
    • Escapes \ and "
    • Strips CR/LF control characters
    • Uses a combined addcslashes() call

Tests

  • Added @covers annotations to all new test methods
  • Added tests for all four header output formats:
    • Name-only
    • Duration-only
    • Description-only
    • Combined duration + description
  • Added edge-case tests for special characters in descriptions (", \, \n, \r)
  • Replaced vague @phpstan-param array<string, mixed> with:
    • @param array<string, MetricArguments>
    • Used @phpstan-import-type for accurate type definitions

Supported Header Formats

Format Example Output
Name only Server-Timing: missedCache
Duration only wp-metric;dur=45.23
Description only wp-metric;desc="Cache hit"
Duration + Description wp-metric;dur=45.23;desc="Database queries"

Testing

  • All 137 Server-Timing tests passing on single site and multisite

Fixes

Fixes #879

Use of AI Tools

Github Copilot CLI was used to write tests. Reviewed the code afterwards.

@github-actions
Copy link

github-actions bot commented Feb 11, 2026

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: the-hercules <thehercules@git.wordpress.org>
Co-authored-by: westonruter <westonruter@git.wordpress.org>
Co-authored-by: anandraj346 <anandraj346@git.wordpress.org>
Co-authored-by: dilipom13 <dilip2615@git.wordpress.org>
Co-authored-by: swissspidy <swissspidy@git.wordpress.org>

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

@the-hercules the-hercules changed the title Allow description and make duration optional Server Timing - Allow description and make duration optional Feb 11, 2026
@codecov
Copy link

codecov bot commented Feb 11, 2026

Codecov Report

❌ Patch coverage is 79.16667% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 69.34%. Comparing base (b9c435a) to head (83dd4e2).
⚠️ Report is 54 commits behind head on trunk.

Files with missing lines Patch % Lines
...rver-timing/class-perflab-server-timing-metric.php 50.00% 5 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##            trunk    #2379      +/-   ##
==========================================
+ Coverage   69.17%   69.34%   +0.17%     
==========================================
  Files          90       90              
  Lines        7708     7765      +57     
==========================================
+ Hits         5332     5385      +53     
- Misses       2376     2380       +4     
Flag Coverage Δ
multisite 69.34% <79.16%> (+0.17%) ⬆️
single 35.81% <79.16%> (+0.42%) ⬆️

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.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

…and improve type definitions

   - Replace string|mixed param with native string type hint in set_description(), removing manual type check
   - Explicitly initialize $description = null
   - Combine addcslashes() calls into single expression
   - Add @Covers annotations to new description test methods
   - Import MetricArguments phpstan type and replace mixed with array<string, MetricArguments> in test param annotations
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Updates the Performance Lab Server-Timing API to better match the Server-Timing header spec by allowing metrics to include an optional description (desc) and by making duration (dur) optional when formatting header segments.

Changes:

  • Add description support to Perflab_Server_Timing_Metric via new setter/getter.
  • Update Server-Timing header formatting to conditionally include dur= and/or desc= and sanitize desc for safe header output.
  • Extend unit tests to cover duration+description combinations and escaping/CRLF stripping edge cases.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
plugins/performance-lab/includes/server-timing/class-perflab-server-timing.php Formats metric segments with optional dur and desc, including description sanitization.
plugins/performance-lab/includes/server-timing/class-perflab-server-timing-metric.php Adds a new $description field plus set_description() / get_description().
plugins/performance-lab/tests/includes/server-timing/test-perflab-server-timing.php Adds tests for header output with descriptions and special-character edge cases.
plugins/performance-lab/tests/includes/server-timing/test-perflab-server-timing-metric.php Adds unit tests for description getter/setter behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 299 to 302
// If neither value nor description is set, skip this metric.
if ( null === $value && null === $description ) {
return null;
}
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

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

The PR description mentions supporting a “name only” header format (e.g. wp-metric), but this implementation still skips metrics when both duration and description are null (returns null), so name-only metrics will never be emitted. If MDN compliance is the goal, consider returning the metric name when both are null and adding a corresponding test case.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll update it to remove the "name only" format since that was inaccurate.Metric with neither duration nor description provides no useful information in the header. This PR only adds description as a new optional field alongside the existing optional duration.

Copy link
Member

Choose a reason for hiding this comment

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

Metric with neither duration nor description provides no useful information in the header.

Is this so? In the MDN docs, this example is provided:

// A metric with a name only
Server-Timing: missedCache

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@westonruter Added support for name only.

Comment on lines +159 to +173
* @param non-empty-string $description The metric description.
*/
public function set_description( string $description ): void {
if ( 0 !== did_action( 'perflab_server_timing_send_header' ) && ! doing_action( 'perflab_server_timing_send_header' ) ) {
_doing_it_wrong(
__METHOD__,
/* translators: %s: WordPress action name */
sprintf( esc_html__( 'The method must be called before or during the %s action.', 'performance-lab' ), 'perflab_server_timing_send_header' ),
''
);
return;
}

$this->description = $description;
}
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

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

The PHPDoc declares $description as non-empty-string, but the method currently accepts and stores empty strings without any validation. Either enforce non-empty input (e.g. _doing_it_wrong() on empty string) or relax the docblock to string so the contract matches runtime behavior.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Leaving it as it is, relying on the type hint for the developer.

Comment on lines +161 to +170
public function set_description( string $description ): void {
if ( 0 !== did_action( 'perflab_server_timing_send_header' ) && ! doing_action( 'perflab_server_timing_send_header' ) ) {
_doing_it_wrong(
__METHOD__,
/* translators: %s: WordPress action name */
sprintf( esc_html__( 'The method must be called before or during the %s action.', 'performance-lab' ), 'perflab_server_timing_send_header' ),
''
);
return;
}
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

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

set_description() adds a new “must be called before or during perflab_server_timing_send_header” guard (via _doing_it_wrong() and early return), but there is no test covering the late-call behavior (similar to test_set_value_prevents_late_measurement). Adding a unit test for the late-call path would help prevent regressions.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The late-call guard in set_description() uses the exact same
did_action() / doing_action() pattern as the existing set_value() method
(lines 91–99).

Adding a test for this would require calling:

do_action( 'perflab_server_timing_send_header' );

However, this triggers the global singleton to re-register default metrics
(e.g., before-template), which causes test isolation failures.

@westonruter westonruter added [Type] Enhancement A suggestion for improvement of an existing feature [Plugin] Performance Lab Issue relates to work in the Performance Lab Plugin only labels Feb 26, 2026
@the-hercules
Copy link
Contributor Author

@westonruter I’ve added support for name-only metrics as suggested. When you get a chance, could you please take a look?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Plugin] Performance Lab Issue relates to work in the Performance Lab Plugin only [Type] Enhancement A suggestion for improvement of an existing feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Server-Timing: allow desc field, make dur optional

3 participants