Make WordPress Core


Ignore:
Timestamp:
10/12/2026 08:18:48 AM (33 hours ago)
Author:
ramonopoly
Message:

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.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/class-wp-theme-json.php

    r64304 r64309  
    11411141                $scale_path    = array( 'settings', 'spacing', 'spacingScale' );
    11421142                $spacing_scale = _wp_array_get( $this->theme_json, $scale_path, null );
    1143                 if ( null !== $spacing_scale ) {
     1143                if ( is_array( $spacing_scale ) ) {
    11441144                        // If the spacingScale is not already keyed by origin.
    11451145                        if ( empty( array_intersect( array_keys( $spacing_scale ), static::VALID_ORIGINS ) ) ) {
    … …  
    12151215                                $block_level_data = $new_theme_json['styles']['blocks'][ $block_type ]['variations'][ $variation_name ] ?? array();
    12161216                                $top_level_data   = $variations[ $variation_name ] ?? array();
    1217                                 $merged_data      = array_replace_recursive( $top_level_data, $block_level_data );
     1217                                if ( ! is_array( $block_level_data ) || ! is_array( $top_level_data ) ) {
     1218                                        continue;
     1219                                }
     1220                                $merged_data = array_replace_recursive( $top_level_data, $block_level_data );
    12181221                                if ( ! empty( $merged_data ) ) {
    12191222                                        _wp_array_set( $new_theme_json, array( 'styles', 'blocks', $block_type, 'variations', $variation_name ), $merged_data );
    … …  
    14941497                        $variation_pseudo_selectors = array();
    14951498                        foreach ( $input['blockTypes'] as $variation_block_type ) {
    1496                                 if ( isset( static::VALID_BLOCK_PSEUDO_SELECTORS[ $variation_block_type ] ) ) {
     1499                                if ( is_string( $variation_block_type ) && isset( static::VALID_BLOCK_PSEUDO_SELECTORS[ $variation_block_type ] ) ) {
    14971500                                        $variation_pseudo_selectors = array_merge(
    14981501                                                $variation_pseudo_selectors,
    … …  
    21022105                if ( in_array( 'custom-css', $types, true ) ) {
    21032106                        // Add the global styles root CSS.
    2104                         $stylesheet .= _wp_array_get( $this->theme_json, array( 'styles', 'css' ) );
     2107                        $root_css = $this->theme_json['styles']['css'] ?? '';
     2108                        if ( is_string( $root_css ) ) {
     2109                                $stylesheet .= $root_css;
     2110                        }
    21052111                }
    21062112
    … …  
    21222128                $processed_css = '';
    21232129
    2124                 if ( empty( $css ) ) {
     2130                if ( empty( $css ) || ! is_string( $css ) ) {
    21252131                        return $processed_css;
    21262132                }
    … …  
    32423248                 * the URL takes precedence and the ref is ignored.
    32433249                 */
    3244                 if ( is_array( $value ) && isset( $value['ref'] ) ) {
     3250                if ( is_array( $value ) && isset( $value['ref'] ) && is_string( $value['ref'] ) ) {
    32453251                        $value_path = explode( '.', $value['ref'] );
    32463252                        $ref_value  = _wp_array_get( $theme_json, $value_path );
    … …  
    44324438
    44334439                        // Apply the base spacing scale to the current layer.
    4434                         $base_spacing_scale      = _wp_array_get( $this->theme_json, $scale_path, array() );
    4435                         $flattened_spacing_scale = array_replace( $flattened_spacing_scale, $base_spacing_scale );
     4440                        $base_spacing_scale = _wp_array_get( $this->theme_json, $scale_path, array() );
     4441                        if ( is_array( $base_spacing_scale ) ) {
     4442                                $flattened_spacing_scale = array_replace( $flattened_spacing_scale, $base_spacing_scale );
     4443                        }
    44364444
    44374445                        $spacing_scale = _wp_array_get( $incoming_data, $scale_path, null );
    4438                         if ( ! isset( $spacing_scale ) ) {
     4446                        if ( ! is_array( $spacing_scale ) ) {
    44394447                                continue;
    44404448                        }
    … …  
    55705578                        ! is_numeric( $spacing_scale['mediumStep'] ) ||
    55715579                        ! isset( $spacing_scale['unit'] ) ||
     5580                        ! is_string( $spacing_scale['unit'] ) ||
    55725581                        ! isset( $spacing_scale['operator'] ) ||
    55735582                        ( '+' !== $spacing_scale['operator'] && '*' !== $spacing_scale['operator'] ) ||
Note: See TracChangeset for help on using the changeset viewer.