Skip to content

Commit 7d6b8f5

Browse files
Themes: Optimize WP_Theme::get_post_templates() for efficiency.
Replace dual `file_get_contents()` calls with a single `get_file_data()` call, which reads only the first 8KB of each file instead of the entire contents. Move `get_block_templates()` inside the cache block so the database query only fires on a cache miss instead of on every call. Props gschoppe, birgire, sukhendu2002. Fixes #42513. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent a1c062c commit 7d6b8f5

2 files changed

Lines changed: 91 additions & 17 deletions

File tree

src/wp-includes/class-wp-theme.php

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1338,13 +1338,21 @@ public function get_post_templates() {
13381338
$files = (array) $this->get_files( 'php', 1, true );
13391339

13401340
foreach ( $files as $file => $full_path ) {
1341-
if ( ! preg_match( '|Template Name:(.*)$|mi', file_get_contents( $full_path ), $header ) ) {
1341+
$headers = get_file_data(
1342+
$full_path,
1343+
array(
1344+
'Template Name' => 'Template Name',
1345+
'Template Post Type' => 'Template Post Type',
1346+
)
1347+
);
1348+
1349+
if ( ! $headers['Template Name'] ) {
13421350
continue;
13431351
}
13441352

13451353
$types = array( 'page' );
1346-
if ( preg_match( '|Template Post Type:(.*)$|mi', file_get_contents( $full_path ), $type ) ) {
1347-
$types = explode( ',', _cleanup_header_comment( $type[1] ) );
1354+
if ( $headers['Template Post Type'] ) {
1355+
$types = explode( ',', $headers['Template Post Type'] );
13481356
}
13491357

13501358
foreach ( $types as $type ) {
@@ -1353,28 +1361,28 @@ public function get_post_templates() {
13531361
$post_templates[ $type ] = array();
13541362
}
13551363

1356-
$post_templates[ $type ][ $file ] = _cleanup_header_comment( $header[1] );
1364+
$post_templates[ $type ][ $file ] = $headers['Template Name'];
13571365
}
13581366
}
13591367

1360-
$this->cache_add( 'post_templates', $post_templates );
1361-
}
1368+
if ( current_theme_supports( 'block-templates' ) ) {
1369+
$block_templates = get_block_templates( array(), 'wp_template' );
1370+
foreach ( get_post_types( array( 'public' => true ) ) as $type ) {
1371+
foreach ( $block_templates as $block_template ) {
1372+
if ( ! $block_template->is_custom ) {
1373+
continue;
1374+
}
13621375

1363-
if ( current_theme_supports( 'block-templates' ) ) {
1364-
$block_templates = get_block_templates( array(), 'wp_template' );
1365-
foreach ( get_post_types( array( 'public' => true ) ) as $type ) {
1366-
foreach ( $block_templates as $block_template ) {
1367-
if ( ! $block_template->is_custom ) {
1368-
continue;
1369-
}
1376+
if ( isset( $block_template->post_types ) && ! in_array( $type, $block_template->post_types, true ) ) {
1377+
continue;
1378+
}
13701379

1371-
if ( isset( $block_template->post_types ) && ! in_array( $type, $block_template->post_types, true ) ) {
1372-
continue;
1380+
$post_templates[ $type ][ $block_template->slug ] = $block_template->title;
13731381
}
1374-
1375-
$post_templates[ $type ][ $block_template->slug ] = $block_template->title;
13761382
}
13771383
}
1384+
1385+
$this->cache_add( 'post_templates', $post_templates );
13781386
}
13791387

13801388
if ( $this->load_textdomain() ) {

tests/phpunit/tests/admin/includesTheme.php

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,72 @@ public function test_get_post_templates_child_theme() {
222222
);
223223
}
224224

225+
/**
226+
* @ticket 42513
227+
*
228+
* @covers WP_Theme::get_post_templates
229+
*/
230+
public function test_get_post_templates_caches_results() {
231+
$theme = wp_get_theme( 'page-templates' );
232+
$this->assertNotEmpty( $theme );
233+
234+
switch_theme( $theme['Template'], $theme['Stylesheet'] );
235+
236+
// First call populates the cache.
237+
$first_result = $theme->get_post_templates();
238+
$this->assertNotEmpty( $first_result );
239+
240+
// Second call should return the same result from cache.
241+
$second_result = $theme->get_post_templates();
242+
$this->assertSame( $first_result, $second_result );
243+
}
244+
245+
/**
246+
* @ticket 42513
247+
*
248+
* @covers WP_Theme::get_post_templates
249+
*/
250+
public function test_get_post_templates_uses_get_file_data() {
251+
$theme = wp_get_theme( 'page-templates' );
252+
$this->assertNotEmpty( $theme );
253+
254+
switch_theme( $theme['Template'], $theme['Stylesheet'] );
255+
256+
$post_templates = $theme->get_post_templates();
257+
258+
// Verify single-line header format is parsed correctly via get_file_data().
259+
$this->assertArrayHasKey( 'page', $post_templates );
260+
$this->assertArrayHasKey( 'template-header.php', $post_templates['page'] );
261+
$this->assertSame( 'This Template Header Is On One Line', $post_templates['page']['template-header.php'] );
262+
}
263+
264+
/**
265+
* @ticket 42513
266+
*
267+
* @covers WP_Theme::get_post_templates
268+
*/
269+
public function test_get_post_templates_cache_cleared_on_switch() {
270+
$theme = wp_get_theme( 'page-templates' );
271+
$this->assertNotEmpty( $theme );
272+
273+
switch_theme( $theme['Template'], $theme['Stylesheet'] );
274+
275+
// Populate cache.
276+
$theme->get_post_templates();
277+
278+
// Clearing theme cache should require a fresh scan on next call.
279+
wp_clean_themes_cache();
280+
281+
$child_theme = wp_get_theme( 'page-templates-child' );
282+
switch_theme( $child_theme['Template'], $child_theme['Stylesheet'] );
283+
284+
$child_templates = $child_theme->get_post_templates();
285+
286+
// Child theme should include its own templates.
287+
$this->assertArrayHasKey( 'foo', $child_templates );
288+
$this->assertArrayHasKey( 'template-top-level-post-types-child.php', $child_templates['foo'] );
289+
}
290+
225291
/**
226292
* Test that the list of theme features pulled from the WordPress.org API returns the expected data structure.
227293
*

0 commit comments

Comments
 (0)