From 702344e38350bc81d7338bff4bd04664b8b7b3f7 Mon Sep 17 00:00:00 2001 From: Felix Arntz Date: Fri, 11 Nov 2022 01:50:11 +0000 Subject: [PATCH] Editor: Improve performance of `WP_Theme_JSON` class by reducing usage of expensive array functions. In many scenarios array functions are more expensive than using simpler `for` or `foreach` loops. This changeset results in roughly 4% faster `wp_head` execution time for both block themes and classic themes. While this may seem like a small win, it is a worthwhile enhancement and only one part of several other little performance tweaks which are being worked on to improve performance of `theme.json` parsing further. Props aristath, desrosj, jrf, spacedmonkey. Fixes #56974. See #57067. Built from https://develop.svn.wordpress.org/trunk@54804 git-svn-id: http://core.svn.wordpress.org/trunk@54356 1a063a9b-81f0-0310-95a4-ce76da25c4cd --- wp-includes/class-wp-theme-json.php | 121 +++++++++++++++++++++------- wp-includes/version.php | 2 +- 2 files changed, 91 insertions(+), 32 deletions(-) diff --git a/wp-includes/class-wp-theme-json.php b/wp-includes/class-wp-theme-json.php index df5bd20d84..d93b4eea67 100644 --- a/wp-includes/class-wp-theme-json.php +++ b/wp-includes/class-wp-theme-json.php @@ -461,6 +461,8 @@ class WP_Theme_JSON { public static function get_element_class_name( $element ) { $class_name = ''; + // TODO: Replace array_key_exists() with isset() check once WordPress drops + // support for PHP 5.6. See https://core.trac.wordpress.org/ticket/57067. if ( array_key_exists( $element, static::__EXPERIMENTAL_ELEMENT_CLASS_NAMES ) ) { $class_name = static::__EXPERIMENTAL_ELEMENT_CLASS_NAMES[ $element ]; } @@ -519,7 +521,10 @@ class WP_Theme_JSON { $nodes = static::get_setting_nodes( $this->theme_json ); foreach ( $nodes as $node ) { foreach ( static::PRESETS_METADATA as $preset_metadata ) { - $path = array_merge( $node['path'], $preset_metadata['path'] ); + $path = $node['path']; + foreach ( $preset_metadata['path'] as $subpath ) { + $path[] = $subpath; + } $preset = _wp_array_get( $this->theme_json, $path, null ); if ( null !== $preset ) { // If the preset is not already keyed by origin. @@ -608,6 +613,7 @@ class WP_Theme_JSON { */ $styles_non_top_level = static::VALID_STYLES; foreach ( array_keys( $styles_non_top_level ) as $section ) { + // array_key_exists() needs to be used instead of isset() because the value can be null. if ( array_key_exists( $section, $styles_non_top_level ) && is_array( $styles_non_top_level[ $section ] ) ) { foreach ( array_keys( $styles_non_top_level[ $section ] ) as $prop ) { if ( 'top' === $styles_non_top_level[ $section ][ $prop ] ) { @@ -631,6 +637,8 @@ class WP_Theme_JSON { foreach ( $valid_element_names as $element ) { $schema_styles_elements[ $element ] = $styles_non_top_level; + // TODO: Replace array_key_exists() with isset() check once WordPress drops + // support for PHP 5.6. See https://core.trac.wordpress.org/ticket/57067. if ( array_key_exists( $element, static::VALID_ELEMENT_PSEUDO_SELECTORS ) ) { foreach ( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $element ] as $pseudo_selector ) { $schema_styles_elements[ $element ][ $pseudo_selector ] = $styles_non_top_level; @@ -1273,8 +1281,12 @@ class WP_Theme_JSON { $selector = $metadata['selector']; - $node = _wp_array_get( $this->theme_json, $metadata['path'], array() ); - $declarations = array_merge( static::compute_preset_vars( $node, $origins ), static::compute_theme_vars( $node ) ); + $node = _wp_array_get( $this->theme_json, $metadata['path'], array() ); + $declarations = static::compute_preset_vars( $node, $origins ); + $theme_vars_declarations = static::compute_theme_vars( $node ); + foreach ( $theme_vars_declarations as $theme_vars_declaration ) { + $declarations[] = $theme_vars_declaration; + } $stylesheet .= static::to_ruleset( $selector, $declarations ); } @@ -1601,11 +1613,11 @@ class WP_Theme_JSON { ); if ( is_array( $value ) ) { - $new_prefix = $new_key . $token; - $result = array_merge( - $result, - static::flatten_tree( $value, $new_prefix, $token ) - ); + $new_prefix = $new_key . $token; + $flattened_subtree = static::flatten_tree( $value, $new_prefix, $token ); + foreach ( $flattened_subtree as $subtree_key => $subtree_value ) { + $result[ $subtree_key ] = $subtree_value; + } } else { $result[ $new_key ] = $value; } @@ -1667,6 +1679,8 @@ class WP_Theme_JSON { if ( is_array( $value_path ) ) { $path_string = implode( '.', $value_path ); if ( + // TODO: Replace array_key_exists() with isset() check once WordPress drops + // support for PHP 5.6. See https://core.trac.wordpress.org/ticket/57067. array_key_exists( $path_string, static::PROTECTED_PROPERTIES ) && _wp_array_get( $settings, static::PROTECTED_PROPERTIES[ $path_string ], null ) === null ) { @@ -1742,7 +1756,7 @@ class WP_Theme_JSON { * where the values is an array with a "ref" key, pointing to a path. * For example: { "ref": "style.color.background" } => "#fff". */ - if ( is_array( $value ) && array_key_exists( 'ref', $value ) ) { + if ( is_array( $value ) && isset( $value['ref'] ) ) { $value_path = explode( '.', $value['ref'] ); $ref_value = _wp_array_get( $theme_json, $value_path ); // Only use the ref value if we find anything. @@ -1750,7 +1764,7 @@ class WP_Theme_JSON { $value = $ref_value; } - if ( is_array( $ref_value ) && array_key_exists( 'ref', $ref_value ) ) { + if ( is_array( $ref_value ) && isset( $ref_value['ref'] ) ) { $path_string = json_encode( $path ); $ref_value_string = json_encode( $ref_value ); _doing_it_wrong( @@ -1886,6 +1900,8 @@ class WP_Theme_JSON { ); // Handle any pseudo selectors for the element. + // TODO: Replace array_key_exists() with isset() check once WordPress drops + // support for PHP 5.6. See https://core.trac.wordpress.org/ticket/57067. if ( array_key_exists( $element, static::VALID_ELEMENT_PSEUDO_SELECTORS ) ) { foreach ( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $element ] as $pseudo_selector ) { @@ -1905,7 +1921,10 @@ class WP_Theme_JSON { return $nodes; } - $nodes = array_merge( $nodes, static::get_block_nodes( $theme_json ) ); + $block_nodes = static::get_block_nodes( $theme_json ); + foreach ( $block_nodes as $block_node ) { + $nodes[] = $block_node; + } /** * Filters the list of style nodes with metadata. @@ -1982,6 +2001,8 @@ class WP_Theme_JSON { ); // Handle any pseudo selectors for the element. + // TODO: Replace array_key_exists() with isset() check once WordPress drops + // support for PHP 5.6. See https://core.trac.wordpress.org/ticket/57067. if ( array_key_exists( $element, static::VALID_ELEMENT_PSEUDO_SELECTORS ) ) { foreach ( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $element ] as $pseudo_selector ) { if ( isset( $theme_json['styles']['blocks'][ $name ]['elements'][ $element ][ $pseudo_selector ] ) ) { @@ -2035,7 +2056,9 @@ class WP_Theme_JSON { // the feature selector. This may occur when multiple block // support features use the same custom selector. if ( isset( $feature_declarations[ $feature_selector ] ) ) { - $feature_declarations[ $feature_selector ] = array_merge( $feature_declarations[ $feature_selector ], $new_feature_declarations ); + foreach ( $new_feature_declarations as $new_feature_declaration ) { + $feature_declarations[ $feature_selector ][] = $feature_declaration; + } } else { $feature_declarations[ $feature_selector ] = $new_feature_declarations; } @@ -2059,6 +2082,8 @@ class WP_Theme_JSON { $element_pseudo_allowed = array(); + // TODO: Replace array_key_exists() with isset() check once WordPress drops + // support for PHP 5.6. See https://core.trac.wordpress.org/ticket/57067. if ( array_key_exists( $current_element, static::VALID_ELEMENT_PSEUDO_SELECTORS ) ) { $element_pseudo_allowed = static::VALID_ELEMENT_PSEUDO_SELECTORS[ $current_element ]; } @@ -2084,6 +2109,8 @@ class WP_Theme_JSON { * Otherwise just compute the styles for the default selector as normal. */ if ( $pseudo_selector && isset( $node[ $pseudo_selector ] ) && + // TODO: Replace array_key_exists() with isset() check once WordPress drops + // support for PHP 5.6. See https://core.trac.wordpress.org/ticket/57067. array_key_exists( $current_element, static::VALID_ELEMENT_PSEUDO_SELECTORS ) && in_array( $pseudo_selector, static::VALID_ELEMENT_PSEUDO_SELECTORS[ $current_element ], true ) ) { @@ -2283,11 +2310,11 @@ class WP_Theme_JSON { $nodes = static::get_setting_nodes( $incoming_data ); $slugs_global = static::get_default_slugs( $this->theme_json, array( 'settings' ) ); foreach ( $nodes as $node ) { - $slugs_node = static::get_default_slugs( $this->theme_json, $node['path'] ); - $slugs = array_merge_recursive( $slugs_global, $slugs_node ); - // Replace the spacing.units. - $path = array_merge( $node['path'], array( 'spacing', 'units' ) ); + $path = $node['path']; + $path[] = 'spacing'; + $path[] = 'units'; + $content = _wp_array_get( $incoming_data, $path, null ); if ( isset( $content ) ) { _wp_array_set( $this->theme_json, $path, $content ); @@ -2298,19 +2325,25 @@ class WP_Theme_JSON { $override_preset = ! static::get_metadata_boolean( $this->theme_json['settings'], $preset['prevent_override'], true ); foreach ( static::VALID_ORIGINS as $origin ) { - $base_path = array_merge( $node['path'], $preset['path'] ); - $path = array_merge( $base_path, array( $origin ) ); - $content = _wp_array_get( $incoming_data, $path, null ); + $base_path = $node['path']; + foreach ( $preset['path'] as $leaf ) { + $base_path[] = $leaf; + } + + $path = $base_path; + $path[] = $origin; + + $content = _wp_array_get( $incoming_data, $path, null ); if ( ! isset( $content ) ) { continue; } if ( 'theme' === $origin && $preset['use_default_names'] ) { - foreach ( $content as &$item ) { - if ( ! array_key_exists( 'name', $item ) ) { + foreach ( $content as $key => $item ) { + if ( ! isset( $item['name'] ) ) { $name = static::get_name_from_defaults( $item['slug'], $base_path ); if ( null !== $name ) { - $item['name'] = $name; + $content[ $key ]['name'] = $name; } } } @@ -2322,6 +2355,9 @@ class WP_Theme_JSON { ) { _wp_array_set( $this->theme_json, $path, $content ); } else { + $slugs_node = static::get_default_slugs( $this->theme_json, $node['path'] ); + $slugs = array_merge_recursive( $slugs_global, $slugs_node ); + $slugs_for_preset = _wp_array_get( $slugs, $preset['path'], array() ); $content = static::filter_slugs( $content, $slugs_for_preset ); _wp_array_set( $this->theme_json, $path, $content ); @@ -2434,7 +2470,12 @@ class WP_Theme_JSON { $slugs = array(); foreach ( static::PRESETS_METADATA as $metadata ) { - $path = array_merge( $node_path, $metadata['path'], array( 'default' ) ); + $path = $node_path; + foreach ( $metadata['path'] as $leaf ) { + $path[] = $leaf; + } + $path[] = 'default'; + $preset = _wp_array_get( $data, $path, null ); if ( ! isset( $preset ) ) { continue; @@ -2463,7 +2504,8 @@ class WP_Theme_JSON { * @return string|null */ protected function get_name_from_defaults( $slug, $base_path ) { - $path = array_merge( $base_path, array( 'default' ) ); + $path = $base_path; + $path[] = 'default'; $default_content = _wp_array_get( $this->theme_json, $path, null ); if ( ! $default_content ) { return null; @@ -2539,6 +2581,8 @@ class WP_Theme_JSON { * $output is stripped of pseudo selectors. Re-add and process them * or insecure styles here. */ + // TODO: Replace array_key_exists() with isset() check once WordPress drops + // support for PHP 5.6. See https://core.trac.wordpress.org/ticket/57067. if ( array_key_exists( $current_element, static::VALID_ELEMENT_PSEUDO_SELECTORS ) ) { foreach ( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $current_element ] as $pseudo_selector ) { if ( isset( $input[ $pseudo_selector ] ) ) { @@ -2593,8 +2637,9 @@ class WP_Theme_JSON { $output = array(); foreach ( static::PRESETS_METADATA as $preset_metadata ) { foreach ( static::VALID_ORIGINS as $origin ) { - $path_with_origin = array_merge( $preset_metadata['path'], array( $origin ) ); - $presets = _wp_array_get( $input, $path_with_origin, null ); + $path_with_origin = $preset_metadata['path']; + $path_with_origin[] = $origin; + $presets = _wp_array_get( $input, $path_with_origin, null ); if ( null === $presets ) { continue; } @@ -2839,7 +2884,10 @@ class WP_Theme_JSON { */ foreach ( $nodes as $node ) { foreach ( static::PRESETS_METADATA as $preset_metadata ) { - $path = array_merge( $node['path'], $preset_metadata['path'] ); + $path = $node['path']; + foreach ( $preset_metadata['path'] as $preset_metadata_path ) { + $path[] = $preset_metadata_path; + } $preset = _wp_array_get( $output, $path, null ); if ( null === $preset ) { continue; @@ -2873,7 +2921,10 @@ class WP_Theme_JSON { foreach ( $nodes as $node ) { $all_opt_ins_are_set = true; foreach ( static::APPEARANCE_TOOLS_OPT_INS as $opt_in_path ) { - $full_path = array_merge( $node['path'], $opt_in_path ); + $full_path = $node['path']; + foreach ( $opt_in_path as $opt_in_path_item ) { + $full_path[] = $opt_in_path_item; + } // Use "unset prop" as a marker instead of "null" because // "null" can be a valid value for some props (e.g. blockGap). $opt_in_value = _wp_array_get( $output, $full_path, 'unset prop' ); @@ -2884,9 +2935,14 @@ class WP_Theme_JSON { } if ( $all_opt_ins_are_set ) { - _wp_array_set( $output, array_merge( $node['path'], array( 'appearanceTools' ) ), true ); + $node_path_with_appearance_tools = $node['path']; + $node_path_with_appearance_tools[] = 'appearanceTools'; + _wp_array_set( $output, $node_path_with_appearance_tools, true ); foreach ( static::APPEARANCE_TOOLS_OPT_INS as $opt_in_path ) { - $full_path = array_merge( $node['path'], $opt_in_path ); + $full_path = $node['path']; + foreach ( $opt_in_path as $opt_in_path_item ) { + $full_path[] = $opt_in_path_item; + } // Use "unset prop" as a marker instead of "null" because // "null" can be a valid value for some props (e.g. blockGap). $opt_in_value = _wp_array_get( $output, $full_path, 'unset prop' ); @@ -3037,7 +3093,10 @@ class WP_Theme_JSON { $slug += 10; } - $spacing_sizes = array_merge( $below_sizes, $above_sizes ); + $spacing_sizes = $below_sizes; + foreach ( $above_sizes as $above_sizes_item ) { + $spacing_sizes[] = $above_sizes_item; + } // If there are 7 or less steps in the scale revert to numbers for labels instead of t-shirt sizes. if ( $spacing_scale['steps'] <= 7 ) { diff --git a/wp-includes/version.php b/wp-includes/version.php index 8567d156df..2350c21884 100644 --- a/wp-includes/version.php +++ b/wp-includes/version.php @@ -16,7 +16,7 @@ * * @global string $wp_version */ -$wp_version = '6.2-alpha-54803'; +$wp_version = '6.2-alpha-54804'; /** * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.