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.
Merges [54804] to the 6.1 branch.
Fixes #56974.
See #57067.

Built from https://develop.svn.wordpress.org/branches/6.1@54805


git-svn-id: http://core.svn.wordpress.org/branches/6.1@54357 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
Felix Arntz 2022-11-11 02:07:13 +00:00
parent 309d6d1df8
commit 15e9ecd961
2 changed files with 91 additions and 32 deletions

View File

@ -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 ) {

View File

@ -16,7 +16,7 @@
*
* @global string $wp_version
*/
$wp_version = '6.1.1-alpha-54800';
$wp_version = '6.1.1-alpha-54805';
/**
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.