Make WordPress Core

Changeset 56978

Timestamp:
10/20/2023 07:06:46 PM (9 months ago)
Author:
joemcgill
Message:

Themes: Make caches for block patterns clearable.

In [56765], theme block pattern files were cached to a transient as a performance enhancement. However, transients are not easily clearable when caches are flushed on environments not using a persistent cache, which can lead to errors if the theme files are renamed, edited, or moved.

This changes the caching mechanism to use wp_cache_set() instead, and caches these values to the global group so they are still persistent on environments using an object cache, and will be cleared by a cache flush.

In addition, the helper _wp_get_block_patterns has been moved WP_Theme::get_block_patterns for consistency with other block related theme methods and cache helpers for these values, WP_Theme::get_pattern_cache and WP_Theme::set_pattern_cache, have been made private.

Relevant unit tests updated.

Props: afercia, flixos90, mukesh27, joemcgill.
Fixes #59633. See #59591, #59490.

Location:
trunk
Files:
3 edited
1 moved

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/block-patterns.php

    r56931 r56978  
    342342
    343343    foreach ( $themes as $theme ) {
    344         $patterns    = _wp_get_block_patterns( $theme );
     344        $patterns    = );
    345345        $dirpath     = $theme->get_stylesheet_directory() . '/patterns/';
    346346        $text_domain = $theme->get( 'TextDomain' );
     
    388388}
    389389add_action( 'init', '_register_theme_block_patterns' );
    390 
    391 /**
    392  * Gets block pattern data for a specified theme.
    393  * Each pattern is defined as a PHP file and defines
    394  *  its metadata using plugin-style headers. The minimum required definition is:
    395  *
    396  *      /**
    397  *       * Title: My Pattern
    398  *       * Slug: my-theme/my-pattern
    399  *       *
    400  *
    401  *  The output of the PHP source corresponds to the content of the pattern, e.g.:
    402  *
    403  *      <main><p><?php echo "Hello"; ?></p></main>
    404  *
    405  *  If applicable, this will collect from both parent and child theme.
    406  *
    407  *  Other settable fields include:
    408  *
    409  *    - Description
    410  *    - Viewport Width
    411  *    - Inserter         (yes/no)
    412  *    - Categories       (comma-separated values)
    413  *    - Keywords         (comma-separated values)
    414  *    - Block Types      (comma-separated values)
    415  *    - Post Types       (comma-separated values)
    416  *    - Template Types   (comma-separated values)
    417  *
    418  * @since 6.4.0
    419  * @access private
    420  *
    421  * @param WP_Theme $theme Theme object.
    422  * @return array Block pattern data.
    423  */
    424 function _wp_get_block_patterns( WP_Theme $theme ) {
    425     $can_use_cached = ! wp_is_development_mode( 'theme' );
    426 
    427     $pattern_data = $theme->get_pattern_cache();
    428     if ( is_array( $pattern_data ) ) {
    429         if ( $can_use_cached ) {
    430             return $pattern_data;
    431         }
    432         // If in development mode, clear pattern cache.
    433         $theme->delete_pattern_cache();
    434     }
    435 
    436     $dirpath      = $theme->get_stylesheet_directory() . '/patterns/';
    437     $pattern_data = array();
    438 
    439     if ( ! file_exists( $dirpath ) ) {
    440         if ( $can_use_cached ) {
    441             $theme->set_pattern_cache( $pattern_data );
    442         }
    443         return $pattern_data;
    444     }
    445     $files = glob( $dirpath . '*.php' );
    446     if ( ! $files ) {
    447         if ( $can_use_cached ) {
    448             $theme->set_pattern_cache( $pattern_data );
    449         }
    450         return $pattern_data;
    451     }
    452 
    453     $default_headers = array(
    454         'title'         => 'Title',
    455         'slug'          => 'Slug',
    456         'description'   => 'Description',
    457         'viewportWidth' => 'Viewport Width',
    458         'inserter'      => 'Inserter',
    459         'categories'    => 'Categories',
    460         'keywords'      => 'Keywords',
    461         'blockTypes'    => 'Block Types',
    462         'postTypes'     => 'Post Types',
    463         'templateTypes' => 'Template Types',
    464     );
    465 
    466     $properties_to_parse = array(
    467         'categories',
    468         'keywords',
    469         'blockTypes',
    470         'postTypes',
    471         'templateTypes',
    472     );
    473 
    474     foreach ( $files as $file ) {
    475         $pattern = get_file_data( $file, $default_headers );
    476 
    477         if ( empty( $pattern['slug'] ) ) {
    478             _doing_it_wrong(
    479                 __FUNCTION__,
    480                 sprintf(
    481                     /* translators: 1: file name. */
    482                     __( 'Could not register file "%s" as a block pattern ("Slug" field missing)' ),
    483                     $file
    484                 ),
    485                 '6.0.0'
    486             );
    487             continue;
    488         }
    489 
    490         if ( ! preg_match( '/^[A-z0-9\/_-]+$/', $pattern['slug'] ) ) {
    491             _doing_it_wrong(
    492                 __FUNCTION__,
    493                 sprintf(
    494                     /* translators: 1: file name; 2: slug value found. */
    495                     __( 'Could not register file "%1$s" as a block pattern (invalid slug "%2$s")' ),
    496                     $file,
    497                     $pattern['slug']
    498                 ),
    499                 '6.0.0'
    500             );
    501         }
    502 
    503         // Title is a required property.
    504         if ( ! $pattern['title'] ) {
    505             _doing_it_wrong(
    506                 __FUNCTION__,
    507                 sprintf(
    508                     /* translators: 1: file name. */
    509                     __( 'Could not register file "%s" as a block pattern ("Title" field missing)' ),
    510                     $file
    511                 ),
    512                 '6.0.0'
    513             );
    514             continue;
    515         }
    516 
    517         // For properties of type array, parse data as comma-separated.
    518         foreach ( $properties_to_parse as $property ) {
    519             if ( ! empty( $pattern[ $property ] ) ) {
    520                 $pattern[ $property ] = array_filter( wp_parse_list( (string) $pattern[ $property ] ) );
    521             } else {
    522                 unset( $pattern[ $property ] );
    523             }
    524         }
    525 
    526         // Parse properties of type int.
    527         $property = 'viewportWidth';
    528         if ( ! empty( $pattern[ $property ] ) ) {
    529             $pattern[ $property ] = (int) $pattern[ $property ];
    530         } else {
    531             unset( $pattern[ $property ] );
    532         }
    533 
    534         // Parse properties of type bool.
    535         $property = 'inserter';
    536         if ( ! empty( $pattern[ $property ] ) ) {
    537             $pattern[ $property ] = in_array(
    538                 strtolower( $pattern[ $property ] ),
    539                 array( 'yes', 'true' ),
    540                 true
    541             );
    542         } else {
    543             unset( $pattern[ $property ] );
    544         }
    545 
    546         $key = str_replace( $dirpath, '', $file );
    547 
    548         $pattern_data[ $key ] = $pattern;
    549     }
    550 
    551     if ( $can_use_cached ) {
    552         $theme->set_pattern_cache( $pattern_data );
    553     }
    554 
    555     return $pattern_data;
    556 }
  • trunk/src/wp-includes/class-wp-theme.php

    r56943 r56978  
    846846
    847847    /**
    848      * Gets block pattern cache.
    849      *
    850      * @since 6.4.0
    851      *
    852      * @return array|false Returns an array of patterns if cache is found, otherwise false.
    853      */
    854     public function get_pattern_cache() {
    855         if ( ! $this->exists() ) {
    856             return false;
    857         }
    858         $pattern_data = get_transient( 'wp_theme_patterns_' . $this->stylesheet );
    859         if ( is_array( $pattern_data ) && $pattern_data['version'] === $this->get( 'Version' ) ) {
    860             return $pattern_data['patterns'];
    861         }
    862         return false;
    863     }
    864 
    865     /**
    866      * Sets block pattern cache.
    867      *
    868      * @since 6.4.0
    869      *
    870      * @param array $patterns Block patterns data to set in cache.
    871      */
    872     public function set_pattern_cache( array $patterns ) {
    873         $pattern_data = array(
    874             'version'  => $this->get( 'Version' ),
    875             'patterns' => $patterns,
    876         );
    877         set_transient( 'wp_theme_patterns_' . $this->stylesheet, $pattern_data );
    878     }
    879 
    880     /**
    881      * Clears block pattern cache.
    882      *
    883      * @since 6.4.0
    884      */
    885     public function delete_pattern_cache() {
    886         delete_transient( 'wp_theme_patterns_' . $this->stylesheet );
    887     }
    888 
    889     /**
    890848     * Gets a raw, unformatted theme header.
    891849     *
     
    18421800
    18431801    /**
     1802
     1803
     1804
     1805
     1806
     1807
     1808
     1809
     1810
     1811
     1812
     1813
     1814
     1815
     1816
     1817
     1818
     1819
     1820
     1821
     1822
     1823
     1824
     1825
     1826
     1827
     1828
     1829
     1830
     1831
     1832
     1833
     1834
     1835
     1836
     1837
     1838
     1839
     1840
     1841
     1842
     1843
     1844
     1845
     1846
     1847
     1848
     1849
     1850
     1851
     1852
     1853
     1854
     1855
     1856
     1857
     1858
     1859
     1860
     1861
     1862
     1863
     1864
     1865
     1866
     1867
     1868
     1869
     1870
     1871
     1872
     1873
     1874
     1875
     1876
     1877
     1878
     1879
     1880
     1881
     1882
     1883
     1884
     1885
     1886
     1887
     1888
     1889
     1890
     1891
     1892
     1893
     1894
     1895
     1896
     1897
     1898
     1899
     1900
     1901
     1902
     1903
     1904
     1905
     1906
     1907
     1908
     1909
     1910
     1911
     1912
     1913
     1914
     1915
     1916
     1917
     1918
     1919
     1920
     1921
     1922
     1923
     1924
     1925
     1926
     1927
     1928
     1929
     1930
     1931
     1932
     1933
     1934
     1935
     1936
     1937
     1938
     1939
     1940
     1941
     1942
     1943
     1944
     1945
     1946
     1947
     1948
     1949
     1950
     1951
     1952
     1953
     1954
     1955
     1956
     1957
     1958
     1959
     1960
     1961
     1962
     1963
     1964
     1965
     1966
     1967
     1968
     1969
     1970
     1971
     1972
     1973
     1974
     1975
     1976
     1977
     1978
     1979
     1980
     1981
     1982
     1983
     1984
     1985
     1986
     1987
     1988
     1989
     1990
     1991
     1992
     1993
     1994
     1995
     1996
     1997
     1998
     1999
     2000
     2001
     2002
     2003
     2004
     2005
     2006
     2007
     2008
    18442009     * Enables a theme for all sites on the current network.
    18452010     *
  • trunk/tests/phpunit/tests/theme/wpGetGlobalStylesheet.php

    r56042 r56978  
    2727
    2828    public function tear_down() {
     29
     30
     31
    2932        // Reset the theme support.
    3033        if ( $this->remove_theme_support_at_teardown ) {
  • trunk/tests/phpunit/tests/theme/wpThemeGetBlockPatterns.php

    r56977 r56978  
    11<?php
    22/**
    3  * Tests for _wp_get_block_patterns.
     3 * Tests for get_block_patterns.
    44 *
    55 * @package WordPress
     
    88 *
    99 * @group blocks
     10
    1011 *
    11  * @covers ::_wp_get_block_patterns
     12 * @covers get_block_patterns
    1213 */
    13 class Tests_Blocks_WpGetBlockPatterns extends WP_UnitTestCase {
     14class Tests_Theme_WPThemeGetBlockPatterns extends WP_UnitTestCase {
     15
     16    public static function wpSetUpBeforeClass() {
     17        // Ensure development mode is reset before running these tests.
     18        unset( $GLOBALS['_wp_tests_development_mode'] );
     19    }
     20
     21    public static function wpTearDownAfterClass() {
     22        // Ensure development mode is reset after running these tests.
     23        unset( $GLOBALS['_wp_tests_development_mode'] );
     24    }
     25
     26    /**
     27     * Test helper to access the private get_pattern_cache method of a theme.
     28     *
     29     * @param WP_Theme $wp_theme A WP_Theme object.
     30     * @return array|false Returns an array of patterns if cache is found, otherwise false.
     31     */
     32    private function get_pattern_cache( $wp_theme ) {
     33        $reflection = new ReflectionMethod( $wp_theme, 'get_pattern_cache' );
     34        $reflection->setAccessible( true );
     35
     36        $pattern_cache = $reflection->invoke( $wp_theme, 'get_pattern_cache' );
     37        $reflection->setAccessible( false );
     38
     39        return $pattern_cache;
     40    }
     41
    1442    /**
    1543     * @ticket 59490
    1644     *
    17      * @dataProvider data_wp_get_block_patterns
     45     * @dataProvider data_get_block_patterns
    1846     *
    19      * @param string $theme    The theme's slug.
    20      * @param array  $expected The expected pattern data.
     47     * @param string $theme The theme's slug.
     48     * @param array  $expected The expected pattern data.
    2149     */
    22     public function test_should_return_block_patterns( $theme, $expected ) {
    23         $patterns = _wp_get_block_patterns( wp_get_theme( $theme ) );
     50    public function test_should_return_block_patterns( $theme_slug, $expected ) {
     51        $theme    = wp_get_theme( $theme_slug );
     52        $patterns = $theme->get_block_patterns();
    2453        $this->assertSameSets( $expected, $patterns );
    2554    }
     
    2756    /**
    2857     * @ticket 59490
     58
     59
    2960     */
    30     public function test_delete_theme_cache() {
     61    public function test_delete__cache() {
    3162        $theme = wp_get_theme( 'block-theme-patterns' );
    32         _wp_get_block_patterns( $theme );
     63
     64        $this->assertTrue( $theme->exists(), 'The test theme could not be found.' );
     65
     66        $theme->get_block_patterns();
     67
    3368        $this->assertSameSets(
    3469            array(
     
    4075                ),
    4176            ),
    42             $theme->get_pattern_cache(),
    43             'The transient for block theme patterns should be set'
     77            $th),
     78            'The '
    4479        );
    4580        $theme->delete_pattern_cache();
    4681        $this->assertFalse(
    47             $theme->get_pattern_cache(),
    48             'The transient for block theme patterns should have been cleared'
     82            $th),
     83            'The '
    4984        );
    5085    }
     
    5388     * @ticket 59490
    5489     */
    55     public function test_should_clear_transient_after_switching_theme() {
     90    public function test_should_clear__after_switching_theme() {
    5691        switch_theme( 'block-theme' );
    5792        $theme1 = wp_get_theme();
    58         _wp_get_block_patterns( $theme1 );
     93
     94        $this->assertTrue( $theme1->exists(), 'The block-theme test theme could not be found.' );
     95
     96        $theme1->get_block_patterns();
    5997        $this->assertSameSets(
    6098            array(),
    61             $theme1->get_pattern_cache(),
    62             'The transient for block theme should be set'
     99            $th),
     100            'The '
    63101        );
     102
    64103        switch_theme( 'block-theme-patterns' );
    65         $this->assertFalse( $theme1->get_pattern_cache(), 'Transient should not be set for block theme after switch theme' );
     104
    66105        $theme2 = wp_get_theme();
    67         $this->assertFalse( $theme2->get_pattern_cache(), 'Transient should not be set for block theme patterns before being requested' );
    68         _wp_get_block_patterns( $theme2 );
     106        $this->assertTrue( $theme2->exists(), 'The block-theme-patterns test theme could not be found.' );
     107
     108        $this->assertFalse( $this->get_pattern_cache( $theme1 ), 'Cache should not be set for block theme after switch theme.' );
     109        $this->assertFalse( $this->get_pattern_cache( $theme2 ), 'Cache should not be set for block theme patterns before being requested.' );
     110
     111        $theme2->get_block_patterns( $theme2 );
    69112        $this->assertSameSets(
    70113            array(
     
    77120
    78121            ),
    79             $theme2->get_pattern_cache(),
    80             'The transient for block theme patterns should be set'
     122            $th),
     123            'The '
    81124        );
    82125    }
     
    87130     * @return array[]
    88131     */
    89     public function data_wp_get_block_patterns() {
     132    public function data_get_block_patterns() {
    90133        return array(
    91134            array(
     
    120163
    121164    /**
    122      * Tests that _wp_get_block_patterns() clears existing transient when in theme development mode.
     165     * Tests that when in theme development mode.
    123166     *
    124167     * @ticket 59591
    125168     */
    126     public function test_should_clear_existing_transient_when_in_development_mode() {
     169    public function test_should_clear_existing__when_in_development_mode() {
    127170        $theme = wp_get_theme( 'block-theme-patterns' );
    128171
     172
     173
    129174        // Calling the function should set the cache.
    130         _wp_get_block_patterns( $theme );
     175        );
    131176        $this->assertSameSets(
    132177            array(
     
    138183                ),
    139184            ),
    140             $theme->get_pattern_cache(),
    141             'The transient for block theme patterns should be set'
     185            $th),
     186            'The '
    142187        );
    143188
    144189        // Calling the function while in theme development mode should clear the cache.
    145190        $GLOBALS['_wp_tests_development_mode'] = 'theme';
    146         _wp_get_block_patterns( $theme );
     191        get_block_patterns( $theme );
    147192        unset( $GLOBALS['_wp_tests_development_mode'] ); // Reset to not pollute other tests.
    148193        $this->assertFalse(
    149             $theme->get_pattern_cache(),
    150             'The transient for block theme patterns should have been cleared due to theme development mode'
     194            $th),
     195            'The '
    151196        );
    152197    }
Note: See TracChangeset for help on using the changeset viewer.