Commit a505e034de for wordpress.org

commit a505e034de087aed77ed45219ef9744e21e1982f
Author: westonruter <westonruter@git.wordpress.org>
Date:   Mon Oct 26 02:22:49 2026 +0000

    Code Quality: Sync PHPStan fixes from Gutenberg.

    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:

    * https://github.com/WordPress/gutenberg/pull/68398
    * https://github.com/WordPress/gutenberg/pull/68404
    * https://github.com/WordPress/gutenberg/pull/68405
    * https://github.com/WordPress/gutenberg/pull/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 https://github.com/WordPress/wordpress-develop/pull/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

diff --git a/wp-includes/block-supports/layout.php b/wp-includes/block-supports/layout.php
index dc3b56c017..3036c581fd 100644
--- a/wp-includes/block-supports/layout.php
+++ b/wp-includes/block-supports/layout.php
@@ -237,7 +237,7 @@ function wp_get_child_layout_style_rules( $selector, $child_layout, $parent_layo
 		$column_start_number = floatval( $column_start );
 		$parent_column_width = $minimum_column_width ? $minimum_column_width : '12rem';
 		$parent_column_value = floatval( $parent_column_width );
-		$parent_column_unit  = explode( $parent_column_value, $parent_column_width );
+		$parent_column_unit  = explode( (string) $parent_column_value, $parent_column_width );

 		$num_cols_to_break_at = 2;
 		if ( $column_span_number && $column_start_number ) {
diff --git a/wp-includes/block-supports/shadow.php b/wp-includes/block-supports/shadow.php
index 4c09ce9d08..272eb87407 100644
--- a/wp-includes/block-supports/shadow.php
+++ b/wp-includes/block-supports/shadow.php
@@ -25,13 +25,13 @@ function wp_register_shadow_support( $block_type ) {
 		$block_type->attributes = array();
 	}

-	if ( array_key_exists( 'style', $block_type->attributes ) ) {
+	if ( ! array_key_exists( 'style', $block_type->attributes ) ) {
 		$block_type->attributes['style'] = array(
 			'type' => 'object',
 		);
 	}

-	if ( array_key_exists( 'shadow', $block_type->attributes ) ) {
+	if ( ! array_key_exists( 'shadow', $block_type->attributes ) ) {
 		$block_type->attributes['shadow'] = array(
 			'type' => 'string',
 		);
diff --git a/wp-includes/class-wp-duotone.php b/wp-includes/class-wp-duotone.php
index 08beac3cd4..b254e3c95b 100644
--- a/wp-includes/class-wp-duotone.php
+++ b/wp-includes/class-wp-duotone.php
@@ -496,7 +496,7 @@ class WP_Duotone {

 		$hsla = self::colord_clamp_hsla(
 			array(
-				'h' => self::colord_parse_hue( $match[1], $match[2] ),
+				'h' => self::colord_parse_hue( (float) $match[1], $match[2] ),
 				's' => (float) $match[3],
 				'l' => (float) $match[4],
 				'a' => '' === $match[5] ? 1 : (float) $match[5] / ( $match[6] ? 100 : 1 ),
diff --git a/wp-includes/class-wp-theme-json.php b/wp-includes/class-wp-theme-json.php
index 34c737bb0d..e3f0721343 100644
--- a/wp-includes/class-wp-theme-json.php
+++ b/wp-includes/class-wp-theme-json.php
@@ -24,6 +24,7 @@ class WP_Theme_JSON {
 	 *
 	 * @since 5.8.0
 	 * @var array
+	 * @phpstan-var array{ version?: int, settings?: array<string, mixed>, styles?: array<string, mixed>, ...<string, mixed> }
 	 */
 	protected $theme_json = null;

@@ -3187,6 +3188,8 @@ class WP_Theme_JSON {
 	 * @param string $selector         The style block selector.
 	 * @param bool   $use_root_padding Whether to add custom properties at root level.
 	 * @return array Returns the modified $declarations.
+	 *
+	 * @phpstan-return list<array{ name: non-falsy-string, value: scalar }>
 	 */
 	protected static function compute_style_properties( $styles, $settings = array(), $properties = null, $theme_json = null, $selector = null, $use_root_padding = null ) {
 		if ( empty( $styles ) ) {
@@ -6052,11 +6055,14 @@ class WP_Theme_JSON {
 	 *
 	 * @since 6.3.0
 	 *
-	 * @param array $metadata The related block metadata containing selectors.
-	 * @param array $node     A merged theme.json node for block or variation. Features
-	 *                        promoted to their own selector are removed from it.
-	 * @return array The style declarations for the node's features with custom
-	 *               selectors.
+	 * @param array<string, mixed> $metadata The related block metadata containing selectors.
+	 * @param array<string, mixed> $node     A merged theme.json node for block or variation. Features
+	 *                                       promoted to their own selector are removed from it.
+	 * @return array<string, array[]> The style declarations for the node's features with custom
+	 *                                selectors, keyed by selector.
+	 *
+	 * @phpstan-param array{ selectors?: array<string, string|array<string, string>> } $metadata
+	 * @phpstan-return array<string, list<array{ name: non-falsy-string, value: scalar }>>
 	 */
 	protected function get_feature_declarations_for_node( $metadata, &$node ) {
 		$declarations = array();
@@ -6076,9 +6082,10 @@ class WP_Theme_JSON {
 				continue;
 			}

-			if ( is_array( $feature_selectors ) ) {
+			if ( is_array( $feature_selectors ) && is_array( $node[ $feature ] ) ) {
+				$feature_styles = $node[ $feature ];
 				foreach ( $feature_selectors as $subfeature => $subfeature_selector ) {
-					if ( 'root' === $subfeature || empty( $node[ $feature ][ $subfeature ] ) ) {
+					if ( 'root' === $subfeature || empty( $feature_styles[ $subfeature ] ) ) {
 						continue;
 					}

@@ -6088,7 +6095,7 @@ class WP_Theme_JSON {
 					 */
 					$subfeature_node = array(
 						$feature => array(
-							$subfeature => $node[ $feature ][ $subfeature ],
+							$subfeature => $feature_styles[ $subfeature ],
 						),
 					);

@@ -6109,8 +6116,9 @@ class WP_Theme_JSON {
 					 * styles will be included under its own selector not the
 					 * block's.
 					 */
-					unset( $node[ $feature ][ $subfeature ] );
+					unset( $feature_styles[ $subfeature ] );
 				}
+				$node[ $feature ] = $feature_styles;
 			}

 			/*
diff --git a/wp-includes/version.php b/wp-includes/version.php
index 5fdac596ab..c42ca99396 100644
--- a/wp-includes/version.php
+++ b/wp-includes/version.php
@@ -16,7 +16,7 @@
  *
  * @global string $wp_version
  */
-$wp_version = '7.2-beta1-64701';
+$wp_version = '7.2-beta1-64727';

 /**
  * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.