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
This commit is contained in:
Felix Arntz 2022-11-11 01:50:11 +00:00
parent 912ac10f4b
commit 702344e383
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 ) { public static function get_element_class_name( $element ) {
$class_name = ''; $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 ) ) { if ( array_key_exists( $element, static::__EXPERIMENTAL_ELEMENT_CLASS_NAMES ) ) {
$class_name = static::__EXPERIMENTAL_ELEMENT_CLASS_NAMES[ $element ]; $class_name = static::__EXPERIMENTAL_ELEMENT_CLASS_NAMES[ $element ];
} }
@ -519,7 +521,10 @@ class WP_Theme_JSON {
$nodes = static::get_setting_nodes( $this->theme_json ); $nodes = static::get_setting_nodes( $this->theme_json );
foreach ( $nodes as $node ) { foreach ( $nodes as $node ) {
foreach ( static::PRESETS_METADATA as $preset_metadata ) { 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 ); $preset = _wp_array_get( $this->theme_json, $path, null );
if ( null !== $preset ) { if ( null !== $preset ) {
// If the preset is not already keyed by origin. // If the preset is not already keyed by origin.
@ -608,6 +613,7 @@ class WP_Theme_JSON {
*/ */
$styles_non_top_level = static::VALID_STYLES; $styles_non_top_level = static::VALID_STYLES;
foreach ( array_keys( $styles_non_top_level ) as $section ) { 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 ] ) ) { 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 ) { foreach ( array_keys( $styles_non_top_level[ $section ] ) as $prop ) {
if ( 'top' === $styles_non_top_level[ $section ][ $prop ] ) { if ( 'top' === $styles_non_top_level[ $section ][ $prop ] ) {
@ -631,6 +637,8 @@ class WP_Theme_JSON {
foreach ( $valid_element_names as $element ) { foreach ( $valid_element_names as $element ) {
$schema_styles_elements[ $element ] = $styles_non_top_level; $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 ) ) { if ( array_key_exists( $element, static::VALID_ELEMENT_PSEUDO_SELECTORS ) ) {
foreach ( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $element ] as $pseudo_selector ) { foreach ( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $element ] as $pseudo_selector ) {
$schema_styles_elements[ $element ][ $pseudo_selector ] = $styles_non_top_level; $schema_styles_elements[ $element ][ $pseudo_selector ] = $styles_non_top_level;
@ -1274,7 +1282,11 @@ class WP_Theme_JSON {
$selector = $metadata['selector']; $selector = $metadata['selector'];
$node = _wp_array_get( $this->theme_json, $metadata['path'], array() ); $node = _wp_array_get( $this->theme_json, $metadata['path'], array() );
$declarations = array_merge( static::compute_preset_vars( $node, $origins ), static::compute_theme_vars( $node ) ); $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 ); $stylesheet .= static::to_ruleset( $selector, $declarations );
} }
@ -1602,10 +1614,10 @@ class WP_Theme_JSON {
if ( is_array( $value ) ) { if ( is_array( $value ) ) {
$new_prefix = $new_key . $token; $new_prefix = $new_key . $token;
$result = array_merge( $flattened_subtree = static::flatten_tree( $value, $new_prefix, $token );
$result, foreach ( $flattened_subtree as $subtree_key => $subtree_value ) {
static::flatten_tree( $value, $new_prefix, $token ) $result[ $subtree_key ] = $subtree_value;
); }
} else { } else {
$result[ $new_key ] = $value; $result[ $new_key ] = $value;
} }
@ -1667,6 +1679,8 @@ class WP_Theme_JSON {
if ( is_array( $value_path ) ) { if ( is_array( $value_path ) ) {
$path_string = implode( '.', $value_path ); $path_string = implode( '.', $value_path );
if ( 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 ) && array_key_exists( $path_string, static::PROTECTED_PROPERTIES ) &&
_wp_array_get( $settings, static::PROTECTED_PROPERTIES[ $path_string ], null ) === null _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. * where the values is an array with a "ref" key, pointing to a path.
* For example: { "ref": "style.color.background" } => "#fff". * 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'] ); $value_path = explode( '.', $value['ref'] );
$ref_value = _wp_array_get( $theme_json, $value_path ); $ref_value = _wp_array_get( $theme_json, $value_path );
// Only use the ref value if we find anything. // Only use the ref value if we find anything.
@ -1750,7 +1764,7 @@ class WP_Theme_JSON {
$value = $ref_value; $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 ); $path_string = json_encode( $path );
$ref_value_string = json_encode( $ref_value ); $ref_value_string = json_encode( $ref_value );
_doing_it_wrong( _doing_it_wrong(
@ -1886,6 +1900,8 @@ class WP_Theme_JSON {
); );
// Handle any pseudo selectors for the element. // 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 ) ) { if ( array_key_exists( $element, static::VALID_ELEMENT_PSEUDO_SELECTORS ) ) {
foreach ( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $element ] as $pseudo_selector ) { foreach ( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $element ] as $pseudo_selector ) {
@ -1905,7 +1921,10 @@ class WP_Theme_JSON {
return $nodes; 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. * Filters the list of style nodes with metadata.
@ -1982,6 +2001,8 @@ class WP_Theme_JSON {
); );
// Handle any pseudo selectors for the element. // 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 ) ) { if ( array_key_exists( $element, static::VALID_ELEMENT_PSEUDO_SELECTORS ) ) {
foreach ( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $element ] as $pseudo_selector ) { foreach ( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $element ] as $pseudo_selector ) {
if ( isset( $theme_json['styles']['blocks'][ $name ]['elements'][ $element ][ $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 // the feature selector. This may occur when multiple block
// support features use the same custom selector. // support features use the same custom selector.
if ( isset( $feature_declarations[ $feature_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 { } else {
$feature_declarations[ $feature_selector ] = $new_feature_declarations; $feature_declarations[ $feature_selector ] = $new_feature_declarations;
} }
@ -2059,6 +2082,8 @@ class WP_Theme_JSON {
$element_pseudo_allowed = array(); $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 ) ) { if ( array_key_exists( $current_element, static::VALID_ELEMENT_PSEUDO_SELECTORS ) ) {
$element_pseudo_allowed = static::VALID_ELEMENT_PSEUDO_SELECTORS[ $current_element ]; $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. * Otherwise just compute the styles for the default selector as normal.
*/ */
if ( $pseudo_selector && isset( $node[ $pseudo_selector ] ) && 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 ) array_key_exists( $current_element, static::VALID_ELEMENT_PSEUDO_SELECTORS )
&& in_array( $pseudo_selector, static::VALID_ELEMENT_PSEUDO_SELECTORS[ $current_element ], true ) && 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 ); $nodes = static::get_setting_nodes( $incoming_data );
$slugs_global = static::get_default_slugs( $this->theme_json, array( 'settings' ) ); $slugs_global = static::get_default_slugs( $this->theme_json, array( 'settings' ) );
foreach ( $nodes as $node ) { 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. // 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 ); $content = _wp_array_get( $incoming_data, $path, null );
if ( isset( $content ) ) { if ( isset( $content ) ) {
_wp_array_set( $this->theme_json, $path, $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 ); $override_preset = ! static::get_metadata_boolean( $this->theme_json['settings'], $preset['prevent_override'], true );
foreach ( static::VALID_ORIGINS as $origin ) { foreach ( static::VALID_ORIGINS as $origin ) {
$base_path = array_merge( $node['path'], $preset['path'] ); $base_path = $node['path'];
$path = array_merge( $base_path, array( $origin ) ); foreach ( $preset['path'] as $leaf ) {
$base_path[] = $leaf;
}
$path = $base_path;
$path[] = $origin;
$content = _wp_array_get( $incoming_data, $path, null ); $content = _wp_array_get( $incoming_data, $path, null );
if ( ! isset( $content ) ) { if ( ! isset( $content ) ) {
continue; continue;
} }
if ( 'theme' === $origin && $preset['use_default_names'] ) { if ( 'theme' === $origin && $preset['use_default_names'] ) {
foreach ( $content as &$item ) { foreach ( $content as $key => $item ) {
if ( ! array_key_exists( 'name', $item ) ) { if ( ! isset( $item['name'] ) ) {
$name = static::get_name_from_defaults( $item['slug'], $base_path ); $name = static::get_name_from_defaults( $item['slug'], $base_path );
if ( null !== $name ) { 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 ); _wp_array_set( $this->theme_json, $path, $content );
} else { } 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() ); $slugs_for_preset = _wp_array_get( $slugs, $preset['path'], array() );
$content = static::filter_slugs( $content, $slugs_for_preset ); $content = static::filter_slugs( $content, $slugs_for_preset );
_wp_array_set( $this->theme_json, $path, $content ); _wp_array_set( $this->theme_json, $path, $content );
@ -2434,7 +2470,12 @@ class WP_Theme_JSON {
$slugs = array(); $slugs = array();
foreach ( static::PRESETS_METADATA as $metadata ) { 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 ); $preset = _wp_array_get( $data, $path, null );
if ( ! isset( $preset ) ) { if ( ! isset( $preset ) ) {
continue; continue;
@ -2463,7 +2504,8 @@ class WP_Theme_JSON {
* @return string|null * @return string|null
*/ */
protected function get_name_from_defaults( $slug, $base_path ) { 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 ); $default_content = _wp_array_get( $this->theme_json, $path, null );
if ( ! $default_content ) { if ( ! $default_content ) {
return null; return null;
@ -2539,6 +2581,8 @@ class WP_Theme_JSON {
* $output is stripped of pseudo selectors. Re-add and process them * $output is stripped of pseudo selectors. Re-add and process them
* or insecure styles here. * 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 ) ) { if ( array_key_exists( $current_element, static::VALID_ELEMENT_PSEUDO_SELECTORS ) ) {
foreach ( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $current_element ] as $pseudo_selector ) { foreach ( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $current_element ] as $pseudo_selector ) {
if ( isset( $input[ $pseudo_selector ] ) ) { if ( isset( $input[ $pseudo_selector ] ) ) {
@ -2593,7 +2637,8 @@ class WP_Theme_JSON {
$output = array(); $output = array();
foreach ( static::PRESETS_METADATA as $preset_metadata ) { foreach ( static::PRESETS_METADATA as $preset_metadata ) {
foreach ( static::VALID_ORIGINS as $origin ) { foreach ( static::VALID_ORIGINS as $origin ) {
$path_with_origin = array_merge( $preset_metadata['path'], array( $origin ) ); $path_with_origin = $preset_metadata['path'];
$path_with_origin[] = $origin;
$presets = _wp_array_get( $input, $path_with_origin, null ); $presets = _wp_array_get( $input, $path_with_origin, null );
if ( null === $presets ) { if ( null === $presets ) {
continue; continue;
@ -2839,7 +2884,10 @@ class WP_Theme_JSON {
*/ */
foreach ( $nodes as $node ) { foreach ( $nodes as $node ) {
foreach ( static::PRESETS_METADATA as $preset_metadata ) { 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 ); $preset = _wp_array_get( $output, $path, null );
if ( null === $preset ) { if ( null === $preset ) {
continue; continue;
@ -2873,7 +2921,10 @@ class WP_Theme_JSON {
foreach ( $nodes as $node ) { foreach ( $nodes as $node ) {
$all_opt_ins_are_set = true; $all_opt_ins_are_set = true;
foreach ( static::APPEARANCE_TOOLS_OPT_INS as $opt_in_path ) { 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 // Use "unset prop" as a marker instead of "null" because
// "null" can be a valid value for some props (e.g. blockGap). // "null" can be a valid value for some props (e.g. blockGap).
$opt_in_value = _wp_array_get( $output, $full_path, 'unset prop' ); $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 ) { 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 ) { 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 // Use "unset prop" as a marker instead of "null" because
// "null" can be a valid value for some props (e.g. blockGap). // "null" can be a valid value for some props (e.g. blockGap).
$opt_in_value = _wp_array_get( $output, $full_path, 'unset prop' ); $opt_in_value = _wp_array_get( $output, $full_path, 'unset prop' );
@ -3037,7 +3093,10 @@ class WP_Theme_JSON {
$slug += 10; $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 there are 7 or less steps in the scale revert to numbers for labels instead of t-shirt sizes.
if ( $spacing_scale['steps'] <= 7 ) { if ( $spacing_scale['steps'] <= 7 ) {

View File

@ -16,7 +16,7 @@
* *
* @global string $wp_version * @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. * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.