Editor: Improve performance of _register_theme_block_patterns function.
The `_register_theme_block_patterns` function imposed a significant resource overhead. This issue primarily stems from themes, such as TT4, that register a substantial number of block patterns. These patterns necessitate numerous file operations, including file lookups, file reading into memory, and related processes. To provide an overview, the _register_theme_block_patterns function performed the following file operations: - is_dir - is_readable - file_exists - glob - file_get_contents (utilized via get_file_data) To address these issues, caching using a transient has been added to a new function call `_wp_get_block_patterns`. If theme development mode is disabled and theme exists, the block patterns are saved in a transient cache. This cache is used all requests after that, saving file lookups and reading files into memory. Cache invalidation is done, when themes are switched, deleted or updated. Meaning that block patterns are not stored in the cache incorrectly. Props flixos90, joemcgill, peterwilsoncc, costdev, swissspidy, aristath, westonruter, spacedmonkey. Fixes #59490 Built from https://develop.svn.wordpress.org/trunk@56765 git-svn-id: http://core.svn.wordpress.org/trunk@56277 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
parent
67161cc1dd
commit
b0872b005d
|
@ -81,6 +81,8 @@ function delete_theme( $stylesheet, $redirect = '' ) {
|
||||||
*/
|
*/
|
||||||
do_action( 'delete_theme', $stylesheet );
|
do_action( 'delete_theme', $stylesheet );
|
||||||
|
|
||||||
|
$theme = wp_get_theme( $stylesheet );
|
||||||
|
|
||||||
$themes_dir = trailingslashit( $themes_dir );
|
$themes_dir = trailingslashit( $themes_dir );
|
||||||
$theme_dir = trailingslashit( $themes_dir . $stylesheet );
|
$theme_dir = trailingslashit( $themes_dir . $stylesheet );
|
||||||
$deleted = $wp_filesystem->delete( $theme_dir, true );
|
$deleted = $wp_filesystem->delete( $theme_dir, true );
|
||||||
|
@ -125,6 +127,9 @@ function delete_theme( $stylesheet, $redirect = '' ) {
|
||||||
WP_Theme::network_disable_theme( $stylesheet );
|
WP_Theme::network_disable_theme( $stylesheet );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear theme caches.
|
||||||
|
$theme->cache_delete();
|
||||||
|
|
||||||
// Force refresh of theme update information.
|
// Force refresh of theme update information.
|
||||||
delete_site_transient( 'update_themes' );
|
delete_site_transient( 'update_themes' );
|
||||||
|
|
||||||
|
|
|
@ -319,37 +319,132 @@ function _register_remote_theme_patterns() {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register any patterns that the active theme may provide under its
|
* Register any patterns that the active theme may provide under its
|
||||||
* `./patterns/` directory. Each pattern is defined as a PHP file and defines
|
* `./patterns/` directory.
|
||||||
* its metadata using plugin-style headers. The minimum required definition is:
|
|
||||||
*
|
|
||||||
* /**
|
|
||||||
* * Title: My Pattern
|
|
||||||
* * Slug: my-theme/my-pattern
|
|
||||||
* *
|
|
||||||
*
|
|
||||||
* The output of the PHP source corresponds to the content of the pattern, e.g.:
|
|
||||||
*
|
|
||||||
* <main><p><?php echo "Hello"; ?></p></main>
|
|
||||||
*
|
|
||||||
* If applicable, this will collect from both parent and child theme.
|
|
||||||
*
|
|
||||||
* Other settable fields include:
|
|
||||||
*
|
|
||||||
* - Description
|
|
||||||
* - Viewport Width
|
|
||||||
* - Inserter (yes/no)
|
|
||||||
* - Categories (comma-separated values)
|
|
||||||
* - Keywords (comma-separated values)
|
|
||||||
* - Block Types (comma-separated values)
|
|
||||||
* - Post Types (comma-separated values)
|
|
||||||
* - Template Types (comma-separated values)
|
|
||||||
*
|
*
|
||||||
* @since 6.0.0
|
* @since 6.0.0
|
||||||
* @since 6.1.0 The `postTypes` property was added.
|
* @since 6.1.0 The `postTypes` property was added.
|
||||||
* @since 6.2.0 The `templateTypes` property was added.
|
* @since 6.2.0 The `templateTypes` property was added.
|
||||||
|
* @since 6.4.0 Uses the `_wp_get_block_patterns` function.
|
||||||
* @access private
|
* @access private
|
||||||
*/
|
*/
|
||||||
function _register_theme_block_patterns() {
|
function _register_theme_block_patterns() {
|
||||||
|
/*
|
||||||
|
* Register patterns for the active theme. If the theme is a child theme,
|
||||||
|
* let it override any patterns from the parent theme that shares the same slug.
|
||||||
|
*/
|
||||||
|
$themes = array();
|
||||||
|
$theme = wp_get_theme();
|
||||||
|
$themes[] = $theme;
|
||||||
|
if ( $theme->parent() ) {
|
||||||
|
$themes[] = $theme->parent();
|
||||||
|
}
|
||||||
|
$registry = WP_Block_Patterns_Registry::get_instance();
|
||||||
|
|
||||||
|
foreach ( $themes as $theme ) {
|
||||||
|
$pattern_data = _wp_get_block_patterns( $theme );
|
||||||
|
$dirpath = $theme->get_stylesheet_directory() . '/patterns/';
|
||||||
|
$text_domain = $theme->get( 'TextDomain' );
|
||||||
|
|
||||||
|
foreach ( $pattern_data['patterns'] as $file => $pattern_data ) {
|
||||||
|
if ( $registry->is_registered( $pattern_data['slug'] ) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The actual pattern content is the output of the file.
|
||||||
|
ob_start();
|
||||||
|
include $dirpath . $file;
|
||||||
|
$pattern_data['content'] = ob_get_clean();
|
||||||
|
if ( ! $pattern_data['content'] ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Translate the pattern metadata.
|
||||||
|
// phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralText,WordPress.WP.I18n.NonSingularStringLiteralDomain,WordPress.WP.I18n.LowLevelTranslationFunction
|
||||||
|
$pattern_data['title'] = translate_with_gettext_context( $pattern_data['title'], 'Pattern title', $text_domain );
|
||||||
|
if ( ! empty( $pattern_data['description'] ) ) {
|
||||||
|
// phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralText,WordPress.WP.I18n.NonSingularStringLiteralDomain,WordPress.WP.I18n.LowLevelTranslationFunction
|
||||||
|
$pattern_data['description'] = translate_with_gettext_context( $pattern_data['description'], 'Pattern description', $text_domain );
|
||||||
|
}
|
||||||
|
|
||||||
|
register_block_pattern( $pattern_data['slug'], $pattern_data );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
add_action( 'init', '_register_theme_block_patterns' );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets block pattern data for a specified theme.
|
||||||
|
* Each pattern is defined as a PHP file and defines
|
||||||
|
* its metadata using plugin-style headers. The minimum required definition is:
|
||||||
|
*
|
||||||
|
* /**
|
||||||
|
* * Title: My Pattern
|
||||||
|
* * Slug: my-theme/my-pattern
|
||||||
|
* *
|
||||||
|
*
|
||||||
|
* The output of the PHP source corresponds to the content of the pattern, e.g.:
|
||||||
|
*
|
||||||
|
* <main><p><?php echo "Hello"; ?></p></main>
|
||||||
|
*
|
||||||
|
* If applicable, this will collect from both parent and child theme.
|
||||||
|
*
|
||||||
|
* Other settable fields include:
|
||||||
|
*
|
||||||
|
* - Description
|
||||||
|
* - Viewport Width
|
||||||
|
* - Inserter (yes/no)
|
||||||
|
* - Categories (comma-separated values)
|
||||||
|
* - Keywords (comma-separated values)
|
||||||
|
* - Block Types (comma-separated values)
|
||||||
|
* - Post Types (comma-separated values)
|
||||||
|
* - Template Types (comma-separated values)
|
||||||
|
*
|
||||||
|
* @since 6.4.0
|
||||||
|
* @access private
|
||||||
|
*
|
||||||
|
* @param WP_Theme $theme Theme object.
|
||||||
|
* @return array Block pattern data.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function _wp_get_block_patterns( WP_Theme $theme ) {
|
||||||
|
if ( ! $theme->exists() ) {
|
||||||
|
return array(
|
||||||
|
'version' => false,
|
||||||
|
'patterns' => array(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$transient_name = 'wp_theme_patterns_' . $theme->get_stylesheet();
|
||||||
|
$version = $theme->get( 'Version' );
|
||||||
|
$can_use_cached = ! wp_is_development_mode( 'theme' );
|
||||||
|
|
||||||
|
if ( $can_use_cached ) {
|
||||||
|
$pattern_data = get_transient( $transient_name );
|
||||||
|
if ( is_array( $pattern_data ) && $pattern_data['version'] === $version ) {
|
||||||
|
return $pattern_data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$pattern_data = array(
|
||||||
|
'version' => $version,
|
||||||
|
'patterns' => array(),
|
||||||
|
);
|
||||||
|
$dirpath = $theme->get_stylesheet_directory() . '/patterns/';
|
||||||
|
|
||||||
|
if ( ! file_exists( $dirpath ) ) {
|
||||||
|
if ( $can_use_cached ) {
|
||||||
|
set_transient( $transient_name, $pattern_data );
|
||||||
|
}
|
||||||
|
return $pattern_data;
|
||||||
|
}
|
||||||
|
$files = glob( $dirpath . '*.php' );
|
||||||
|
if ( ! $files ) {
|
||||||
|
if ( $can_use_cached ) {
|
||||||
|
set_transient( $transient_name, $pattern_data );
|
||||||
|
}
|
||||||
|
return $pattern_data;
|
||||||
|
}
|
||||||
|
|
||||||
$default_headers = array(
|
$default_headers = array(
|
||||||
'title' => 'Title',
|
'title' => 'Title',
|
||||||
'slug' => 'Slug',
|
'slug' => 'Slug',
|
||||||
|
@ -363,130 +458,94 @@ function _register_theme_block_patterns() {
|
||||||
'templateTypes' => 'Template Types',
|
'templateTypes' => 'Template Types',
|
||||||
);
|
);
|
||||||
|
|
||||||
/*
|
$properties_to_parse = array(
|
||||||
* Register patterns for the active theme. If the theme is a child theme,
|
'categories',
|
||||||
* let it override any patterns from the parent theme that shares the same slug.
|
'keywords',
|
||||||
*/
|
'blockTypes',
|
||||||
$themes = array();
|
'postTypes',
|
||||||
$stylesheet = get_stylesheet();
|
'templateTypes',
|
||||||
$template = get_template();
|
);
|
||||||
if ( $stylesheet !== $template ) {
|
|
||||||
$themes[] = wp_get_theme( $stylesheet );
|
|
||||||
}
|
|
||||||
$themes[] = wp_get_theme( $template );
|
|
||||||
|
|
||||||
foreach ( $themes as $theme ) {
|
foreach ( $files as $file ) {
|
||||||
$dirpath = $theme->get_stylesheet_directory() . '/patterns/';
|
$pattern = get_file_data( $file, $default_headers );
|
||||||
if ( ! is_dir( $dirpath ) || ! is_readable( $dirpath ) ) {
|
|
||||||
|
if ( empty( $pattern['slug'] ) ) {
|
||||||
|
_doing_it_wrong(
|
||||||
|
__FUNCTION__,
|
||||||
|
sprintf(
|
||||||
|
/* translators: %s: file name. */
|
||||||
|
__( 'Could not register file "%s" as a block pattern ("Slug" field missing)' ),
|
||||||
|
$file
|
||||||
|
),
|
||||||
|
'6.0.0'
|
||||||
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ( file_exists( $dirpath ) ) {
|
|
||||||
$files = glob( $dirpath . '*.php' );
|
|
||||||
if ( $files ) {
|
|
||||||
foreach ( $files as $file ) {
|
|
||||||
$pattern_data = get_file_data( $file, $default_headers );
|
|
||||||
|
|
||||||
if ( empty( $pattern_data['slug'] ) ) {
|
if ( ! preg_match( '/^[A-z0-9\/_-]+$/', $pattern['slug'] ) ) {
|
||||||
_doing_it_wrong(
|
_doing_it_wrong(
|
||||||
'_register_theme_block_patterns',
|
__FUNCTION__,
|
||||||
sprintf(
|
sprintf(
|
||||||
/* translators: %s: file name. */
|
/* translators: %1s: file name; %2s: slug value found. */
|
||||||
__( 'Could not register file "%s" as a block pattern ("Slug" field missing)' ),
|
__( 'Could not register file "%1$s" as a block pattern (invalid slug "%2$s")' ),
|
||||||
$file
|
$file,
|
||||||
),
|
$pattern['slug']
|
||||||
'6.0.0'
|
),
|
||||||
);
|
'6.0.0'
|
||||||
continue;
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ! preg_match( '/^[A-z0-9\/_-]+$/', $pattern_data['slug'] ) ) {
|
// Title is a required property.
|
||||||
_doing_it_wrong(
|
if ( ! $pattern['title'] ) {
|
||||||
'_register_theme_block_patterns',
|
_doing_it_wrong(
|
||||||
sprintf(
|
__FUNCTION__,
|
||||||
/* translators: %1s: file name; %2s: slug value found. */
|
sprintf(
|
||||||
__( 'Could not register file "%1$s" as a block pattern (invalid slug "%2$s")' ),
|
/* translators: %1s: file name. */
|
||||||
$file,
|
__( 'Could not register file "%s" as a block pattern ("Title" field missing)' ),
|
||||||
$pattern_data['slug']
|
$file
|
||||||
),
|
),
|
||||||
'6.0.0'
|
'6.0.0'
|
||||||
);
|
);
|
||||||
}
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if ( WP_Block_Patterns_Registry::get_instance()->is_registered( $pattern_data['slug'] ) ) {
|
// For properties of type array, parse data as comma-separated.
|
||||||
continue;
|
foreach ( $properties_to_parse as $property ) {
|
||||||
}
|
if ( ! empty( $pattern[ $property ] ) ) {
|
||||||
|
$pattern[ $property ] = array_filter( wp_parse_list( (string) $pattern[ $property ] ) );
|
||||||
// Title is a required property.
|
} else {
|
||||||
if ( ! $pattern_data['title'] ) {
|
unset( $pattern[ $property ] );
|
||||||
_doing_it_wrong(
|
|
||||||
'_register_theme_block_patterns',
|
|
||||||
sprintf(
|
|
||||||
/* translators: %1s: file name; %2s: slug value found. */
|
|
||||||
__( 'Could not register file "%s" as a block pattern ("Title" field missing)' ),
|
|
||||||
$file
|
|
||||||
),
|
|
||||||
'6.0.0'
|
|
||||||
);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// For properties of type array, parse data as comma-separated.
|
|
||||||
foreach ( array( 'categories', 'keywords', 'blockTypes', 'postTypes', 'templateTypes' ) as $property ) {
|
|
||||||
if ( ! empty( $pattern_data[ $property ] ) ) {
|
|
||||||
$pattern_data[ $property ] = array_filter(
|
|
||||||
preg_split(
|
|
||||||
'/[\s,]+/',
|
|
||||||
(string) $pattern_data[ $property ]
|
|
||||||
)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
unset( $pattern_data[ $property ] );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse properties of type int.
|
|
||||||
foreach ( array( 'viewportWidth' ) as $property ) {
|
|
||||||
if ( ! empty( $pattern_data[ $property ] ) ) {
|
|
||||||
$pattern_data[ $property ] = (int) $pattern_data[ $property ];
|
|
||||||
} else {
|
|
||||||
unset( $pattern_data[ $property ] );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse properties of type bool.
|
|
||||||
foreach ( array( 'inserter' ) as $property ) {
|
|
||||||
if ( ! empty( $pattern_data[ $property ] ) ) {
|
|
||||||
$pattern_data[ $property ] = in_array(
|
|
||||||
strtolower( $pattern_data[ $property ] ),
|
|
||||||
array( 'yes', 'true' ),
|
|
||||||
true
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
unset( $pattern_data[ $property ] );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Translate the pattern metadata.
|
|
||||||
$text_domain = $theme->get( 'TextDomain' );
|
|
||||||
// phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralText,WordPress.WP.I18n.NonSingularStringLiteralDomain,WordPress.WP.I18n.LowLevelTranslationFunction
|
|
||||||
$pattern_data['title'] = translate_with_gettext_context( $pattern_data['title'], 'Pattern title', $text_domain );
|
|
||||||
if ( ! empty( $pattern_data['description'] ) ) {
|
|
||||||
// phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralText,WordPress.WP.I18n.NonSingularStringLiteralDomain,WordPress.WP.I18n.LowLevelTranslationFunction
|
|
||||||
$pattern_data['description'] = translate_with_gettext_context( $pattern_data['description'], 'Pattern description', $text_domain );
|
|
||||||
}
|
|
||||||
|
|
||||||
// The actual pattern content is the output of the file.
|
|
||||||
ob_start();
|
|
||||||
include $file;
|
|
||||||
$pattern_data['content'] = ob_get_clean();
|
|
||||||
if ( ! $pattern_data['content'] ) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
register_block_pattern( $pattern_data['slug'], $pattern_data );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parse properties of type int.
|
||||||
|
$property = 'viewportWidth';
|
||||||
|
if ( ! empty( $pattern[ $property ] ) ) {
|
||||||
|
$pattern[ $property ] = (int) $pattern[ $property ];
|
||||||
|
} else {
|
||||||
|
unset( $pattern[ $property ] );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse properties of type bool.
|
||||||
|
$property = 'inserter';
|
||||||
|
if ( ! empty( $pattern[ $property ] ) ) {
|
||||||
|
$pattern[ $property ] = in_array(
|
||||||
|
strtolower( $pattern[ $property ] ),
|
||||||
|
array( 'yes', 'true' ),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
unset( $pattern[ $property ] );
|
||||||
|
}
|
||||||
|
|
||||||
|
$key = str_replace( $dirpath, '', $file );
|
||||||
|
|
||||||
|
$pattern_data['patterns'][ $key ] = $pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( $can_use_cached ) {
|
||||||
|
set_transient( $transient_name, $pattern_data );
|
||||||
|
}
|
||||||
|
|
||||||
|
return $pattern_data;
|
||||||
}
|
}
|
||||||
add_action( 'init', '_register_theme_block_patterns' );
|
|
||||||
|
|
|
@ -821,6 +821,16 @@ final class WP_Theme implements ArrayAccess {
|
||||||
$this->block_template_folders = null;
|
$this->block_template_folders = null;
|
||||||
$this->headers = array();
|
$this->headers = array();
|
||||||
$this->__construct( $this->stylesheet, $this->theme_root );
|
$this->__construct( $this->stylesheet, $this->theme_root );
|
||||||
|
$this->delete_pattern_cache();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear block pattern cache.
|
||||||
|
*
|
||||||
|
* @since 6.4.0
|
||||||
|
*/
|
||||||
|
public function delete_pattern_cache() {
|
||||||
|
delete_transient( 'wp_theme_patterns_' . $this->stylesheet );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -873,6 +873,10 @@ function switch_theme( $stylesheet ) {
|
||||||
$wp_stylesheet_path = null;
|
$wp_stylesheet_path = null;
|
||||||
$wp_template_path = null;
|
$wp_template_path = null;
|
||||||
|
|
||||||
|
// Clear pattern caches.
|
||||||
|
$new_theme->delete_pattern_cache();
|
||||||
|
$old_theme->delete_pattern_cache();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fires after the theme is switched.
|
* Fires after the theme is switched.
|
||||||
*
|
*
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
*
|
*
|
||||||
* @global string $wp_version
|
* @global string $wp_version
|
||||||
*/
|
*/
|
||||||
$wp_version = '6.4-beta1-56764';
|
$wp_version = '6.4-beta1-56765';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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.
|
||||||
|
|
Loading…
Reference in New Issue