Skip to content

Commit fe590c1

Browse files
authored
Merge pull request #503 from BeAPI/fix/perf-assets-editor-patterns
perf: cache assets.json manifest, drop WP <6.0 patterns backport, declare PHP/WP requirements
2 parents 52f2928 + 486448a commit fe590c1

5 files changed

Lines changed: 56 additions & 179 deletions

File tree

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@
1010

1111
## Requirements
1212

13+
### PHP & WordPress
14+
15+
- PHP **8.3** or higher
16+
- WordPress **6.3** or higher
17+
1318
### Composer
1419

1520
You need composer to autoload all your classes from the inc folder.

inc/Services/Assets.php

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -132,14 +132,9 @@ public function get_min_file( string $type ): string {
132132
return '';
133133
}
134134

135-
if ( ! file_exists( \get_theme_file_path( '/dist/assets.json' ) ) ) {
136-
return '';
137-
}
138-
139-
$json = file_get_contents( \get_theme_file_path( '/dist/assets.json' ) ); //phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
140-
$assets = json_decode( $json, true );
135+
$assets = $this->get_assets_manifest();
141136

142-
if ( empty( $assets ) || JSON_ERROR_NONE !== json_last_error() ) {
137+
if ( empty( $assets ) ) {
143138
return '';
144139
}
145140

@@ -176,6 +171,41 @@ public function get_min_file( string $type ): string {
176171
return $file;
177172
}
178173

174+
/**
175+
* Read and decode the `dist/assets.json` manifest once per request.
176+
*
177+
* The manifest is requested several times per request (scripts registration,
178+
* `stylesheet_uri` filter, editor assets…): memoize the decoded content to
179+
* avoid repeated filesystem reads and JSON decoding.
180+
*
181+
* @return array<string, string> Manifest entries, or an empty array if unavailable/invalid.
182+
*/
183+
private function get_assets_manifest(): array {
184+
static $manifest = null;
185+
186+
if ( null !== $manifest ) {
187+
return $manifest;
188+
}
189+
190+
$manifest = [];
191+
$manifest_path = \get_theme_file_path( '/dist/assets.json' );
192+
193+
if ( ! file_exists( $manifest_path ) ) {
194+
return $manifest;
195+
}
196+
197+
$json = file_get_contents( $manifest_path ); //phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
198+
$assets = json_decode( $json, true );
199+
200+
if ( ! is_array( $assets ) || JSON_ERROR_NONE !== json_last_error() ) {
201+
return $manifest;
202+
}
203+
204+
$manifest = $assets;
205+
206+
return $manifest;
207+
}
208+
179209
/**
180210
* Retrieve data for a compiled asset file.
181211
*

inc/Services/Editor.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,15 @@ public function boot( Service_Container $container ): void {
7171

7272
/**
7373
* editor style
74+
*
75+
* Editor styles are only consumed in admin/editor context: skip the
76+
* manifest lookup and filesystem check on front-end requests.
7477
*/
7578
private function style(): void {
79+
if ( ! is_admin() ) {
80+
return;
81+
}
82+
7683
$file = $this->assets->get_min_file( 'editor.css' ) ?: 'editor.css';
7784

7885
/**

inc/Services/Editor_Patterns.php

Lines changed: 6 additions & 172 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@
66
use BEA\Theme\Framework\Service;
77
use BEA\Theme\Framework\Service_Container;
88

9+
/**
10+
* Register the theme block pattern categories.
11+
*
12+
* The patterns themselves (PHP files in `./patterns/`) are automatically
13+
* registered by WordPress core since 6.0 (`_register_theme_block_patterns()`).
14+
*/
915
class Editor_Patterns implements Service {
1016
/**
1117
* @param Service_Container $container
@@ -25,7 +31,6 @@ public function get_service_name(): string {
2531
*/
2632
public function boot( Service_Container $container ): void {
2733
\add_action( 'init', [ $this, 'register_categories' ], 10 );
28-
\add_action( 'init', [ $this, 'register_patterns' ], 11 );
2934
}
3035

3136
/**
@@ -47,175 +52,4 @@ public function register_categories(): void {
4752
register_block_pattern_category( $name, $properties );
4853
}
4954
}
50-
51-
/**
52-
* Register any patterns that the active theme may provide under its
53-
* `./patterns/` directory. Each pattern is defined as a PHP file and defines
54-
* its metadata using plugin-style headers. The minimum required definition is:
55-
*
56-
* /**
57-
* * Title: My Pattern
58-
* * Slug: my-theme/my-pattern
59-
* *
60-
*
61-
* The output of the PHP source corresponds to the content of the pattern, e.g.:
62-
*
63-
* <main><p><?php echo "Hello"; ?></p></main>
64-
*
65-
* If applicable, this will collect from both parent and child theme.
66-
*
67-
* Other settable fields include:
68-
*
69-
* - Description
70-
* - Viewport Width
71-
* - Categories (comma-separated values)
72-
* - Keywords (comma-separated values)
73-
* - Block Types (comma-separated values)
74-
* - Inserter (yes/no)
75-
*
76-
* @since 6.0.0
77-
* @access private
78-
* @internal
79-
* @see https://github.com/WordPress/gutenberg/blob/trunk/lib/compat/wordpress-6.0/block-patterns.php
80-
*/
81-
public function register_patterns(): void {
82-
83-
/**
84-
* this function is already present in WordPress 6
85-
*/
86-
if ( version_compare( get_bloginfo( 'version' ), '6.0', '>=' ) ) {
87-
return;
88-
}
89-
90-
$default_headers = [
91-
'title' => 'Title',
92-
'slug' => 'Slug',
93-
'description' => 'Description',
94-
'viewportWidth' => 'Viewport Width',
95-
'categories' => 'Categories',
96-
'keywords' => 'Keywords',
97-
'blockTypes' => 'Block Types',
98-
'inserter' => 'Inserter',
99-
];
100-
101-
// Register patterns for the active theme. If the theme is a child theme,
102-
// let it override any patterns from the parent theme that shares the same slug.
103-
$themes = [];
104-
$stylesheet = get_stylesheet();
105-
$template = get_template();
106-
if ( $stylesheet !== $template ) {
107-
$themes[] = wp_get_theme( $stylesheet );
108-
}
109-
$themes[] = wp_get_theme( $template );
110-
111-
foreach ( $themes as $theme ) {
112-
$dirpath = $theme->get_stylesheet_directory() . '/patterns/';
113-
if ( ! is_dir( $dirpath ) || ! is_readable( $dirpath ) ) {
114-
continue;
115-
}
116-
$files = glob( $dirpath . '*.php' );
117-
if ( is_array( $files ) && count( $files ) > 0 ) {
118-
foreach ( $files as $file ) {
119-
$pattern_data = get_file_data( $file, $default_headers );
120-
if ( empty( $pattern_data['slug'] ) ) {
121-
_doing_it_wrong(
122-
'_register_theme_block_patterns',
123-
sprintf(
124-
/* translators: %s: file name. */
125-
esc_html__( 'Could not register file "%s" as a block pattern ("Slug" field missing)', 'beapi-frontend-framework' ),
126-
esc_html( $file )
127-
),
128-
'6.0.0'
129-
);
130-
continue;
131-
}
132-
133-
if ( ! preg_match( '/^[A-z0-9\/_-]+$/', $pattern_data['slug'] ) ) {
134-
_doing_it_wrong(
135-
'_register_theme_block_patterns',
136-
sprintf(
137-
/* translators: %1s: file name; %2s: slug value found. */
138-
esc_html__( 'Could not register file "%1$s" as a block pattern (invalid slug "%2$s")', 'beapi-frontend-framework' ),
139-
esc_html( $file ),
140-
esc_html( $pattern_data['slug'] )
141-
),
142-
'6.0.0'
143-
);
144-
}
145-
if ( \WP_Block_Patterns_Registry::get_instance()->is_registered( $pattern_data['slug'] ) ) {
146-
continue;
147-
}
148-
149-
// Title is a required property.
150-
if ( ! $pattern_data['title'] ) {
151-
_doing_it_wrong(
152-
'_register_theme_block_patterns',
153-
sprintf(
154-
/* translators: %1s: file name; %2s: slug value found. */
155-
esc_html__( 'Could not register file "%s" as a block pattern ("Title" field missing)', 'beapi-frontend-framework' ),
156-
esc_html( $file )
157-
),
158-
'6.0.0'
159-
);
160-
continue;
161-
}
162-
163-
// For properties of type array, parse data as comma-separated.
164-
foreach ( [ 'categories', 'keywords', 'blockTypes' ] as $property ) {
165-
if ( ! empty( $pattern_data[ $property ] ) ) {
166-
$pattern_data[ $property ] = array_filter(
167-
preg_split(
168-
'/[\s,]+/',
169-
(string) $pattern_data[ $property ]
170-
)
171-
);
172-
} else {
173-
unset( $pattern_data[ $property ] );
174-
}
175-
}
176-
177-
// Parse properties of type int.
178-
foreach ( [ 'viewportWidth' ] as $property ) {
179-
if ( ! empty( $pattern_data[ $property ] ) ) {
180-
$pattern_data[ $property ] = (int) $pattern_data[ $property ];
181-
} else {
182-
unset( $pattern_data[ $property ] );
183-
}
184-
}
185-
186-
// Parse properties of type bool.
187-
foreach ( [ 'inserter' ] as $property ) {
188-
if ( ! empty( $pattern_data[ $property ] ) ) {
189-
$pattern_data[ $property ] = in_array(
190-
strtolower( $pattern_data[ $property ] ),
191-
[ 'yes', 'true' ],
192-
true
193-
);
194-
} else {
195-
unset( $pattern_data[ $property ] );
196-
}
197-
}
198-
199-
// Translate the pattern metadata.
200-
$text_domain = $theme->get( 'TextDomain' );
201-
//phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralText, WordPress.WP.I18n.NonSingularStringLiteralContext, WordPress.WP.I18n.NonSingularStringLiteralDomain, WordPress.WP.I18n.LowLevelTranslationFunction
202-
$pattern_data['title'] = translate_with_gettext_context( $pattern_data['title'], 'Pattern title', $text_domain );
203-
if ( ! empty( $pattern_data['description'] ) ) {
204-
//phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralText, WordPress.WP.I18n.NonSingularStringLiteralContext, WordPress.WP.I18n.NonSingularStringLiteralDomain, WordPress.WP.I18n.LowLevelTranslationFunction
205-
$pattern_data['description'] = translate_with_gettext_context( $pattern_data['description'], 'Pattern description', $text_domain );
206-
}
207-
208-
// The actual pattern content is the output of the file.
209-
ob_start();
210-
include $file;
211-
$pattern_data['content'] = ob_get_clean();
212-
if ( empty( $pattern_data['content'] ) ) {
213-
continue;
214-
}
215-
216-
register_block_pattern( $pattern_data['slug'], $pattern_data );
217-
}
218-
}
219-
}
220-
}
22155
}

style.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ Author: BeAPI
77
Author URI: http://www.beapi.fr
88
Text Domain: beapi-frontend-framework
99
Requires at least: 6.3
10+
Requires PHP: 8.3
1011
*/

0 commit comments

Comments
 (0)