Skip to content

feat: let wp_set_presence() accept an explicit GMT timestamp - #456

Open
theaminulai wants to merge 2 commits into
WordPress:mainfrom
theaminulai:Add-optional-timestamp-to-wp_set_presence
Open

feat: let wp_set_presence() accept an explicit GMT timestamp#456
theaminulai wants to merge 2 commits into
WordPress:mainfrom
theaminulai:Add-optional-timestamp-to-wp_set_presence

Conversation

@theaminulai

Copy link
Copy Markdown
Member

Why

#452 wp_set_presence() hardcodes $now = gmdate( 'Y-m-d H:i:s' ) as the row's date_gmt, so a caller relaying awareness on behalf of other clients (like sync-storage bridging Gutenberg collaborators) can't preserve their real timestamps. Every relayed entry gets stamped with the relay's own clock, so whoever happens to be polling holds everyone else in the room alive. sync-storage hit exactly this before sync-storage#91: each client rewrote every collaborator's row on every poll, keeping departed collaborators in the room.

What

  • wp_set_presence() gains an optional 5th parameter, $date_gmt ('Y-m-d H:i:s', same shape wp_get_presence() already returns), defaulting to null (current behavior: now).
  • A supplied timestamp in the future is clamped to now via min( $date_gmt, $current ), so a caller can't pin a row past the TTL indefinitely.
  • @since 0.3.0 added on the parameter; README's PHP API snippet for wp_set_presence() updated to document it.

How

$now is computed as null === $date_gmt ? $current : min( $date_gmt, $current ) string comparison works because 'Y-m-d H:i:s' sorts lexicographically the same as chronologically. Everything downstream (the INSERT ... ON DUPLICATE KEY UPDATE) is unchanged; $now just has a new source.

Scope note: #452 also lists "the skip guard from #450 does not swallow a write that carries an explicit timestamp" as done-when criteria. #450 (a guard that skips a write when state is unchanged and the row is inside the cutoff) hasn't landed in this repo yet; there's currently no such guard in wp_set_presence() to interact with. This PR implements #452 standalone; whoever lands #450 will need to make sure its "skip" branch checks for null !== $date_gmt (or equivalent) before discarding an explicitly-timestamped write.

Test instructions

  1. composer phpcs / composer phpstan both clean.
  2. npm test (needs wp-env/Docker); new tests in tests/test-functions.php:
    • test_set_presence_defaults_date_gmt_to_now no $date_gmt arg still stamps now.
    • test_set_presence_accepts_an_explicit_past_timestamp a past timestamp is stored verbatim.
    • test_set_presence_clamps_a_future_timestamp_to_now a future timestamp is clamped, not trusted.
  3. Manual: call wp_set_presence( $room, 'client-1', [], 0, gmdate('Y-m-d H:i:s', time() - 300) ) via wp eval or a custom mu-plugin, then wp presence list and confirm the row's age reflects the past timestamp, not "just now."

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

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.

Core Committers: Use this line as a base for the props when committing in SVN:

Props theaminuldev, iamchitti, joefusco.

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

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

🛝 WordPress Playgrounds

Built from 4077810

5 users 40 users
Single site Launch single site, 5 users Launch single site, 40 users
Multisite Launch multisite, 5 users Launch multisite, 40 users

@codecov

codecov Bot commented Sep 2, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 95.97%. Comparing base (b2cac55) to head (4077810).

Additional details and impacted files
@@            Coverage Diff            @@
##               main     #456   +/-   ##
=========================================
  Coverage     95.97%   95.97%           
  Complexity      255      255           
=========================================
  Files            21       21           
  Lines          3056     3057    +1     
=========================================
+ Hits           2933     2934    +1     
  Misses          123      123           
Flag Coverage Δ
multisite 95.97% <100.00%> (+<0.01%) ⬆️
phpunit 71.05% <100.00%> (+<0.01%) ⬆️

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

🚀 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.

@i-am-chitti i-am-chitti left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks for the contribution.

#450 has been merged, so the scope note is stale. Once, this PR merges the bug is in main.

wp_presence_write_is_redundant() never sees $date_gmt, so it skips the write. A relay backdating a collaborator who has left gets ignored, and they stay in the room. The parameter does nothing for the case it was added for.

Fix looks like one line at includes/functions.php:311, plus a test for that combination:

if ( null === $date_gmt && wp_presence_write_is_redundant( $room, $client_id, $data_json ) ) {
      return true;
}

Comment thread includes/functions.php
$data_json = wp_json_encode( $state );
$now = gmdate( 'Y-m-d H:i:s' );
$current = gmdate( 'Y-m-d H:i:s' );
$now = null === $date_gmt ? $current : min( $date_gmt, $current );

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

$date_gmt needs validating as a real date before it gets here. Invalid input string can be passed and processed incorrectly. A format check up front would be better -

if ( null !== $date_gmt && ! DateTimeImmutable::createFromFormat( 'Y-m-d H:i:s', $date_gmt ) ) {
      return false;
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Thanks for the feedback. I agree that $date_gmt should be validated before it is processed.

One question regarding the validation approach: as far as I can see, DateTimeImmutable::createFromFormat() is not currently used in the WordPress codebase. Would you prefer us to use this native PHP API here, or should we follow an existing WordPress date/time validation pattern instead?

I’d prefer to keep this consistent with existing WordPress Core conventions if there is an appropriate utility/pattern available.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Validate it up front with a preg_match on the full Y-m-d H:i:s plus wp_checkdate(), which is what wp_resolve_post_date() does.

@josephfusco
josephfusco force-pushed the Add-optional-timestamp-to-wp_set_presence branch from cf8ca86 to 97a7afd Compare September 4, 2026 13:56
github-actions Bot added a commit that referenced this pull request Sep 4, 2026
github-actions Bot added a commit that referenced this pull request Sep 4, 2026
github-actions Bot added a commit that referenced this pull request Sep 4, 2026
Comment thread includes/functions.php Outdated
github-actions Bot added a commit that referenced this pull request Sep 4, 2026

@josephfusco josephfusco left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This is nearly there and just needs the guard bypass so an explicit timestamp is not dropped, plus the format check from the thread above.

@josephfusco josephfusco added the [Area] Database Issues for the wp_presence table and cron cleanup label Sep 4, 2026
github-actions Bot added a commit that referenced this pull request Sep 5, 2026
theaminulai and others added 2 commits September 5, 2026 20:09
Extend `wp_set_presence()` with an optional `$date_gmt` argument so relayed presence updates can keep the original client timestamp instead of always using the relay server clock. Future timestamps are clamped to current GMT time to avoid rows being pinned beyond TTL. README API docs and function tests were updated to cover default behavior, explicit past timestamps, and future clamping.
@josephfusco
josephfusco force-pushed the Add-optional-timestamp-to-wp_set_presence branch from b905367 to 4077810 Compare September 6, 2026 00:09
github-actions Bot added a commit that referenced this pull request Sep 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Area] Database Issues for the wp_presence table and cron cleanup

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants