Skip to content

Ignore PHPStan false positives for WP_REST_Request offset writes - #86284

Merged
aaronrobertshaw merged 2 commits into
trunkfrom
update/phpstan-ignore-rest-request-offset-assign
Nov 4, 2026
Merged

aaronrobertshaw merged 2 commits into
trunkfrom
update/phpstan-ignore-rest-request-offset-assign

Conversation

@westonruter

@westonruter westonruter commented Nov 4, 2026 •

Copy link
Copy Markdown
Member

What?

See #86088

Moves the PHPStan errors for array-access writes on WP_REST_Request out of the baseline and into an intentional ignoreErrors entry in phpstan.neon.dist, as suggested in #86100 (comment).

Why?

Writes like $request['context'] = 'edit' are reported as offsetAssign.dimType and offsetAssign.valueType errors, but they are false positives. In the WordPress stubs, WP_REST_Request::offsetSet() uses a method-level template (@template TOffset of key-of<T>, @param T[TOffset] $value), which PHPStan does not resolve for an offset write, so every write is rejected, valid ones included. The equivalent set_param() call is accepted. The fix belongs in PHPStan: phpstan/phpstan-src#6903

Rewriting working code to suit a tooling bug (as was tried in #86110 and #86100) is not the right fix. And the baseline records errors that are still to be fixed in Gutenberg, which these are not. Core's phpstan.neon.dist already keeps such intentional ignores separate from its baseline, so this follows that prior art.

How?

  • Adds an ignoreErrors section to phpstan.neon.dist with two entries, matched on the message and identifier, covering both the bare WP_REST_Request and generic WP_REST_Request<…> forms. A comment explains the cause and links to the PHPStan PR, so the entries can be removed once a PHPStan release with the fix is required. reportUnmatched: false lets them lapse quietly in the meantime.
  • Moves the return.void ignore for action callbacks, added to tools/phpstan/base.neon in PHPStan: Ignore return.void errors for action callbacks #86108, into the same ignoreErrors section, so all intentional ignores live in one place. Entries are grouped under rule-level headings as in core; this one is under Level 0, since the HookCallbackRule that reports it is not gated by level.
  • Regenerates tools/phpstan/baseline.neon with composer phpstan:baselines, which drops the six WP_REST_Request entries now covered (seven errors, in lib/compat/wordpress-7.1/class-gutenberg-rest-attachments-controller-7-1.php and lib/media/class-gutenberg-rest-attachments-controller.php). The baseline goes from 58 to 51 errors.

Testing Instructions

  1. Run composer phpstan and confirm it reports no errors.
  2. Run composer phpstan:baselines and confirm tools/phpstan/baseline.neon has no further changes.

Testing Instructions for Keyboard

Not applicable. There are no UI changes.

Screenshots or screencast

Not applicable.

Use of AI Tools

This PR was authored with Claude Code using Claude Opus 5.5 (medium effort), which wrote the ignoreErrors entries, regenerated the baseline, and drafted this description.

PHPStan rejects every array-access write on WP_REST_Request, valid ones
included, because it does not resolve the method-level template that the
WordPress stubs declare on offsetSet(). These are not errors to be fixed
in Gutenberg, so they move from the baseline to an intentional
ignoreErrors entry in phpstan.neon.dist, with a link to the upstream fix
so the entry can be removed once a PHPStan release includes it.

The baseline is regenerated with `composer phpstan:baselines`, dropping
the six entries now covered.

Co-Authored-By: Claude Opus 5.5 (medium effort) <noreply@anthropic.com>
Claude-Code-Session-Id: ce560f88-7bfc-4577-bd90-4d370efe8a19
@westonruter westonruter added the [Type] Code Quality Issues or PRs that relate to code quality label Nov 4, 2026
@westonruter
westonruter marked this pull request as ready for review November 4, 2026 06:34
@github-actions

github-actions Bot commented Nov 4, 2026 •

Copy link
Copy Markdown

🤖 PR meta 🤖

Preview in WordPress Playground Preview in Storybook

🎉 Props

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 <westonruter@git.wordpress.org>
Co-authored-by: aaronrobertshaw <aaronrobertshaw@git.wordpress.org>

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


Props Bot commands

You can trigger Props Bot actions by commenting on this PR:

  • @propsbot add <username> to give props to a GitHub user. Requires write access.
  • @propsbot remove <username> to leave a user out of the props. Requires write access.
  • @propsbot refresh to refresh this list. Adding the props-bot label works too.

Updated as activity occurs, without notifying anyone named here. Add the props-bot label to refresh.

⚡ Performance

Show the results

Client side metrics exclude the server response time.

front-end-block-theme

Metric 009fcd0 trunk % Change
timeToFirstByte 55.5 ms +13.42% -1.98% 55.4 ms +11.82% -4.15% 0.18%
largestContentfulPaint 92 ms +8.7% -4.35% 94 ms +6.38% -6.38% -2.13%
lcpMinusTtfb 34.25 ms +9.78% -4.53% 34.4 ms +20.93% -1.89% -0.44%
wpBeforeTemplate 29.66 ms +10.45% -3.51% 29 ms +20.66% -2.79% 2.28%
wpTemplate 22.41 ms +2.05% -3.61% 21.96 ms +3.37% -4.1% 2.05%
wpTotal 51.8 ms +12.1% -2.07% 51.72 ms +11.77% -4.56% 0.15%
wpMemoryUsage 7.71 MB +0% -0% 7.67 MB +0% -0% 0.43%
wpDbQueries 16 +0% -0% 16 +0% -0% 0%

front-end-classic-theme

Metric 009fcd0 trunk % Change
timeToFirstByte 46.45 ms +4.95% -2.69% 46.05 ms +5.65% -1.95% 0.87%
largestContentfulPaint 100 ms +4% -2% 100 ms +0% -4% 0%
lcpMinusTtfb 53.7 ms +2.14% -3.07% 52.3 ms +4.02% -2.01% 2.68%
wpBeforeTemplate 26.67 ms +2.29% -1.57% 26.22 ms +7.7% -1.95% 1.72%
wpTemplate 16.77 ms +3.76% -5.61% 16.69 ms +2.46% -3.18% 0.48%
wpTotal 43.22 ms +5.69% -2.43% 42.92 ms +6.29% -2.35% 0.7%
wpMemoryUsage 6.40 MB +0% -0% 6.36 MB +0% -0% 0.53%
wpDbQueries 13 +0% -0% 13 +0% -0% 0%

media-processing

Metric 009fcd0 trunk % Change
mediaProcessingJpeg 396.53 ms +1.91% -0.48% 396.41 ms +2.26% -1.35% 0.03%
mediaProcessingAvif 6115.14 ms +0.53% -0.53% 6110.9 ms +1.12% -1.73% 0.07%
mediaProcessingJpegToAvif 4241.2 ms +0.24% -0.37% 4129.57 ms +1.98% -0.86% 2.7%

media-upload

Metric 009fcd0 trunk % Change
jpegUploadProcessing 1944.63 ms +1.38% -24.36% 1460 ms +1.2% -0.76% 33.19%
pngUploadProcessing 166.16 ms +66.63% -4.99% 173.94 ms +39.3% -5.94% -4.47%
largeJpegUploadProcessing 1454.8 ms +1.3% -0.53% 1455.81 ms +1.03% -0.63% -0.07%
multipleImageUploadProcessing 2028.27 ms +3.66% -0.55% 1598.03 ms +25.47% -1.28% 26.92%

post-editor

Metric 009fcd0 trunk % Change
serverResponse 241.99 ms +4.59% -7.42% 251.02 ms +5.83% -7.25% -3.6%
firstPaint 232.73 ms +27.74% -15.75% 317.44 ms +3.51% -18.06% -26.69%
domContentLoaded 528.98 ms +2.66% -1.65% 556.15 ms +4.48% -0.97% -4.89%
loaded 529.95 ms +2.59% -1.71% 556.9 ms +4.47% -0.96% -4.84%
firstContentfulPaint 316.54 ms +0.77% -0.53% 327.96 ms +6.12% -1.43% -3.48%
firstBlock 1333.34 ms +2.15% -2.41% 1424.71 ms +2.07% -2.26% -6.41%
type 12.51 ms +0.8% -9.11% 13.09 ms +9.09% -12.61% -4.43%
typeWithoutInspector 12.33 ms +4.22% -9.41% 13.42 ms +7% -7.68% -8.12%
typeWithTopToolbar 17.83 ms +7.85% -9.98% 19.04 ms +2.52% -5.41% -6.36%
typeContainer 6.63 ms +7.69% -12.22% 5.92 ms +11.99% -8.11% 11.99%
focus 79.92 ms +5.79% -1.23% 72.17 ms +8.47% -4.13% 10.74%
firstFocus 181.41 ms +0% -0% 152.85 ms +0% -0% 18.68%
selectAll 244.31 ms +1.11% -0.85% 243.16 ms +0.48% -1% 0.47%
undo 21.94 ms +6.88% -3.19% 21.14 ms +6.58% -8.51% 3.78%
listViewOpen 49.16 ms +3.42% -9.38% 43.2 ms +12.71% -1.62% 13.8%
inserterOpen 27.45 ms +12.35% -6.34% 29.08 ms +9.7% -7.32% -5.61%
inserterHover 8.65 ms +5.32% -5.32% 8.41 ms +10.82% -6.3% 2.85%
inserterSearch 6.9 ms +5.36% -4.93% 6.81 ms +5.43% -7.49% 1.32%
loadPatterns 463.35 ms +2.5% -7.32% 435.42 ms +3.85% -3.13% 6.41%
wpTotal 235.08 ms +4.54% -7.41% 244.44 ms +5.93% -7.43% -3.83%
wpMemoryUsage 14.75 MB +0% -0% 14.72 MB +0% -0% 0.24%
wpDbQueries 55 +0% -0% 55 +0% -0% 0%

site-editor

Metric 009fcd0 trunk % Change
serverResponse 264.67 ms +16.43% -1.51% 244.11 ms +3.33% -6.16% 8.42%
firstPaint 251.33 ms +28.06% -21.1% 241.21 ms +35.11% -29.55% 4.2%
domContentLoaded 647.55 ms +6.09% -6.5% 559.88 ms +22.55% -1.71% 15.66%
loaded 648.49 ms +6.09% -6.52% 560.52 ms +22.73% -1.7% 15.69%
firstContentfulPaint 366.62 ms +7.77% -4.5% 342.34 ms +26.56% -5.84% 7.09%
firstBlock 2012.57 ms +6.79% -8.7% 1647.41 ms +18.53% -2.64% 22.17%
type 13.52 ms +15.61% -4.29% 12.82 ms +9.83% -6.94% 5.46%
navigate 81.96 ms +4.83% -13.82% 77.61 ms +4.36% -3.81% 5.6%
loadPatterns 979.23 ms +4.62% -6.21% 939.1 ms +12.69% -14.19% 4.27%
loadPages 943.92 ms +6.89% -0.62% 935.22 ms +3.04% -0.71% 0.93%
wpTotal 257.97 ms +16.68% -1.38% 237.96 ms +3.32% -6.5% 8.41%
wpMemoryUsage 12.77 MB +0% -0% 12.73 MB +0% -0% 0.27%
wpDbQueries 45 +0% -0% 45 +0% -0% 0%

49e6b18 Run

🏁 Flaky tests

Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

As a site builder, I want to insert a core navigation overlay pattern with CTA and see it on the frontend in /test/e2e/specs/site-editor/navigation-overlay-template-part.spec.js, passed after 1 failed attempt.
Error: expect(locator).toBeVisible() failed

Locator:  getByRole('option', { name: /Overlay with site info and CTA/i })
Expected: visible
Received: hidden
Timeout:  5000ms

Call log:
  - Expect "toBeVisible" getByRole('option', { name: /Overlay with site info and CTA/i }) with timeout 5000ms
  - waiting for getByRole('option', { name: /Overlay with site info and CTA/i })
    14 × locator resolved to <div role="option" tabindex="-1" data-base-ui-tooltip-trigger="" aria-label="Overlay with site info and CTA" class="block-editor-block-patterns-list__item" id="core/navigation-overlay-centered-with-extras" aria-describedby="block-editor-block-patterns-list__item-description-4">…</div>
       - unexpected value "hidden"

    at /home/runner/work/gutenberg/gutenberg/test/e2e/specs/site-editor/navigation-overlay-template-part.spec.js:254:31

49e6b18 Run

@westonruter

Copy link
Copy Markdown
Member Author

cc @noruzzamans

The action callback return.void ignore that trunk added to
tools/phpstan/base.neon is an intentional ignore like the WP_REST_Request
ones, so it moves into the ignoreErrors section of phpstan.neon.dist under
a Level 0 heading. The conflicting baseline is regenerated.

Co-Authored-By: Claude Opus 5.5 (medium effort) <noreply@anthropic.com>
Claude-Code-Session-Id: ce560f88-7bfc-4577-bd90-4d370efe8a19

@aaronrobertshaw aaronrobertshaw left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for putting this together 👍

It tests well for me.

✅ composer phpstan is clean on the PR merged with current trunk
✅ composer phpstan:baselines leaves no diff
✅ Ignored writes are valid set_param() equivalents
✅ Patterns are anchored to WP_REST_Request

I have one suggestion, not a blocker. Could the two REST entries drop reportUnmatched: false?

Without it, the PHPStan bump with the upstream fix would fail and prompt their removal. That matches how the return.void and baseline entries behave. As is, they would stay quietly.

A small follow-up could mention this list in the PHPStan coding guidelines. They only describe the baseline.

I'm happy to approve and merge this now though. It will unblock a better approach to avoiding PHPStan errors while functions land in Gutenberg before Core.

@westonruter

Copy link
Copy Markdown
Member Author

I have one suggestion, not a blocker. Could the two REST entries drop reportUnmatched: false?

Yeah, that's true. Removing that would ensure PHPStan complains when the underlying issue is fixed, prompting their removal. So these can be removed the next time we're updating this, but otherwise I'm sure I'll do that once phpstan/phpstan-src#6903 is merged. In any case, the ignoring if the rules would just be some dead code and otherwise harmless.

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

Labels

[Type] Code Quality Issues or PRs that relate to code quality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants