Commit 467a6b3200 for wordpress.org

commit 467a6b3200e942fbe5c8160845fc333c7a00b5ce
Author: ramonopoly <ramonopoly@git.wordpress.org>
Date:   Mon Oct 12 08:19:51 2026 +0000

    Editor: Ignore theme.json values of unexpected types

    This commit prevents `WP_Theme_JSON` from throwing fatal TypeErrors when theme.json or global styles data contains values of the wrong type.

    Those values could be user- or AI-generated.

    The checks in this commit cover spacing scale arrays, block variation selectors, and custom block CSS processing.

    The motivation arose from a real-life PHP fatal where `WP_Theme_JSON::process_blocks_custom_css` attempted to explode a non-string.

    Developed in: https://github.com/WordPress/wordpress-develop/pull/14190

    Props ramonopoly, aaronrobertshaw, andrewserong.

    Fixes #66286.


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


    git-svn-id: http://core.svn.wordpress.org/trunk@63459 1a063a9b-81f0-0310-95a4-ce76da25c4cd

diff --git a/wp-includes/class-wp-theme-json.php b/wp-includes/class-wp-theme-json.php
index c17f2b1b07..2f80a256b8 100644
--- a/wp-includes/class-wp-theme-json.php
+++ b/wp-includes/class-wp-theme-json.php
@@ -1140,7 +1140,7 @@ class WP_Theme_JSON {
 		// In addition to presets, spacingScale (which generates presets) is also keyed by origin.
 		$scale_path    = array( 'settings', 'spacing', 'spacingScale' );
 		$spacing_scale = _wp_array_get( $this->theme_json, $scale_path, null );
-		if ( null !== $spacing_scale ) {
+		if ( is_array( $spacing_scale ) ) {
 			// If the spacingScale is not already keyed by origin.
 			if ( empty( array_intersect( array_keys( $spacing_scale ), static::VALID_ORIGINS ) ) ) {
 				_wp_array_set( $this->theme_json, $scale_path, array( $origin => $spacing_scale ) );
@@ -1214,7 +1214,10 @@ class WP_Theme_JSON {
 			foreach ( $registered_variations as $variation_name ) {
 				$block_level_data = $new_theme_json['styles']['blocks'][ $block_type ]['variations'][ $variation_name ] ?? array();
 				$top_level_data   = $variations[ $variation_name ] ?? array();
-				$merged_data      = array_replace_recursive( $top_level_data, $block_level_data );
+				if ( ! is_array( $block_level_data ) || ! is_array( $top_level_data ) ) {
+					continue;
+				}
+				$merged_data = array_replace_recursive( $top_level_data, $block_level_data );
 				if ( ! empty( $merged_data ) ) {
 					_wp_array_set( $new_theme_json, array( 'styles', 'blocks', $block_type, 'variations', $variation_name ), $merged_data );
 				}
@@ -1493,7 +1496,7 @@ class WP_Theme_JSON {
 		if ( ! empty( $input['blockTypes'] ) && is_array( $input['blockTypes'] ) ) {
 			$variation_pseudo_selectors = array();
 			foreach ( $input['blockTypes'] as $variation_block_type ) {
-				if ( isset( static::VALID_BLOCK_PSEUDO_SELECTORS[ $variation_block_type ] ) ) {
+				if ( is_string( $variation_block_type ) && isset( static::VALID_BLOCK_PSEUDO_SELECTORS[ $variation_block_type ] ) ) {
 					$variation_pseudo_selectors = array_merge(
 						$variation_pseudo_selectors,
 						static::VALID_BLOCK_PSEUDO_SELECTORS[ $variation_block_type ]
@@ -2101,7 +2104,10 @@ class WP_Theme_JSON {
 		// Load the custom CSS last so it has the highest specificity.
 		if ( in_array( 'custom-css', $types, true ) ) {
 			// Add the global styles root CSS.
-			$stylesheet .= _wp_array_get( $this->theme_json, array( 'styles', 'css' ) );
+			$root_css = $this->theme_json['styles']['css'] ?? '';
+			if ( is_string( $root_css ) ) {
+				$stylesheet .= $root_css;
+			}
 		}

 		return $stylesheet;
@@ -2121,7 +2127,7 @@ class WP_Theme_JSON {
 	public static function process_blocks_custom_css( $css, $selector ) {
 		$processed_css = '';

-		if ( empty( $css ) ) {
+		if ( empty( $css ) || ! is_string( $css ) ) {
 			return $processed_css;
 		}

@@ -3241,7 +3247,7 @@ class WP_Theme_JSON {
 		 * In the case of backgroundImage, if both a ref and a URL are present in the value,
 		 * the URL takes precedence and the ref is ignored.
 		 */
-		if ( is_array( $value ) && isset( $value['ref'] ) ) {
+		if ( is_array( $value ) && isset( $value['ref'] ) && is_string( $value['ref'] ) ) {
 			$value_path = explode( '.', $value['ref'] );
 			$ref_value  = _wp_array_get( $theme_json, $value_path );
 			// Background Image refs can refer to a string or an array containing a URL string.
@@ -4431,11 +4437,13 @@ class WP_Theme_JSON {
 			$scale_path = array( 'settings', 'spacing', 'spacingScale', $origin );

 			// Apply the base spacing scale to the current layer.
-			$base_spacing_scale      = _wp_array_get( $this->theme_json, $scale_path, array() );
-			$flattened_spacing_scale = array_replace( $flattened_spacing_scale, $base_spacing_scale );
+			$base_spacing_scale = _wp_array_get( $this->theme_json, $scale_path, array() );
+			if ( is_array( $base_spacing_scale ) ) {
+				$flattened_spacing_scale = array_replace( $flattened_spacing_scale, $base_spacing_scale );
+			}

 			$spacing_scale = _wp_array_get( $incoming_data, $scale_path, null );
-			if ( ! isset( $spacing_scale ) ) {
+			if ( ! is_array( $spacing_scale ) ) {
 				continue;
 			}

@@ -5569,6 +5577,7 @@ class WP_Theme_JSON {
 			! isset( $spacing_scale['mediumStep'] ) ||
 			! is_numeric( $spacing_scale['mediumStep'] ) ||
 			! isset( $spacing_scale['unit'] ) ||
+			! is_string( $spacing_scale['unit'] ) ||
 			! isset( $spacing_scale['operator'] ) ||
 			( '+' !== $spacing_scale['operator'] && '*' !== $spacing_scale['operator'] ) ||
 			! isset( $spacing_scale['increment'] ) ||
diff --git a/wp-includes/version.php b/wp-includes/version.php
index 1b34d7e5fd..52d32668f5 100644
--- a/wp-includes/version.php
+++ b/wp-includes/version.php
@@ -16,7 +16,7 @@
  *
  * @global string $wp_version
  */
-$wp_version = '7.2-alpha-64308';
+$wp_version = '7.2-alpha-64309';

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