@@ -319,37 +319,132 @@ function _register_remote_theme_patterns() {
319319
320320/**
321321 * Register any patterns that the active theme may provide under its
322- * `./patterns/` directory. Each pattern is defined as a PHP file and defines
323- * its metadata using plugin-style headers. The minimum required definition is:
322+ * `./patterns/` directory.
324323 *
325- * /**
326- * * Title: My Pattern
327- * * Slug: my-theme/my-pattern
328- * *
324+ * @since 6.0.0
325+ * @since 6.1.0 The `postTypes` property was added.
326+ * @since 6.2.0 The `templateTypes` property was added.
327+ * @since 6.4.0 Uses the `_wp_get_block_patterns` function.
328+ * @access private
329+ */
330+ function _register_theme_block_patterns () {
331+ /*
332+ * Register patterns for the active theme. If the theme is a child theme,
333+ * let it override any patterns from the parent theme that shares the same slug.
334+ */
335+ $ themes = array ();
336+ $ theme = wp_get_theme ();
337+ $ themes [] = $ theme ;
338+ if ( $ theme ->parent () ) {
339+ $ themes [] = $ theme ->parent ();
340+ }
341+ $ registry = WP_Block_Patterns_Registry::get_instance ();
342+
343+ foreach ( $ themes as $ theme ) {
344+ $ pattern_data = _wp_get_block_patterns ( $ theme );
345+ $ dirpath = $ theme ->get_stylesheet_directory () . '/patterns/ ' ;
346+ $ text_domain = $ theme ->get ( 'TextDomain ' );
347+
348+ foreach ( $ pattern_data ['patterns ' ] as $ file => $ pattern_data ) {
349+ if ( $ registry ->is_registered ( $ pattern_data ['slug ' ] ) ) {
350+ continue ;
351+ }
352+
353+ // The actual pattern content is the output of the file.
354+ ob_start ();
355+ include $ dirpath . $ file ;
356+ $ pattern_data ['content ' ] = ob_get_clean ();
357+ if ( ! $ pattern_data ['content ' ] ) {
358+ continue ;
359+ }
360+
361+ // Translate the pattern metadata.
362+ // phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralText,WordPress.WP.I18n.NonSingularStringLiteralDomain,WordPress.WP.I18n.LowLevelTranslationFunction
363+ $ pattern_data ['title ' ] = translate_with_gettext_context ( $ pattern_data ['title ' ], 'Pattern title ' , $ text_domain );
364+ if ( ! empty ( $ pattern_data ['description ' ] ) ) {
365+ // phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralText,WordPress.WP.I18n.NonSingularStringLiteralDomain,WordPress.WP.I18n.LowLevelTranslationFunction
366+ $ pattern_data ['description ' ] = translate_with_gettext_context ( $ pattern_data ['description ' ], 'Pattern description ' , $ text_domain );
367+ }
368+
369+ register_block_pattern ( $ pattern_data ['slug ' ], $ pattern_data );
370+ }
371+ }
372+ }
373+ add_action ( 'init ' , '_register_theme_block_patterns ' );
374+
375+ /**
376+ * Gets block pattern data for a specified theme.
377+ * Each pattern is defined as a PHP file and defines
378+ * its metadata using plugin-style headers. The minimum required definition is:
329379 *
330- * The output of the PHP source corresponds to the content of the pattern, e.g.:
380+ * /**
381+ * * Title: My Pattern
382+ * * Slug: my-theme/my-pattern
383+ * *
331384 *
332- * <main><p><?php echo "Hello"; ?></p></main>
385+ * The output of the PHP source corresponds to the content of the pattern, e.g.:
333386 *
334- * If applicable, this will collect from both parent and child theme.
387+ * <main><p><?php echo "Hello"; ?></p></main>
335388 *
336- * Other settable fields include:
389+ * If applicable, this will collect from both parent and child theme.
337390 *
338- * - Description
339- * - Viewport Width
340- * - Inserter (yes/no)
341- * - Categories (comma-separated values)
342- * - Keywords (comma-separated values)
343- * - Block Types (comma-separated values)
344- * - Post Types (comma-separated values)
345- * - Template Types (comma-separated values)
391+ * Other settable fields include:
346392 *
347- * @since 6.0.0
348- * @since 6.1.0 The `postTypes` property was added.
349- * @since 6.2.0 The `templateTypes` property was added.
393+ * - Description
394+ * - Viewport Width
395+ * - Inserter (yes/no)
396+ * - Categories (comma-separated values)
397+ * - Keywords (comma-separated values)
398+ * - Block Types (comma-separated values)
399+ * - Post Types (comma-separated values)
400+ * - Template Types (comma-separated values)
401+ *
402+ * @since 6.4.0
350403 * @access private
404+ *
405+ * @param WP_Theme $theme Theme object.
406+ * @return array Block pattern data.
351407 */
352- function _register_theme_block_patterns () {
408+
409+ function _wp_get_block_patterns ( WP_Theme $ theme ) {
410+ if ( ! $ theme ->exists () ) {
411+ return array (
412+ 'version ' => false ,
413+ 'patterns ' => array (),
414+ );
415+ }
416+
417+ $ transient_name = 'wp_theme_patterns_ ' . $ theme ->get_stylesheet ();
418+ $ version = $ theme ->get ( 'Version ' );
419+ $ can_use_cached = ! wp_is_development_mode ( 'theme ' );
420+
421+ if ( $ can_use_cached ) {
422+ $ pattern_data = get_transient ( $ transient_name );
423+ if ( is_array ( $ pattern_data ) && $ pattern_data ['version ' ] === $ version ) {
424+ return $ pattern_data ;
425+ }
426+ }
427+
428+ $ pattern_data = array (
429+ 'version ' => $ version ,
430+ 'patterns ' => array (),
431+ );
432+ $ dirpath = $ theme ->get_stylesheet_directory () . '/patterns/ ' ;
433+
434+ if ( ! file_exists ( $ dirpath ) ) {
435+ if ( $ can_use_cached ) {
436+ set_transient ( $ transient_name , $ pattern_data );
437+ }
438+ return $ pattern_data ;
439+ }
440+ $ files = glob ( $ dirpath . '*.php ' );
441+ if ( ! $ files ) {
442+ if ( $ can_use_cached ) {
443+ set_transient ( $ transient_name , $ pattern_data );
444+ }
445+ return $ pattern_data ;
446+ }
447+
353448 $ default_headers = array (
354449 'title ' => 'Title ' ,
355450 'slug ' => 'Slug ' ,
@@ -363,130 +458,94 @@ function _register_theme_block_patterns() {
363458 'templateTypes ' => 'Template Types ' ,
364459 );
365460
366- /*
367- * Register patterns for the active theme. If the theme is a child theme,
368- * let it override any patterns from the parent theme that shares the same slug.
369- */
370- $ themes = array ();
371- $ stylesheet = get_stylesheet ();
372- $ template = get_template ();
373- if ( $ stylesheet !== $ template ) {
374- $ themes [] = wp_get_theme ( $ stylesheet );
375- }
376- $ themes [] = wp_get_theme ( $ template );
461+ $ properties_to_parse = array (
462+ 'categories ' ,
463+ 'keywords ' ,
464+ 'blockTypes ' ,
465+ 'postTypes ' ,
466+ 'templateTypes ' ,
467+ );
377468
378- foreach ( $ themes as $ theme ) {
379- $ dirpath = $ theme ->get_stylesheet_directory () . '/patterns/ ' ;
380- if ( ! is_dir ( $ dirpath ) || ! is_readable ( $ dirpath ) ) {
469+ foreach ( $ files as $ file ) {
470+ $ pattern = get_file_data ( $ file , $ default_headers );
471+
472+ if ( empty ( $ pattern ['slug ' ] ) ) {
473+ _doing_it_wrong (
474+ __FUNCTION__ ,
475+ sprintf (
476+ /* translators: %s: file name. */
477+ __ ( 'Could not register file "%s" as a block pattern ("Slug" field missing) ' ),
478+ $ file
479+ ),
480+ '6.0.0 '
481+ );
381482 continue ;
382483 }
383- if ( file_exists ( $ dirpath ) ) {
384- $ files = glob ( $ dirpath . '*.php ' );
385- if ( $ files ) {
386- foreach ( $ files as $ file ) {
387- $ pattern_data = get_file_data ( $ file , $ default_headers );
388-
389- if ( empty ( $ pattern_data ['slug ' ] ) ) {
390- _doing_it_wrong (
391- '_register_theme_block_patterns ' ,
392- sprintf (
393- /* translators: %s: file name. */
394- __ ( 'Could not register file "%s" as a block pattern ("Slug" field missing) ' ),
395- $ file
396- ),
397- '6.0.0 '
398- );
399- continue ;
400- }
401-
402- if ( ! preg_match ( '/^[A-z0-9\/_-]+$/ ' , $ pattern_data ['slug ' ] ) ) {
403- _doing_it_wrong (
404- '_register_theme_block_patterns ' ,
405- sprintf (
406- /* translators: %1s: file name; %2s: slug value found. */
407- __ ( 'Could not register file "%1$s" as a block pattern (invalid slug "%2$s") ' ),
408- $ file ,
409- $ pattern_data ['slug ' ]
410- ),
411- '6.0.0 '
412- );
413- }
414-
415- if ( WP_Block_Patterns_Registry::get_instance ()->is_registered ( $ pattern_data ['slug ' ] ) ) {
416- continue ;
417- }
418-
419- // Title is a required property.
420- if ( ! $ pattern_data ['title ' ] ) {
421- _doing_it_wrong (
422- '_register_theme_block_patterns ' ,
423- sprintf (
424- /* translators: %1s: file name; %2s: slug value found. */
425- __ ( 'Could not register file "%s" as a block pattern ("Title" field missing) ' ),
426- $ file
427- ),
428- '6.0.0 '
429- );
430- continue ;
431- }
432-
433- // For properties of type array, parse data as comma-separated.
434- foreach ( array ( 'categories ' , 'keywords ' , 'blockTypes ' , 'postTypes ' , 'templateTypes ' ) as $ property ) {
435- if ( ! empty ( $ pattern_data [ $ property ] ) ) {
436- $ pattern_data [ $ property ] = array_filter (
437- preg_split (
438- '/[\s,]+/ ' ,
439- (string ) $ pattern_data [ $ property ]
440- )
441- );
442- } else {
443- unset( $ pattern_data [ $ property ] );
444- }
445- }
446-
447- // Parse properties of type int.
448- foreach ( array ( 'viewportWidth ' ) as $ property ) {
449- if ( ! empty ( $ pattern_data [ $ property ] ) ) {
450- $ pattern_data [ $ property ] = (int ) $ pattern_data [ $ property ];
451- } else {
452- unset( $ pattern_data [ $ property ] );
453- }
454- }
455-
456- // Parse properties of type bool.
457- foreach ( array ( 'inserter ' ) as $ property ) {
458- if ( ! empty ( $ pattern_data [ $ property ] ) ) {
459- $ pattern_data [ $ property ] = in_array (
460- strtolower ( $ pattern_data [ $ property ] ),
461- array ( 'yes ' , 'true ' ),
462- true
463- );
464- } else {
465- unset( $ pattern_data [ $ property ] );
466- }
467- }
468-
469- // Translate the pattern metadata.
470- $ text_domain = $ theme ->get ( 'TextDomain ' );
471- // phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralText,WordPress.WP.I18n.NonSingularStringLiteralDomain,WordPress.WP.I18n.LowLevelTranslationFunction
472- $ pattern_data ['title ' ] = translate_with_gettext_context ( $ pattern_data ['title ' ], 'Pattern title ' , $ text_domain );
473- if ( ! empty ( $ pattern_data ['description ' ] ) ) {
474- // phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralText,WordPress.WP.I18n.NonSingularStringLiteralDomain,WordPress.WP.I18n.LowLevelTranslationFunction
475- $ pattern_data ['description ' ] = translate_with_gettext_context ( $ pattern_data ['description ' ], 'Pattern description ' , $ text_domain );
476- }
477-
478- // The actual pattern content is the output of the file.
479- ob_start ();
480- include $ file ;
481- $ pattern_data ['content ' ] = ob_get_clean ();
482- if ( ! $ pattern_data ['content ' ] ) {
483- continue ;
484- }
485-
486- register_block_pattern ( $ pattern_data ['slug ' ], $ pattern_data );
487- }
484+
485+ if ( ! preg_match ( '/^[A-z0-9\/_-]+$/ ' , $ pattern ['slug ' ] ) ) {
486+ _doing_it_wrong (
487+ __FUNCTION__ ,
488+ sprintf (
489+ /* translators: %1s: file name; %2s: slug value found. */
490+ __ ( 'Could not register file "%1$s" as a block pattern (invalid slug "%2$s") ' ),
491+ $ file ,
492+ $ pattern ['slug ' ]
493+ ),
494+ '6.0.0 '
495+ );
496+ }
497+
498+ // Title is a required property.
499+ if ( ! $ pattern ['title ' ] ) {
500+ _doing_it_wrong (
501+ __FUNCTION__ ,
502+ sprintf (
503+ /* translators: %1s: file name. */
504+ __ ( 'Could not register file "%s" as a block pattern ("Title" field missing) ' ),
505+ $ file
506+ ),
507+ '6.0.0 '
508+ );
509+ continue ;
510+ }
511+
512+ // For properties of type array, parse data as comma-separated.
513+ foreach ( $ properties_to_parse as $ property ) {
514+ if ( ! empty ( $ pattern [ $ property ] ) ) {
515+ $ pattern [ $ property ] = array_filter ( wp_parse_list ( (string ) $ pattern [ $ property ] ) );
516+ } else {
517+ unset( $ pattern [ $ property ] );
488518 }
489519 }
520+
521+ // Parse properties of type int.
522+ $ property = 'viewportWidth ' ;
523+ if ( ! empty ( $ pattern [ $ property ] ) ) {
524+ $ pattern [ $ property ] = (int ) $ pattern [ $ property ];
525+ } else {
526+ unset( $ pattern [ $ property ] );
527+ }
528+
529+ // Parse properties of type bool.
530+ $ property = 'inserter ' ;
531+ if ( ! empty ( $ pattern [ $ property ] ) ) {
532+ $ pattern [ $ property ] = in_array (
533+ strtolower ( $ pattern [ $ property ] ),
534+ array ( 'yes ' , 'true ' ),
535+ true
536+ );
537+ } else {
538+ unset( $ pattern [ $ property ] );
539+ }
540+
541+ $ key = str_replace ( $ dirpath , '' , $ file );
542+
543+ $ pattern_data ['patterns ' ][ $ key ] = $ pattern ;
490544 }
545+
546+ if ( $ can_use_cached ) {
547+ set_transient ( $ transient_name , $ pattern_data );
548+ }
549+
550+ return $ pattern_data ;
491551}
492- add_action ( 'init ' , '_register_theme_block_patterns ' );
0 commit comments