Skip to content

Sync PHPStan fixes from Gutenberg for block supports, duotone, and theme.json - #14862

Closed
westonruter wants to merge 11 commits into
WordPress:trunkfrom
westonruter:fix/phpstan-gutenberg-sync-65817
Closed

westonruter wants to merge 11 commits into
WordPress:trunkfrom
westonruter:fix/phpstan-gutenberg-sync-65817

Conversation

@westonruter

@westonruter westonruter commented Oct 25, 2026 •

Copy link
Copy Markdown
Member

✅ Committed in r64727 (f72b766).


This brings to core the fixes from four Gutenberg PRs, now merged, that resolved PHPStan errors in the plugin, as part of adding PHPStan to Gutenberg (WordPress/gutenberg#66598):

  • Shadow block support (chore: remove always-true conditionals in gutenberg_register_shadow_support() gutenberg#68398): wp_register_shadow_support() was missing the ! before both array_key_exists() checks since r56046. As a result, a block type with shadow support but no style or shadow attribute never had them registered, while a block type that already defined them had its definitions replaced, dropping any default. Gutenberg already has the negation, so core now matches it. Tests are added for the attribute registration; two of the three data sets fail without the fix.
  • Layout block support (Cast $parent_column_value to a string before exploding in gutenberg_get_child_layout_style_rules() gutenberg#68404): wp_get_child_layout_style_rules() passed a float as the separator to explode(). It is now cast to a string, which is what PHP did implicitly.
  • Duotone (Cast the parsed hue to a float in WP_Duotone_Gutenberg::colord_parse_hsla_string() gutenberg#68405): WP_Duotone::colord_parse_hsla_string() passed the matched hue string to colord_parse_hue(), which expects a float. It is now cast, like the saturation, lightness, and alpha values beside it.
  • Theme JSON (Narrow types and sync WP_Theme_JSON_Gutenberg::get_feature_declarations_for_node() with core gutenberg#68407): WP_Theme_JSON::get_feature_declarations_for_node() no longer has any PHPStan errors, even at level 10. This follows up on r63426, which corrected its @param types from object to array.
    • Its types are narrowed, with the declaration shape that get_state_declarations_with_text_clip_escapes() already uses.
    • It checks that a node feature is an array before reading its subfeatures, since the node comes from theme.json data. The subfeatures are read from a local copy that is written back after the loop, because PHPStan forgets the narrowed type after the unset() inside the loop. This also fixes a fatal error: when a feature's value is a string, such as a shadow, and the block's selectors for that feature are a list rather than keyed by subfeature name, each numeric key reads a character of the string, so empty() is false and the unset() throws Error: Cannot unset string offsets. (This corrects an earlier claim here that behavior was unchanged, which only held for selectors keyed by name.)
    • The $theme_json property gets a shape for its version, settings, and styles keys, so $this->theme_json['settings'] is an array rather than mixed throughout the class.
    • compute_style_properties() gets a @phpstan-return with the same declaration shape.

The PHPStan baseline entries for the layout and duotone errors are removed. At level 5 nothing new is reported. At level 10, class-wp-theme-json.php goes from 987 to 940 errors. The last two items move some errors to the boundaries: assignments to $theme_json from methods that still return a plain array, and declaration values passed to is_safe_css_declaration(), which expects a string. Those are left for a follow-up.

Trac ticket: https://core.trac.wordpress.org/ticket/65817

Use of AI Tools

AI assistance: Yes
Tool(s): Claude Code
Model(s): Claude Opus 5.5
Used for: Porting the fixes from the Gutenberg PRs, writing the shadow support tests, and drafting the commit messages and this description, under the direction and review of the author.


This Pull Request is for code review only. Please keep all other discussion in the Trac ticket. Do not merge this Pull Request. See GitHub Pull Requests for Code Review in the Core Handbook for more details.

🤖 Generated with Claude Code

westonruter and others added 4 commits October 25, 2026 13:52
`wp_register_shadow_support()` checked `array_key_exists()` without
negating it, which inverted both conditions: a block type with shadow
support but no `style` or `shadow` attribute never had them registered,
while one that already defined them had its definitions replaced,
dropping any `default`. Negate the checks so the attributes are added when
absent and left alone when present, as the other block supports do and as
Gutenberg's `gutenberg_register_shadow_support()` already does.

Add tests covering the attribute registration.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`wp_get_child_layout_style_rules()` passed the float from `floatval()` as
the separator to `explode()`, which expects a string. Cast it explicitly,
which yields the same separator that PHP's implicit conversion did, and
remove the corresponding PHPStan baseline entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`WP_Duotone::colord_parse_hsla_string()` passed the matched hue string
straight to `colord_parse_hue()`, which expects a float, while the
saturation, lightness, and alpha matches beside it were already cast.
Cast the hue too, and remove the corresponding PHPStan baseline entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Describe the shapes `WP_Theme_JSON::get_feature_declarations_for_node()`
works with: the block metadata and the node are string-keyed arrays, the
`selectors` the method reads map each feature to a selector string or to a
map of subfeature selectors, and the returned declarations are keyed by
selector. Each declaration list uses the same shape that
`get_state_declarations_with_text_clip_escapes()` already documents.

This resolves the PHPStan errors on mixed offsets and iteration inside the
method at the higher rule levels.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Test using WordPress Playground

The changes in this pull request can previewed and tested using a WordPress Playground instance.

WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser.

Some things to be aware of

  • All changes will be lost when closing a tab with a Playground instance.
  • All changes will be lost when refreshing the page.
  • A fresh instance is created each time the link below is clicked.
  • Every time this pull request is updated, a new ZIP file containing all changes is created. If changes are not reflected in the Playground instance,
    it's possible that the most recent build failed, or has not completed. Check the list of workflow runs to be sure.

For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation.

Test this pull request with WordPress Playground.

westonruter and others added 4 commits October 25, 2026 14:23
`get_feature_declarations_for_node()` only checked that the feature's
selectors were an array before reading `$node[ $feature ][ $subfeature ]`,
but the node comes from theme.json data, where the feature value may be a
string or another non-array value. Such a value has no subfeatures, and
`empty()` already treated every lookup on it as empty, so skipping the
subfeature loop for it keeps the behavior the same while making the check
explicit.

This resolves the PHPStan error for accessing an offset on `mixed`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Checking `is_array( $node[ $feature ] )` was not enough for PHPStan,
since unsetting `$node[ $feature ][ $subfeature ]` inside the loop made it
forget the narrowed type on the next iteration. Copy the feature styles to
a local variable, read and unset the subfeatures there, and write the copy
back to the node after the loop, which leaves the node exactly as before.

This resolves the PHPStan error for accessing an offset on `mixed`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Give the `$theme_json` property a `@phpstan-var` shape for its top-level
`version`, `settings`, and `styles` keys, leaving it open to the others.
Reading `$this->theme_json['settings']` or `['styles']` then yields an
array rather than `mixed`, which resolves the PHPStan errors at the higher
rule levels where these are passed to `compute_style_properties()` and the
other style helpers, or have offsets read from them.

The methods that build the data still return a plain `array`, so the
assignments to the property are now reported instead, until those return
types are narrowed as well.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Give `compute_style_properties()` a `@phpstan-return` with the same
declaration shape that `get_state_declarations_with_text_clip_escapes()`
documents. Its callers, including `get_feature_declarations_for_node()`,
then see a list of declarations rather than a plain array, which resolves
their PHPStan errors at the higher rule levels.

This moves some errors elsewhere at those levels: to the method's own body, whose property
names come from the untyped `$properties` parameter and whose font size
value may be `null` as far as PHPStan can tell, and to the callers that pass
a declaration value to `is_safe_css_declaration()`, which expects a string.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
westonruter added a commit to justlevine/gutenberg that referenced this pull request Oct 25, 2026
The fix is being brought to core in WordPress/wordpress-develop#14862,
along with the related PHPStan fixes from three other Gutenberg PRs, which
share this entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
westonruter added a commit to justlevine/gutenberg that referenced this pull request Oct 25, 2026
The fix is being brought to core in WordPress/wordpress-develop#14862,
along with the related PHPStan fixes from three other Gutenberg PRs, which
share this entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
westonruter added a commit to justlevine/gutenberg that referenced this pull request Oct 25, 2026
The fix is being brought to core in WordPress/wordpress-develop#14862,
along with the related PHPStan fixes from three other Gutenberg PRs, which
share this entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
westonruter added a commit to justlevine/gutenberg that referenced this pull request Oct 25, 2026
The fix is being brought to core in WordPress/wordpress-develop#14862,
along with the related PHPStan fixes from three other Gutenberg PRs, which
share this entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
westonruter added a commit to WordPress/gutenberg that referenced this pull request Oct 25, 2026
…support()` (#68398)

* chore: remove always-true conditionals in `gutenberg_register_shadow_support()`

* Add the backport changelog entry for the core PR

The fix is being brought to core in WordPress/wordpress-develop#14862,
along with the related PHPStan fixes from three other Gutenberg PRs, which
share this entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: justlevine <justlevine@git.wordpress.org>
Co-authored-by: westonruter <westonruter@git.wordpress.org>
westonruter added a commit to WordPress/gutenberg that referenced this pull request Oct 25, 2026
…g_get_child_layout_style_rules()` (#68404)

* fix: cast `$parent_column_value` before exploding in gutenberg_render_layout_support_flag()

* Add the backport changelog entry for the core PR

The fix is being brought to core in WordPress/wordpress-develop#14862,
along with the related PHPStan fixes from three other Gutenberg PRs, which
share this entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: justlevine <justlevine@git.wordpress.org>
Co-authored-by: westonruter <westonruter@git.wordpress.org>
Co-authored-by: carolinan <poena@git.wordpress.org>
westonruter added a commit to WordPress/gutenberg that referenced this pull request Oct 25, 2026
…_hsla_string()` (#68405)

* fix: cast `$match[1]` to float in WP_DuotoneGutenberg::colord_parse_hsla_string()

* Add the backport changelog entry for the core PR

The fix is being brought to core in WordPress/wordpress-develop#14862,
along with the related PHPStan fixes from three other Gutenberg PRs, which
share this entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: justlevine <justlevine@git.wordpress.org>
Co-authored-by: westonruter <westonruter@git.wordpress.org>
Co-authored-by: carolinan <poena@git.wordpress.org>
westonruter added a commit to WordPress/gutenberg that referenced this pull request Oct 26, 2026
…ons_for_node()` with core (#68407)

* docs: fix $metadata param type in WP_Theme_JSON_Gutenberg::get_feature_declarations_for_node()

* Document `$node` as an array too, as core does

Both parameters of `get_feature_declarations_for_node()` only ever receive
arrays. This syncs the `@param` tags with `WP_Theme_JSON` in WordPress
core as of r63426, which corrected both from `object` and noted that the
by-reference `$node` has its promoted features removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Narrow the types for get_feature_declarations_for_node()

Describe the shapes `WP_Theme_JSON_Gutenberg::get_feature_declarations_for_node()`
works with: the `selectors` it reads from the block metadata map each
feature to a selector string or to a map of subfeature selectors, the node
is a theme.json node keyed by feature, and the returned declarations are
keyed by selector. Each declaration list uses the same shape that
`get_state_declarations_with_text_clip_escapes()` already documents.

This resolves the PHPStan errors on mixed offsets and iteration inside the
method at the higher rule levels.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Fix indentation

* Type the block metadata as a string-keyed array

The block metadata passed to `get_feature_declarations_for_node()` is a
string-keyed array, just like the node, so say so in the plain `@param`
tag as well. The `@phpstan-param` shape still describes the `selectors`
key the method reads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Add the backport changelog entry for the core PR

The fix is being brought to core in WordPress/wordpress-develop#14862,
along with the related PHPStan fixes from three other Gutenberg PRs, which
share this entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Check that a node feature is an array before reading its subfeatures

`get_feature_declarations_for_node()` only checked that the feature's
selectors were an array before reading `$node[ $feature ][ $subfeature ]`,
but the node comes from theme.json data, where the feature value may be a
string or another non-array value. Such a value has no subfeatures, and
`empty()` already treated every lookup on it as empty, so skipping the
subfeature loop for it keeps the behavior the same while making the check
explicit.

This resolves the PHPStan error for accessing an offset on `mixed`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Read the subfeatures from a local copy of the feature styles

Checking `is_array( $node[ $feature ] )` was not enough for PHPStan,
since unsetting `$node[ $feature ][ $subfeature ]` inside the loop made it
forget the narrowed type on the next iteration. Copy the feature styles to
a local variable, read and unset the subfeatures there, and write the copy
back to the node after the loop, which leaves the node exactly as before.

This resolves the PHPStan error for accessing an offset on `mixed`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Fall back to an empty settings array, as core does

When the theme.json data has no `settings`, `get_styles_for_block()` and
`get_feature_declarations_for_node()` fell back to `null`, which was
then passed on as the settings to `compute_style_properties()` and the
other style helpers, all of which expect an array. Core has always fallen
back to an empty array here, so do the same.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Describe the shape of the theme.json data property

Give the `$theme_json` property a `@phpstan-var` shape for its top-level
`version`, `settings`, and `styles` keys, leaving it open to the others.
Reading `$this->theme_json['settings']` or `['styles']` then yields an
array rather than `mixed`, which resolves the PHPStan errors at the higher
rule levels where these are passed to `compute_style_properties()` and the
other style helpers, or have offsets read from them.

The methods that build the data still return a plain `array`, so the
assignments to the property are now reported instead, until those return
types are narrowed as well.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Describe the declarations returned by compute_style_properties()

Give `compute_style_properties()` a `@phpstan-return` with the same
declaration shape that `get_state_declarations_with_text_clip_escapes()`
documents. Its callers, including `get_feature_declarations_for_node()`,
then see a list of declarations rather than a plain array, which resolves
their PHPStan errors at the higher rule levels.

This moves some errors elsewhere at those levels: to the method's own body, whose property
names come from the untyped `$properties` parameter and whose font size
value may be `null` as far as PHPStan can tell, and to the callers that pass
a declaration value to `is_safe_css_declaration()`, which expects a string.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Pass the settings to get_state_base_styles() without a fallback

Now that `get_styles_for_block()` falls back to an empty settings array,
`$settings` is never `null`, so the `?? array()` when passing it to
`get_state_base_styles()` is redundant. Drop it, as core does.

This resolves the PHPStan error that the variable on the left side of
`??` always exists and is not nullable.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Sync the get_feature_declarations_for_node() comments with core

Bring over the documentation differences from
`WP_Theme_JSON::get_feature_declarations_for_node()` in core: the
punctuation in the summary, the `@since` tag, no blank line between the
`@param` and `@return` tags, and multi-line comments in the `/* */` style
instead of consecutive `//` lines. The method is now identical to core's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Remove the core @SInCE tag from get_feature_declarations_for_node()

The `@since 6.3.0` tag brought over from core refers to the WordPress
release that added the method, which is meaningless for the plugin's copy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: justlevine <justlevine@git.wordpress.org>
Co-authored-by: westonruter <westonruter@git.wordpress.org>
Co-authored-by: carolinan <poena@git.wordpress.org>
westonruter and others added 3 commits October 25, 2026 17:20
Add a `Shadow_Attributes` type alias for the `style` and `shadow`
attribute definitions, and use it to narrow the `$value` and `$expected`
parameters of `test_wp_register_shadow_support()` and the rows of its data
provider. The data provider's keys are also narrowed to non-falsy
strings, and its description references the test method with `{@see}`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Tests are not published in the developer docs, so the shapes can go in
the regular `@param` and `@return` tags directly, without a type alias
or `@phpstan-` prefixed tags.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Define the attribute shape once as a `Shadow_Attributes` type alias and
reference it from the plain `@param` and `@return` tags, rather than
repeating the shape for the test method's parameters and the data
provider's rows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@westonruter
westonruter marked this pull request as ready for review October 26, 2026 00:25
@westonruter
westonruter requested a review from ramonjd October 26, 2026 00:25
@github-actions

Copy link
Copy Markdown

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs.

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

Props westonruter.

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.

peterwilsoncc pushed a commit to peterwilsoncc/gutenberg-build that referenced this pull request Oct 26, 2026
…support()` (#68398)

* chore: remove always-true conditionals in `gutenberg_register_shadow_support()`

* Add the backport changelog entry for the core PR

The fix is being brought to core in WordPress/wordpress-develop#14862,
along with the related PHPStan fixes from three other Gutenberg PRs, which
share this entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: justlevine <justlevine@git.wordpress.org>
Co-authored-by: westonruter <westonruter@git.wordpress.org>

Source: WordPress/gutenberg@8a0ce0c
peterwilsoncc pushed a commit to peterwilsoncc/gutenberg-build that referenced this pull request Oct 26, 2026
…g_get_child_layout_style_rules()` (#68404)

* fix: cast `$parent_column_value` before exploding in gutenberg_render_layout_support_flag()

* Add the backport changelog entry for the core PR

The fix is being brought to core in WordPress/wordpress-develop#14862,
along with the related PHPStan fixes from three other Gutenberg PRs, which
share this entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: justlevine <justlevine@git.wordpress.org>
Co-authored-by: westonruter <westonruter@git.wordpress.org>
Co-authored-by: carolinan <poena@git.wordpress.org>

Source: WordPress/gutenberg@389149a
peterwilsoncc pushed a commit to peterwilsoncc/gutenberg-build that referenced this pull request Oct 26, 2026
…_hsla_string()` (#68405)

* fix: cast `$match[1]` to float in WP_DuotoneGutenberg::colord_parse_hsla_string()

* Add the backport changelog entry for the core PR

The fix is being brought to core in WordPress/wordpress-develop#14862,
along with the related PHPStan fixes from three other Gutenberg PRs, which
share this entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: justlevine <justlevine@git.wordpress.org>
Co-authored-by: westonruter <westonruter@git.wordpress.org>
Co-authored-by: carolinan <poena@git.wordpress.org>

Source: WordPress/gutenberg@4f99823
peterwilsoncc pushed a commit to peterwilsoncc/gutenberg-build that referenced this pull request Oct 26, 2026
…ons_for_node()` with core (#68407)

* docs: fix $metadata param type in WP_Theme_JSON_Gutenberg::get_feature_declarations_for_node()

* Document `$node` as an array too, as core does

Both parameters of `get_feature_declarations_for_node()` only ever receive
arrays. This syncs the `@param` tags with `WP_Theme_JSON` in WordPress
core as of r63426, which corrected both from `object` and noted that the
by-reference `$node` has its promoted features removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Narrow the types for get_feature_declarations_for_node()

Describe the shapes `WP_Theme_JSON_Gutenberg::get_feature_declarations_for_node()`
works with: the `selectors` it reads from the block metadata map each
feature to a selector string or to a map of subfeature selectors, the node
is a theme.json node keyed by feature, and the returned declarations are
keyed by selector. Each declaration list uses the same shape that
`get_state_declarations_with_text_clip_escapes()` already documents.

This resolves the PHPStan errors on mixed offsets and iteration inside the
method at the higher rule levels.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Fix indentation

* Type the block metadata as a string-keyed array

The block metadata passed to `get_feature_declarations_for_node()` is a
string-keyed array, just like the node, so say so in the plain `@param`
tag as well. The `@phpstan-param` shape still describes the `selectors`
key the method reads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Add the backport changelog entry for the core PR

The fix is being brought to core in WordPress/wordpress-develop#14862,
along with the related PHPStan fixes from three other Gutenberg PRs, which
share this entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Check that a node feature is an array before reading its subfeatures

`get_feature_declarations_for_node()` only checked that the feature's
selectors were an array before reading `$node[ $feature ][ $subfeature ]`,
but the node comes from theme.json data, where the feature value may be a
string or another non-array value. Such a value has no subfeatures, and
`empty()` already treated every lookup on it as empty, so skipping the
subfeature loop for it keeps the behavior the same while making the check
explicit.

This resolves the PHPStan error for accessing an offset on `mixed`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Read the subfeatures from a local copy of the feature styles

Checking `is_array( $node[ $feature ] )` was not enough for PHPStan,
since unsetting `$node[ $feature ][ $subfeature ]` inside the loop made it
forget the narrowed type on the next iteration. Copy the feature styles to
a local variable, read and unset the subfeatures there, and write the copy
back to the node after the loop, which leaves the node exactly as before.

This resolves the PHPStan error for accessing an offset on `mixed`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Fall back to an empty settings array, as core does

When the theme.json data has no `settings`, `get_styles_for_block()` and
`get_feature_declarations_for_node()` fell back to `null`, which was
then passed on as the settings to `compute_style_properties()` and the
other style helpers, all of which expect an array. Core has always fallen
back to an empty array here, so do the same.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Describe the shape of the theme.json data property

Give the `$theme_json` property a `@phpstan-var` shape for its top-level
`version`, `settings`, and `styles` keys, leaving it open to the others.
Reading `$this->theme_json['settings']` or `['styles']` then yields an
array rather than `mixed`, which resolves the PHPStan errors at the higher
rule levels where these are passed to `compute_style_properties()` and the
other style helpers, or have offsets read from them.

The methods that build the data still return a plain `array`, so the
assignments to the property are now reported instead, until those return
types are narrowed as well.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Describe the declarations returned by compute_style_properties()

Give `compute_style_properties()` a `@phpstan-return` with the same
declaration shape that `get_state_declarations_with_text_clip_escapes()`
documents. Its callers, including `get_feature_declarations_for_node()`,
then see a list of declarations rather than a plain array, which resolves
their PHPStan errors at the higher rule levels.

This moves some errors elsewhere at those levels: to the method's own body, whose property
names come from the untyped `$properties` parameter and whose font size
value may be `null` as far as PHPStan can tell, and to the callers that pass
a declaration value to `is_safe_css_declaration()`, which expects a string.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Pass the settings to get_state_base_styles() without a fallback

Now that `get_styles_for_block()` falls back to an empty settings array,
`$settings` is never `null`, so the `?? array()` when passing it to
`get_state_base_styles()` is redundant. Drop it, as core does.

This resolves the PHPStan error that the variable on the left side of
`??` always exists and is not nullable.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Sync the get_feature_declarations_for_node() comments with core

Bring over the documentation differences from
`WP_Theme_JSON::get_feature_declarations_for_node()` in core: the
punctuation in the summary, the `@since` tag, no blank line between the
`@param` and `@return` tags, and multi-line comments in the `/* */` style
instead of consecutive `//` lines. The method is now identical to core's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Remove the core @SInCE tag from get_feature_declarations_for_node()

The `@since 6.3.0` tag brought over from core refers to the WordPress
release that added the method, which is meaningless for the plugin's copy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: justlevine <justlevine@git.wordpress.org>
Co-authored-by: westonruter <westonruter@git.wordpress.org>
Co-authored-by: carolinan <poena@git.wordpress.org>

Source: WordPress/gutenberg@dd6d865

@ramonjd ramonjd left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for moving on this @westonruter

I ran the branch and tested the type fix scenarios. All LGTM

Scenario theme.json data Trunk This PR
Baseline none ✅ Renders ✅ Renders
Control: string selector blocks.test/ok.shadow ✅ Shadow on .wp-block-test-ok__inner ✅ Same
Block styles blocks.test/crash.shadow ❌ Fatal ✅ Shadow on .wp-block-test-crash
Pseudo-state (core/button :hover) blocks.core/button[':hover'].shadow ❌ Fatal ✅ Shadow on .wp-block-button .wp-block-button__link:hover
Block style variation blocks.test/crash.variations.raised.shadow ❌ Fatal ✅ Shadow on .wp-block-test-crash.is-style-raised--2
Variation breakpoint …variations.raised['@mobile'].shadow ❌ Fatal ✅ Same rule inside @media (width <= 480px)

Also checked the ports. They faithful to all four GB PRs.

Do you need anything else here? I'll give a ✅

wporg-sync pushed a commit that referenced this pull request Oct 26, 2026
These fixes were made in Gutenberg while resolving the PHPStan errors in the plugin, ahead of adding PHPStan to its CI, and are brought to core to keep the two codebases in sync. They were developed in:

* WordPress/gutenberg#68398
* WordPress/gutenberg#68404
* WordPress/gutenberg#68405
* WordPress/gutenberg#68407

The first one fixes a bug in core. Since r56046 the `array_key_exists()` checks in `wp_register_shadow_support()` have lacked their negation, so a block type with shadow support but no `style` or `shadow` attribute never had them registered, while one that already defined them had its definitions replaced, dropping any `default`. Gutenberg already had the negation. Tests are added for the attribute registration.

The others make implicit type conversions explicit in the layout and duotone code, and narrow the types used by `WP_Theme_JSON::get_feature_declarations_for_node()` so that it reports no errors even at PHPStan rule level 10. Since the node comes from `theme.json` data, that method now also checks that a feature is an array before reading its subfeatures. This fixes a fatal error ("Cannot unset string offsets") when a feature's value is a string, such as a `shadow`, and the block's selectors for that feature are a list rather than keyed by subfeature name, since each numeric key then reads a character of the string instead of a subfeature. The baseline entries for the resolved errors are removed.

Developed in #14862.
Follow-up to r56046, r56058, r56101, r62478, r63426.

Props justlevine, westonruter, poena, ramonopoly.
See #65817.


git-svn-id: https://develop.svn.wordpress.org/trunk@64727 602fd350-edb4-49c9-b593-d223f7449a82
wporg-sync pushed a commit to WordPress/WordPress that referenced this pull request Oct 26, 2026
These fixes were made in Gutenberg while resolving the PHPStan errors in the plugin, ahead of adding PHPStan to its CI, and are brought to core to keep the two codebases in sync. They were developed in:

* WordPress/gutenberg#68398
* WordPress/gutenberg#68404
* WordPress/gutenberg#68405
* WordPress/gutenberg#68407

The first one fixes a bug in core. Since r56046 the `array_key_exists()` checks in `wp_register_shadow_support()` have lacked their negation, so a block type with shadow support but no `style` or `shadow` attribute never had them registered, while one that already defined them had its definitions replaced, dropping any `default`. Gutenberg already had the negation. Tests are added for the attribute registration.

The others make implicit type conversions explicit in the layout and duotone code, and narrow the types used by `WP_Theme_JSON::get_feature_declarations_for_node()` so that it reports no errors even at PHPStan rule level 10. Since the node comes from `theme.json` data, that method now also checks that a feature is an array before reading its subfeatures. This fixes a fatal error ("Cannot unset string offsets") when a feature's value is a string, such as a `shadow`, and the block's selectors for that feature are a list rather than keyed by subfeature name, since each numeric key then reads a character of the string instead of a subfeature. The baseline entries for the resolved errors are removed.

Developed in WordPress/wordpress-develop#14862.
Follow-up to r56046, r56058, r56101, r62478, r63426.

Props justlevine, westonruter, poena, ramonopoly.
See #65817.

Built from https://develop.svn.wordpress.org/trunk@64727


git-svn-id: http://core.svn.wordpress.org/trunk@63877 1a063a9b-81f0-0310-95a4-ce76da25c4cd
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants