Make WordPress Core

Changeset 57129


Ignore:
Timestamp:
11/20/2023 10:27:17 PM (4 months ago)
Author:
joemcgill
Message:

Themes: Remove memoization from stylesheet and theme directories.

This fixes bugs introduced in [56635] whereby the template or stylesheet path could be memoized incorrectly if get_template_directory() or get_stylesheet_directory() were called before the theme has been fully initialized.

Props partyfrikadelle, coreyw, kdowns, rebasaurus, meta4, flixos90, mukesh27, joemcgill.
Fixes #59847.

Location:
trunk
Files:
9 added
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/ms-blogs.php

    r56974 r57129  
    492492 * @global bool            $switched
    493493 * @global string          $table_prefix
    494  * @global string          $wp_template_path
    495  * @global string          $wp_stylesheet_path
    496494 * @global WP_Object_Cache $wp_object_cache
    497495 *
     
    535533
    536534    $wpdb->set_blog_id( $new_blog_id );
    537     $GLOBALS['table_prefix']       = $wpdb->get_blog_prefix();
    538     $GLOBALS['blog_id']            = $new_blog_id;
    539     $GLOBALS['wp_template_path']   = null;
    540     $GLOBALS['wp_stylesheet_path'] = null;
     535    $GLOBALS['table_prefix'] = $wpdb->get_blog_prefix();
     536    $GLOBALS['blog_id']      = $new_blog_id;
    541537
    542538    if ( function_exists( 'wp_cache_switch_to_blog' ) ) {
     
    605601 * @global bool            $switched
    606602 * @global string          $table_prefix
    607  * @global string          $wp_template_path
    608  * @global string          $wp_stylesheet_path
    609603 * @global WP_Object_Cache $wp_object_cache
    610604 *
     
    632626
    633627    $wpdb->set_blog_id( $new_blog_id );
    634     $GLOBALS['blog_id']            = $new_blog_id;
    635     $GLOBALS['table_prefix']       = $wpdb->get_blog_prefix();
    636     $GLOBALS['wp_template_path']   = null;
    637     $GLOBALS['wp_stylesheet_path'] = null;
     628    $GLOBALS['blog_id']      = $new_blog_id;
     629    $GLOBALS['table_prefix'] = $wpdb->get_blog_prefix();
    638630
    639631    if ( function_exists( 'wp_cache_switch_to_blog' ) ) {
  • trunk/src/wp-includes/theme.php

    r57009 r57129  
    189189 * @since 1.5.0
    190190 * @since 6.4.0 Memoizes filter execution so that it only runs once for the current theme.
    191  *
    192  * @global string $wp_stylesheet_path Current theme stylesheet directory path.
     191 * @since 6.4.2 Memoization removed.
    193192 *
    194193 * @return string Path to active theme's stylesheet directory.
    195194 */
    196195function get_stylesheet_directory() {
    197     global $wp_stylesheet_path;
    198 
    199     if ( null === $wp_stylesheet_path ) {
    200         $stylesheet     = get_stylesheet();
    201         $theme_root     = get_theme_root( $stylesheet );
    202         $stylesheet_dir = "$theme_root/$stylesheet";
    203 
    204         /**
    205          * Filters the stylesheet directory path for the active theme.
    206          *
    207          * @since 1.5.0
    208          *
    209          * @param string $stylesheet_dir Absolute path to the active theme.
    210          * @param string $stylesheet     Directory name of the active theme.
    211          * @param string $theme_root     Absolute path to themes directory.
    212          */
    213         $stylesheet_dir = apply_filters( 'stylesheet_directory', $stylesheet_dir, $stylesheet, $theme_root );
    214 
    215         // If there are filter callbacks, force the logic to execute on every call.
    216         if ( has_filter( 'stylesheet' ) || has_filter( 'theme_root' ) || has_filter( 'stylesheet_directory' ) ) {
    217             return $stylesheet_dir;
    218         }
    219 
    220         $wp_stylesheet_path = $stylesheet_dir;
    221     }
    222 
    223     return $wp_stylesheet_path;
     196    $stylesheet     = get_stylesheet();
     197    $theme_root     = get_theme_root( $stylesheet );
     198    $stylesheet_dir = "$theme_root/$stylesheet";
     199
     200    /**
     201     * Filters the stylesheet directory path for the active theme.
     202     *
     203     * @since 1.5.0
     204     *
     205     * @param string $stylesheet_dir Absolute path to the active theme.
     206     * @param string $stylesheet     Directory name of the active theme.
     207     * @param string $theme_root     Absolute path to themes directory.
     208     */
     209    return apply_filters( 'stylesheet_directory', $stylesheet_dir, $stylesheet, $theme_root );
    224210}
    225211
     
    339325 * @since 1.5.0
    340326 * @since 6.4.0 Memoizes filter execution so that it only runs once for the current theme.
    341  *
    342  * @global string $wp_template_path Current theme template directory path.
     327 * @since 6.4.1 Memoization removed.
    343328 *
    344329 * @return string Path to active theme's template directory.
    345330 */
    346331function get_template_directory() {
    347     global $wp_template_path;
    348 
    349     if ( null === $wp_template_path ) {
    350         $template     = get_template();
    351         $theme_root   = get_theme_root( $template );
    352         $template_dir = "$theme_root/$template";
    353 
    354         /**
    355          * Filters the active theme directory path.
    356          *
    357          * @since 1.5.0
    358          *
    359          * @param string $template_dir The path of the active theme directory.
    360          * @param string $template     Directory name of the active theme.
    361          * @param string $theme_root   Absolute path to the themes directory.
    362          */
    363         $template_dir = apply_filters( 'template_directory', $template_dir, $template, $theme_root );
    364 
    365         // If there are filter callbacks, force the logic to execute on every call.
    366         if ( has_filter( 'template' ) || has_filter( 'theme_root' ) || has_filter( 'template_directory' ) ) {
    367             return $template_dir;
    368         }
    369 
    370         $wp_template_path = $template_dir;
    371     }
    372 
    373     return $wp_template_path;
     332    $template     = get_template();
     333    $theme_root   = get_theme_root( $template );
     334    $template_dir = "$theme_root/$template";
     335
     336    /**
     337     * Filters the active theme directory path.
     338     *
     339     * @since 1.5.0
     340     *
     341     * @param string $template_dir The path of the active theme directory.
     342     * @param string $template     Directory name of the active theme.
     343     * @param string $theme_root   Absolute path to the themes directory.
     344     */
     345    return apply_filters( 'template_directory', $template_dir, $template, $theme_root );
    374346}
    375347
     
    777749 * @global array                $sidebars_widgets
    778750 * @global array                $wp_registered_sidebars
    779  * @global string               $wp_stylesheet_path
    780  * @global string               $wp_template_path
    781751 *
    782752 * @param string $stylesheet Stylesheet name.
    783753 */
    784754function switch_theme( $stylesheet ) {
    785     global $wp_theme_directories, $wp_customize, $sidebars_widgets, $wp_registered_sidebars, $wp_stylesheet_path, $wp_template_path;
     755    global $wp_theme_directories, $wp_customize, $sidebars_widgets, $wp_registered_sidebars;
    786756
    787757    $requirements = validate_theme_requirements( $stylesheet );
     
    866836
    867837    update_option( 'theme_switched', $old_theme->get_stylesheet() );
    868 
    869     /*
    870      * Reset globals to force refresh the next time these directories are
    871      * accessed via `get_stylesheet_directory()` / `get_template_directory()`.
    872      */
    873     $wp_stylesheet_path = null;
    874     $wp_template_path   = null;
    875838
    876839    // Clear pattern caches.
  • trunk/tests/phpunit/tests/theme.php

    r57104 r57129  
    11801180        }
    11811181    }
     1182
     1183    /**
     1184     * Make sure filters added after the initial call are fired.
     1185     *
     1186     * @ticket 59847
     1187     *
     1188     * @covers ::get_stylesheet_directory
     1189     */
     1190    public function test_get_stylesheet_directory_filters_apply() {
     1191        // Call the function prior to the filter being added.
     1192        get_stylesheet_directory();
     1193
     1194        $expected = 'test_root/dir';
     1195
     1196        // Add the filer.
     1197        add_filter(
     1198            'stylesheet_directory',
     1199            function () use ( $expected ) {
     1200                return $expected;
     1201            }
     1202        );
     1203
     1204        $this->assertSame( $expected, get_stylesheet_directory() );
     1205    }
     1206
     1207    /**
     1208     * Make sure filters added after the initial call are fired.
     1209     *
     1210     * @ticket 59847
     1211     *
     1212     * @covers ::get_template_directory
     1213     */
     1214    public function test_get_template_directory_filters_apply() {
     1215        // Call the function prior to the filter being added.
     1216        get_template_directory();
     1217
     1218        $expected = 'test_root/dir';
     1219
     1220        // Add the filer.
     1221        add_filter(
     1222            'template_directory',
     1223            function () use ( $expected ) {
     1224                return $expected;
     1225            }
     1226        );
     1227
     1228        $this->assertSame( $expected, get_template_directory() );
     1229    }
     1230
     1231    /**
     1232     * Make sure get_stylesheet_directory uses the correct path when the root theme dir changes.
     1233     *
     1234     * @ticket 59847
     1235     *
     1236     * @covers ::get_stylesheet_directory
     1237     */
     1238    public function test_get_stylesheet_directory_uses_registered_theme_dir() {
     1239        $old_theme = wp_get_theme();
     1240
     1241        switch_theme( 'test' );
     1242
     1243        $old_root = get_theme_root( 'test' );
     1244        $path1    = get_stylesheet_directory();
     1245
     1246        $new_root = DIR_TESTDATA . '/themedir2';
     1247        register_theme_directory( $new_root );
     1248
     1249        // Mock the stylesheet root option to mimic that the active root has changed.
     1250        add_filter(
     1251            'pre_option_stylesheet_root',
     1252            function () use ( $new_root ) {
     1253                return $new_root;
     1254            }
     1255        );
     1256
     1257        $path2 = get_stylesheet_directory();
     1258
     1259        // Cleanup.
     1260        switch_theme( $old_theme->get_stylesheet() );
     1261
     1262        $this->assertEquals( $old_root . '/test', $path1, 'The original stylesheet path is not correct' );
     1263        $this->assertEquals( $new_root . '/test', $path2, 'The new stylesheet path is not correct' );
     1264    }
     1265
     1266    /**
     1267     * Make sure get_template_directory uses the correct path when the root theme dir changes.
     1268     *
     1269     * @ticket 59847
     1270     *
     1271     * @covers ::get_template_directory
     1272     */
     1273    public function test_get_template_directory_uses_registered_theme_dir() {
     1274        $old_theme = wp_get_theme();
     1275
     1276        switch_theme( 'test' );
     1277
     1278        // Mock parent theme to be returned as the template.
     1279        add_filter(
     1280            'pre_option_template',
     1281            function () {
     1282                return 'test-parent';
     1283            }
     1284        );
     1285
     1286        $old_root = get_theme_root( 'test' );
     1287        $path1    = get_template_directory();
     1288
     1289        $new_root = DIR_TESTDATA . '/themedir2';
     1290        register_theme_directory( $new_root );
     1291
     1292        // Mock the template root option to mimic that the active root has changed.
     1293        add_filter(
     1294            'pre_option_template_root',
     1295            function () use ( $new_root ) {
     1296                return $new_root;
     1297            }
     1298        );
     1299
     1300        $path2 = get_template_directory();
     1301
     1302        // Cleanup.
     1303        switch_theme( $old_theme->get_stylesheet() );
     1304
     1305        $this->assertEquals( $old_root . '/test-parent', $path1, 'The original template path is not correct' );
     1306        $this->assertEquals( $new_root . '/test-parent', $path2, 'The new template path is not correct' );
     1307    }
    11821308}
Note: See TracChangeset for help on using the changeset viewer.