Skip to content

Theme JSON, Duotone: Fix return types and docblocks reported by PHPStan - #86103

Merged
westonruter merged 15 commits into
WordPress:trunkfrom
huzaifaalmesbah:fix/phpstan-lib-evergreen
Nov 6, 2026
Merged

westonruter merged 15 commits into
WordPress:trunkfrom
huzaifaalmesbah:fix/phpstan-lib-evergreen

Conversation

@huzaifaalmesbah

@huzaifaalmesbah huzaifaalmesbah commented Nov 1, 2026 •

Copy link
Copy Markdown
Member

What?

See #86088

Fixes return types and docblocks in WP_Theme_JSON_Gutenberg and WP_Duotone_Gutenberg reported by PHPStan, and removes the resolved entries from the PHPStan baseline. It also applies the array shape for block type selectors that was added to WordPress Core in r65318 (Core-65817, WordPress/wordpress-develop#15644).

Why?

Three of these are already fixed in WordPress Core, so the Gutenberg classes were out of sync. The fourth is in a deprecated, Gutenberg-only method. This resolves 4 of the 226 errors initially baselined in #66693.

How?

  • WP_Theme_JSON_Gutenberg::get_block_name_from_metadata_path() fell off the end without a return value when there was no path. It now returns $block_metadata['path'][2] ?? null, matching Core. (return.missing)
  • WP_Theme_JSON_Gutenberg::get_block_selectors() documented its $block_type parameter as object, but it is only called with block types from the registry and passes them to wp_get_block_css_selector(), so it is now documented as WP_Block_Type, matching Core.
  • The deprecated, Gutenberg-only WP_Theme_JSON_Gutenberg::get_block_custom_css() documented $css as array, but passes it to process_blocks_custom_css(), which expects a string. It is now documented as string. (argument.type)
  • WP_Duotone_Gutenberg::get_all_global_style_block_names() never returns null, so its @return is now array<string, string>, keyed by block name. (return.unusedType) This is more precise than Core's string[], so it can be synced to Core later.
  • Removed the 4 corresponding entries from tools/phpstan/baseline.neon.

The block type selectors shape from Core is applied wherever Gutenberg passes the selectors along, matching Core's docblocks: the return value of WP_Theme_JSON_Gutenberg::get_block_selectors(), the selectors key of get_feature_declarations_for_node()'s $metadata, and the parameters of gutenberg_get_state_style_groups(). As in Core, get_block_selectors() now prepends the root selector to the default feature selectors, so that a feature named root cannot replace the root selector string with an array.

The docblock of the Gutenberg-only $feature_selectors and $feature_declarations parameters of WP_Theme_JSON_Gutenberg::get_state_declarations_with_text_clip_escapes() now follows Core's layout, with the declaration list shapes that Core documents. Those need the names of the escape declarations to be non-empty strings, so gutenberg_get_state_text_clip_escape() and gutenberg_get_state_text_fill_reset() now document the exact declaration each returns, such as array{ 'background-image'?: 'unset' }. This is more precise than the array<non-falsy-string, non-falsy-string> that Core has on them, so it can be synced to Core later.

The WordPress stubs for PHPStan are generated from Core trunk at r65318, so that the analysis knows the type of WP_Block_Type::$selectors.

The method.childParameterType error for WP_REST_Global_Styles_Controller_Gutenberg::prepare_links() is left as it is: Core has the same code and keeps the same entry in its baseline, and fixing it would change the method signature.

There is no change in behavior.

Testing Instructions

  1. Run composer phpstan and confirm it passes with no unmatched ignored errors.
  2. Run composer phpstan:baselines and confirm tools/phpstan/baseline.neon has no further changes.
  3. Run the theme.json, block state, and duotone PHP unit tests: npm run test:unit:php -- -- --filter "WP_Theme_JSON|States|Duotone".

Testing Instructions for Keyboard

Not applicable. There are no UI changes.

Screenshots or screencast

Not applicable.

Use of AI Tools

I used Claude Code to identify the affected occurrences and draft the change. I reviewed the diff and take responsibility for it.

Additional changes were made by @westonruter using Claude Code with Claude Opus 5.5 (medium effort): the block type selectors shape, the matching Core docblocks, and the stubs update.

- Return `null` explicitly from `WP_Theme_JSON_Gutenberg::get_block_name_from_metadata_path()` when there is no path, as in Core.
- Document that `get_block_selectors()` returns an array, as in Core.
- Document the `$css` parameter of the deprecated, Gutenberg-only `get_block_custom_css()` as a string, which is what it passes on to `process_blocks_custom_css()`.
- Document that `WP_Duotone_Gutenberg::get_all_global_style_block_names()` returns `string[]`, as in Core.

Remove the 4 resolved entries from the PHPStan baseline.

See WordPress#86088.
@huzaifaalmesbah huzaifaalmesbah added [Type] Code Quality Issues or PRs that relate to code quality No Core Sync Required Indicates that any changes do not need to be synced to WordPress Core labels Nov 1, 2026
@github-actions

github-actions Bot commented Nov 1, 2026 •

Copy link
Copy Markdown

🤖 PR meta 🤖

Preview in WordPress Playground

🎉 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: huzaifaalmesbah <huzaifaalmesbah@git.wordpress.org>
Co-authored-by: westonruter <westonruter@git.wordpress.org>
Co-authored-by: noruzzamans <noruzzaman@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.

@noruzzamans

Copy link
Copy Markdown
Member

I had opened #86107 for the Duotone return type fix before spotting it here. I've closed #86107 in favor of this PR. Nice work!

@huzaifaalmesbah
huzaifaalmesbah marked this pull request as ready for review November 1, 2026 03:37
…green

# Conflicts:
#	tools/phpstan/baseline.neon
Comment thread lib/class-wp-duotone-gutenberg.php Resolved
Comment thread lib/class-wp-theme-json-gutenberg.php Resolved
Comment thread lib/class-wp-theme-json-gutenberg.php Resolved
westonruter and others added 9 commits November 5, 2026 15:56
…ck names

`WP_Duotone_Gutenberg::get_all_global_style_block_names()` returns a map of block names to duotone preset slugs, matching the `$global_styles_block_names` property type. The method never returns `null`, since it initializes the property to an empty array before populating it, so only the property keeps the nullable type.

Co-Authored-By: Claude Opus 5.5 (medium effort) <noreply@anthropic.com>
Claude-Code-Session-Id: 7a12dabb-d901-4983-86e2-a9cbfedc1158
`WP_Theme_JSON_Gutenberg::get_block_selectors()` is only called with block types from `WP_Block_Type_Registry::get_all_registered()`, and it passes the value on to `wp_get_block_css_selector()`, which expects a `WP_Block_Type`. Replace the generic `object` type accordingly.

Co-Authored-By: Claude Opus 5.5 (medium effort) <noreply@anthropic.com>
Claude-Code-Session-Id: 7a12dabb-d901-4983-86e2-a9cbfedc1158
Mirror the array shape that core defines for `WP_Block_Type::$selectors`: an optional `root` selector string, plus selectors for block support features (such as `color` or `typography`), each either a selector string or an array of selectors keyed by subfeature. Document it as `array<string, string|array<string, string>>`, with the more specific `array{ root?: string, ...<string, string|array<string, string>> }` in `@phpstan-` tags, on:

- The return value of `WP_Theme_JSON_Gutenberg::get_block_selectors()`.
- The `selectors` key of the `$metadata` parameter of `WP_Theme_JSON_Gutenberg::get_feature_declarations_for_node()`.
- The `$feature_selectors` parameter of `WP_Theme_JSON_Gutenberg::get_state_declarations_with_text_clip_escapes()`.
- The `$block_selectors` parameter of `gutenberg_get_state_style_groups()`.

In `WP_Theme_JSON_Gutenberg::get_block_selectors()`, the default selectors are now built by prepending the `root` selector to the feature selectors, rather than adding the feature selectors after it. The keys and their order are unchanged, but a feature named `root` in a subclass's `BLOCK_SUPPORT_FEATURE_LEVEL_SELECTORS` can no longer replace the root selector string with an array.

At level 10, this resolves 4 PHPStan errors once the WordPress stubs include the core change. With the current stubs, `WP_Block_Type::$selectors` is still a plain `array`, so 2 errors remain where it is returned from `get_block_selectors()` and passed to `gutenberg_get_state_style_groups()`.

Co-Authored-By: Claude Opus 5.5 (medium effort) <noreply@anthropic.com>
Claude-Code-Session-Id: 7a12dabb-d901-4983-86e2-a9cbfedc1158
…green

# Conflicts:
#	tools/phpstan/baseline.neon
…tors core PR

Generate the stubs from the head of the wordpress-develop pull request that defines the array shape for `WP_Block_Type::$selectors`, which the selector types in `WP_Theme_JSON_Gutenberg` and `gutenberg_get_state_style_groups()` depend on. With these stubs, the two level 10 PHPStan errors where the property is returned from `get_block_selectors()` and passed to `gutenberg_get_state_style_groups()` are resolved. The level 5 baseline is unchanged.

Regenerate the stubs with `--commit=trunk` once the core change is committed.

Co-Authored-By: Claude Opus 5.5 (medium effort) <noreply@anthropic.com>
Claude-Code-Session-Id: 7a12dabb-d901-4983-86e2-a9cbfedc1158
…aalmesbah/gutenberg into fix/phpstan-lib-evergreen

Co-Authored-By: Claude Opus 5.5 (medium effort) <noreply@anthropic.com>
Claude-Code-Session-Id: 7a12dabb-d901-4983-86e2-a9cbfedc1158
…le_groups()`

Mirror the core change to `wp_get_state_style_groups()`. The `$state_style` parameter is a theme.json style object, keyed by style feature (such as `color` or `typography`), and each key is looked up in `$block_selectors`. Its values come from theme.json and are checked by the function itself, so they stay `mixed`.

This resolves the `missingType.iterableValue` PHPStan error on the parameter. The caller, `gutenberg_get_block_state_style_rules()`, narrows each state style from `mixed` only with `is_array()`, so at level 10 it now reports an `argument.type` error for passing `array<mixed, mixed>`, which is a more accurate account of the untyped theme.json data.

Co-Authored-By: Claude Opus 5.5 (medium effort) <noreply@anthropic.com>
Claude-Code-Session-Id: 7a12dabb-d901-4983-86e2-a9cbfedc1158
westonruter and others added 2 commits November 6, 2026 12:50
Give `WP_Theme_JSON_Gutenberg::get_state_declarations_with_text_clip_escapes()` the same docblock layout as its core counterpart: plain `@param` types aligned in one column, with the precise types in `@phpstan-param` and `@phpstan-return` tags. This moves the selectors shape out of the `@param` for `$feature_selectors`, where it misaligned the column, and adds the declaration list shapes that core already documents for `$declarations` and the return value.

Those shapes need the `name` of each escape declaration to be a `non-falsy-string`, so `gutenberg_get_state_text_clip_escape()` and `gutenberg_get_state_text_fill_reset()` now also have the `@phpstan-return array<non-falsy-string, non-falsy-string>` tags of their core counterparts.

At level 10, this resolves the 2 `missingType.iterableValue` PHPStan errors on the method and introduces none.

Co-Authored-By: Claude Opus 5.5 (medium effort) <noreply@anthropic.com>
Claude-Code-Session-Id: 7a12dabb-d901-4983-86e2-a9cbfedc1158
The core change that defines the array shape for `WP_Block_Type::$selectors` was committed in r65318, so generate the stubs from that trunk commit instead of the pull request it was developed in. The level 5 baseline is unchanged.

Co-Authored-By: Claude Opus 5.5 (medium effort) <noreply@anthropic.com>
Claude-Code-Session-Id: 7a12dabb-d901-4983-86e2-a9cbfedc1158
…ct shapes

`gutenberg_get_state_text_clip_escape()` only ever returns `background-image: unset` or nothing, and `gutenberg_get_state_text_fill_reset()` only ever returns `-webkit-text-fill-color: currentColor` or nothing, so document their return values with those exact shapes rather than as maps of non-empty strings.

Co-Authored-By: Claude Opus 5.5 (medium effort) <noreply@anthropic.com>
Claude-Code-Session-Id: 7a12dabb-d901-4983-86e2-a9cbfedc1158

Copilot AI 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.

🟢 Approval recommended

No active review issues remain, with the documented annotation, stub, and baseline updates addressing the identified changes.

No open issues 🎉

What changed in this PR

This pull request aligns Gutenberg’s Theme JSON, duotone, and block-state PHPStan annotations with WordPress Core, updates the generated stubs to revision r65318, and removes four resolved baseline suppressions.

Changes:

  • Corrects return types and parameter documentation in Theme JSON and duotone code.
  • Applies precise block-selector and state-declaration array shapes.
  • Preserves root-selector precedence when constructing default selectors.
  • Updates WordPress stubs and removes resolved PHPStan baseline entries.
  • Review note: add focused tests covering root-first selector construction and subclass selector-key collisions.
File Description
tools/​phpstan/​wordpress-stubs/​composer.json Updates the generated WordPress stubs source revision to r65318.
tools/​phpstan/​baseline.neon Removes the four resolved PHPStan suppressions while retaining unrelated ignores.
lib/​class-wp-theme-json-gutenberg.php Corrects Theme JSON documentation, return handling, selector shapes, and root-selector handling.
lib/​class-wp-duotone-gutenberg.php Corrects the global style block names return documentation.
lib/​block-supports/​states.php Adds precise selector and state-declaration annotations.

Review effort: Lite

@westonruter
westonruter merged commit 69068c3 into WordPress:trunk Nov 6, 2026
102 of 105 checks passed
@github-actions github-actions Bot added this to the Gutenberg 24.5 milestone Nov 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

No Core Sync Required Indicates that any changes do not need to be synced to WordPress Core [Type] Code Quality Issues or PRs that relate to code quality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants