Make WordPress Core

Changeset 56635


Ignore:
Timestamp:
09/20/2023 05:25:26 PM (7 months ago)
Author:
flixos90
Message:

Themes: Deprecate usage of TEMPLATEPATH and STYLESHEETPATH constants.

While generally the functions get_template_directory() and get_stylesheet_directory() were long recommended to use to get the parent or child theme directory, the TEMPLATEPATH and STYLESHEETPATH constants were still used in a few places in core, most importantly in template related logic.

The remaining usage was problematic as it prevented testability of certain key components of WordPress core.

This changeset replaces all remaining usage with the corresponding functions and effectively marks these constants as deprecated. It also adds test coverage accordingly and even unlocks some existing, previously commented out test coverage to work as expected.

Performance of the new approach has been benchmarked and shows no notable differences. Yet, given that the current theme directories are not expected to change within a regular WordPress page load, the get_template_directory() and get_stylesheet_directory() functions were amended with in-memory caching of the result, unless one of the defining values is being filtered.

Props thekt12, spacedmonkey, mukesh27, aaroncampbell, scribu, lloydbudd, cais, chipbennett, toscho, omarabid, CrazyJaco, DrewAPicture, obenland, wonderboymusic, nacin, helen, dd32, chriscct7, SergeyBiryukov, swissspidy, joemcgill, flixos90.
Fixes #18298.

Location:
trunk
Files:
11 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-admin/includes/theme.php

    r56571 r56635  
    11671167     */
    11681168    if ( ! empty( $redirect ) ) {
     1169        $stylesheet_path = get_stylesheet_directory();
     1170        $template_path   = get_template_directory();
     1171
    11691172        $functions_path = '';
    1170         if ( str_contains( STYLESHEETPATH, $extension ) ) {
    1171             $functions_path = STYLESHEETPATH . '/functions.php';
    1172         } elseif ( str_contains( TEMPLATEPATH, $extension ) ) {
    1173             $functions_path = TEMPLATEPATH . '/functions.php';
     1173        if ( str_contains( $stylesheet_path, $extension ) ) {
     1174            $functions_path = $stylesheet_path . '/functions.php';
     1175        } elseif ( str_contains( $template_path, $extension ) ) {
     1176            $functions_path = $template_path . '/functions.php';
    11741177        }
    11751178
  • trunk/src/wp-includes/comment-template.php

    r56549 r56635  
    13821382 *
    13831383 * The `$file` path is passed through a filter hook called {@see 'comments_template'},
    1384  * which includes the TEMPLATEPATH and $file combined. Tries the $filtered path
     1384 * which includes the template directory and $file combined. Tries the $filtered path
    13851385 * first and if it fails it will require the default comment template from the
    13861386 * default theme. If either does not exist, then the WordPress process will be
     
    16011601    }
    16021602
    1603     $theme_template = STYLESHEETPATH . $file;
     1603    $stylesheet_path = get_stylesheet_directory();
     1604    $template_path   = get_template_directory();
     1605
     1606    $theme_template = $stylesheet_path . $file;
    16041607
    16051608    /**
     
    16141617    if ( file_exists( $include ) ) {
    16151618        require $include;
    1616     } elseif ( file_exists( TEMPLATEPATH . $file ) ) {
    1617         require TEMPLATEPATH . $file;
     1619    } elseif ( file_exists( $template_path . $file ) ) {
     1620        require $template_path . $file;
    16181621    } else { // Backward compat code will be removed in a future release.
    16191622        require ABSPATH . WPINC . '/theme-compat/comments.php';
  • trunk/src/wp-includes/default-constants.php

    r56548 r56635  
    408408     *
    409409     * @since 1.5.0
     410     * @deprecated 6.4.0 Use get_template_directory() instead.
     411     * @see get_template_directory()
    410412     */
    411413    define( 'TEMPLATEPATH', get_template_directory() );
     
    415417     *
    416418     * @since 2.1.0
     419     * @deprecated 6.4.0 Use get_stylesheet_directory() instead.
     420     * @see get_stylesheet_directory()
    417421     */
    418422    define( 'STYLESHEETPATH', get_stylesheet_directory() );
  • trunk/src/wp-includes/load.php

    r56548 r56635  
    10501050    }
    10511051
    1052     if ( TEMPLATEPATH !== STYLESHEETPATH ) {
    1053         $themes[] = STYLESHEETPATH;
    1054     }
    1055 
    1056     $themes[] = TEMPLATEPATH;
     1052    $stylesheet_path = get_stylesheet_directory();
     1053    $template_path   = get_template_directory();
     1054
     1055    if ( $template_path !== $stylesheet_path ) {
     1056        $themes[] = $stylesheet_path;
     1057    }
     1058
     1059    $themes[] = $template_path;
    10571060
    10581061    /*
  • trunk/src/wp-includes/template.php

    r56357 r56635  
    685685 * Retrieves the name of the highest priority template file that exists.
    686686 *
    687  * Searches in the STYLESHEETPATH before TEMPLATEPATH and wp-includes/theme-compat
    688  * so that themes which inherit from a parent theme can just overload one file.
     687 * Searches in the stylesheet directory before the template directory and
     688 * wp-includes/theme-compat so that themes which inherit from a parent theme
     689 * can just overload one file.
    689690 *
    690691 * @since 2.7.0
     
    700701 */
    701702function locate_template( $template_names, $load = false, $load_once = true, $args = array() ) {
     703    $stylesheet_path = get_stylesheet_directory();
     704    $template_path   = get_template_directory();
     705    $is_child_theme  = $stylesheet_path !== $template_path;
     706
    702707    $located = '';
    703708    foreach ( (array) $template_names as $template_name ) {
     
    705710            continue;
    706711        }
    707         if ( file_exists( STYLESHEETPATH . '/' . $template_name ) ) {
    708             $located = STYLESHEETPATH . '/' . $template_name;
     712        if ( file_exists( $stylesheet_path . '/' . $template_name ) ) {
     713            $located = $stylesheet_path . '/' . $template_name;
    709714            break;
    710         } elseif ( is_child_theme() && file_exists( TEMPLATEPATH . '/' . $template_name ) ) {
    711             $located = TEMPLATEPATH . '/' . $template_name;
     715        } elseif ( $is_child_theme && file_exists( $template_path . '/' . $template_name ) ) {
     716            $located = $template_path . '/' . $template_name;
    712717            break;
    713718        } elseif ( file_exists( ABSPATH . WPINC . '/theme-compat/' . $template_name ) ) {
  • trunk/src/wp-includes/theme.php

    r56414 r56635  
    158158 */
    159159function is_child_theme() {
    160     return ( TEMPLATEPATH !== STYLESHEETPATH );
     160    return get_template_directory() !== get_stylesheet_directory();
    161161}
    162162
     
    188188 *
    189189 * @since 1.5.0
     190 * @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.
    190193 *
    191194 * @return string Path to active theme's stylesheet directory.
    192195 */
    193196function get_stylesheet_directory() {
    194     $stylesheet     = get_stylesheet();
    195     $theme_root     = get_theme_root( $stylesheet );
    196     $stylesheet_dir = "$theme_root/$stylesheet";
    197 
    198     /**
    199      * Filters the stylesheet directory path for the active theme.
    200      *
    201      * @since 1.5.0
    202      *
    203      * @param string $stylesheet_dir Absolute path to the active theme.
    204      * @param string $stylesheet     Directory name of the active theme.
    205      * @param string $theme_root     Absolute path to themes directory.
    206      */
    207     return apply_filters( 'stylesheet_directory', $stylesheet_dir, $stylesheet, $theme_root );
     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;
    208224}
    209225
     
    322338 *
    323339 * @since 1.5.0
     340 * @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.
    324343 *
    325344 * @return string Path to active theme's template directory.
    326345 */
    327346function get_template_directory() {
    328     $template     = get_template();
    329     $theme_root   = get_theme_root( $template );
    330     $template_dir = "$theme_root/$template";
    331 
    332     /**
    333      * Filters the active theme directory path.
    334      *
    335      * @since 1.5.0
    336      *
    337      * @param string $template_dir The path of the active theme directory.
    338      * @param string $template     Directory name of the active theme.
    339      * @param string $theme_root   Absolute path to the themes directory.
    340      */
    341     return apply_filters( 'template_directory', $template_dir, $template, $theme_root );
     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;
    342374}
    343375
     
    745777 * @global array                $sidebars_widgets
    746778 * @global array                $wp_registered_sidebars
     779 * @global string               $wp_stylesheet_path
     780 * @global string               $wp_template_path
    747781 *
    748782 * @param string $stylesheet Stylesheet name.
    749783 */
    750784function switch_theme( $stylesheet ) {
    751     global $wp_theme_directories, $wp_customize, $sidebars_widgets, $wp_registered_sidebars;
     785    global $wp_theme_directories, $wp_customize, $sidebars_widgets, $wp_registered_sidebars, $wp_stylesheet_path, $wp_template_path;
    752786
    753787    $requirements = validate_theme_requirements( $stylesheet );
     
    832866
    833867    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;
    834875
    835876    /**
  • trunk/tests/phpunit/tests/comment/commentsTemplate.php

    r53863 r56635  
    1111
    1212    /**
     13     * Performs setup tasks for every test.
     14     */
     15    public function set_up() {
     16        parent::set_up();
     17        switch_theme( 'default' );
     18    }
     19
     20    /**
    1321     * @ticket 8071
    1422     */
  • trunk/tests/phpunit/tests/comment/wpListComments.php

    r53863 r56635  
    77 */
    88class Tests_Comment_WpListComments extends WP_UnitTestCase {
     9
     10    /**
     11     * Performs setup tasks for every test.
     12     */
     13    public function set_up() {
     14        parent::set_up();
     15        switch_theme( 'default' );
     16    }
     17
    918    /**
    1019     * @ticket 35175
  • trunk/tests/phpunit/tests/general/template.php

    r54090 r56635  
    1111
    1212class Tests_General_Template extends WP_UnitTestCase {
     13
    1314    protected $wp_site_icon;
    1415    public $site_icon_id;
     
    4243        parent::set_up();
    4344
     45        switch_theme( 'default' );
    4446        $this->wp_site_icon = new WP_Site_Icon();
    4547    }
  • trunk/tests/phpunit/tests/template.php

    r56547 r56635  
    628628            ),
    629629        );
     630    }
     631
     632    /**
     633     * Tests that `locate_template()` uses the current theme even after switching the theme.
     634     *
     635     * @ticket 18298
     636     *
     637     * @covers ::locate_template
     638     */
     639    public function test_locate_template_uses_current_theme() {
     640        $themes = wp_get_themes();
     641
     642        // Look for parent themes with an index.php template.
     643        $relevant_themes = array();
     644        foreach ( $themes as $theme ) {
     645            if ( $theme->get_stylesheet() !== $theme->get_template() ) {
     646                continue;
     647            }
     648            $php_templates = $theme['Template Files'];
     649            if ( ! isset( $php_templates['index.php'] ) ) {
     650                continue;
     651            }
     652            $relevant_themes[] = $theme;
     653        }
     654        if ( count( $relevant_themes ) < 2 ) {
     655            $this->markTestSkipped( 'Test requires at least two parent themes with an index.php template.' );
     656        }
     657
     658        $template_names = array( 'index.php' );
     659
     660        $old_theme = $relevant_themes[0];
     661        $new_theme = $relevant_themes[1];
     662
     663        switch_theme( $old_theme->get_stylesheet() );
     664        $this->assertSame( $old_theme->get_stylesheet_directory() . '/index.php', locate_template( $template_names ), 'Incorrect index template found in initial theme.' );
     665
     666        switch_theme( $new_theme->get_stylesheet() );
     667        $this->assertSame( $new_theme->get_stylesheet_directory() . '/index.php', locate_template( $template_names ), 'Incorrect index template found in theme after switch.' );
    630668    }
    631669
  • trunk/tests/phpunit/tests/theme.php

    r54663 r56635  
    3737        parent::set_up();
    3838
     39        // Sets up the `wp-content/themes/` directory to ensure consistency when running tests.
    3940        $this->orig_theme_dir = $wp_theme_directories;
    40         $wp_theme_directories = array( WP_CONTENT_DIR . '/themes' );
     41        $wp_theme_directories = array( WP_CONTENT_DIR . '/themes', realpath( DIR_TESTDATA . '/themedir1' ) );
    4142
    4243        add_filter( 'extra_theme_headers', array( $this, 'theme_data_extra_headers' ) );
     
    283284        for ( $i = 0; $i < 3; $i++ ) {
    284285            foreach ( $themes as $name => $theme ) {
     286                // Skip invalid theme directory names (such as `block_theme-[0.4.0]`).
     287                if ( ! preg_match( '/^[a-z0-9-]+$/', $theme['Stylesheet'] ) ) {
     288                    continue;
     289                }
     290
    285291                // Switch to this theme.
    286292                if ( 2 === $i ) {
     
    290296                }
    291297
    292                 $this->assertSame( $name, get_current_theme() );
     298                $this->assertSame( $theme['Name'], get_current_theme() );
    293299
    294300                // Make sure the various get_* functions return the correct values.
     
    296302                $this->assertSame( $theme['Stylesheet'], get_stylesheet() );
    297303
    298                 $root_fs = get_theme_root();
     304                $root_fs = $theme->get_theme_root();
    299305                $this->assertTrue( is_dir( $root_fs ) );
    300306
    301                 $root_uri = get_theme_root_uri();
     307                $root_uri = $theme->get_theme_root_uri();
    302308                $this->assertNotEmpty( $root_uri );
    303309
     
    310316                $this->assertSame( $root_uri . '/' . get_template(), get_template_directory_uri() );
    311317
    312                 // get_query_template()
     318                // Skip block themes for get_query_template() tests since this test is focused on classic templates.
     319                if ( wp_is_block_theme() && current_theme_supports( 'block-templates' ) ) {
     320                    continue;
     321                }
    313322
    314323                // Template file that doesn't exist.
     
    316325
    317326                // Template files that do exist.
    318                 /*
    319327                foreach ( $theme['Template Files'] as $path ) {
    320                     $file = basename($path, '.php');
    321                     FIXME: untestable because get_query_template() uses TEMPLATEPATH.
    322                     $this->assertSame('', get_query_template($file));
     328                    $file = basename( $path, '.php' );
     329
     330                    // The functions.php file is not a template.
     331                    if ( 'functions' === $file ) {
     332                        continue;
     333                    }
     334
     335                    // Underscores are not supported by `locate_template()`.
     336                    if ( 'taxonomy-post_format' === $file ) {
     337                        $file = 'taxonomy';
     338                    }
     339
     340                    $child_theme_file  = get_stylesheet_directory() . '/' . $file . '.php';
     341                    $parent_theme_file = get_template_directory() . '/' . $file . '.php';
     342                    if ( file_exists( $child_theme_file ) ) {
     343                        $this->assertSame( $child_theme_file, get_query_template( $file ) );
     344                    } elseif ( file_exists( $parent_theme_file ) ) {
     345                        $this->assertSame( $parent_theme_file, get_query_template( $file ) );
     346                    } else {
     347                        $this->assertSame( '', get_query_template( $file ) );
     348                    }
    323349                }
    324                 */
    325350
    326351                // These are kind of tautologies but at least exercise the code.
     
    856881
    857882    /**
     883     * Tests that a theme in the custom test data theme directory is recognized.
     884     *
     885     * @ticket 18298
     886     */
     887    public function test_theme_in_custom_theme_dir_is_valid() {
     888        switch_theme( 'block-theme' );
     889        $this->assertTrue( wp_get_theme()->exists() );
     890    }
     891
     892    /**
     893     * Tests that `is_child_theme()` returns true for child theme.
     894     *
     895     * @ticket 18298
     896     *
     897     * @covers ::is_child_theme
     898     */
     899    public function test_is_child_theme_true() {
     900        switch_theme( 'block-theme-child' );
     901        $this->assertTrue( is_child_theme() );
     902    }
     903
     904    /**
     905     * Tests that `is_child_theme()` returns false for parent theme.
     906     *
     907     * @ticket 18298
     908     *
     909     * @covers ::is_child_theme
     910     */
     911    public function test_is_child_theme_false() {
     912        switch_theme( 'block-theme' );
     913        $this->assertFalse( is_child_theme() );
     914    }
     915
     916    /**
     917     * Tests that the child theme directory is correctly detected.
     918     *
     919     * @ticket 18298
     920     *
     921     * @covers ::get_stylesheet_directory
     922     */
     923    public function test_get_stylesheet_directory() {
     924        switch_theme( 'block-theme-child' );
     925        $this->assertSame( realpath( DIR_TESTDATA ) . '/themedir1/block-theme-child', get_stylesheet_directory() );
     926    }
     927
     928    /**
     929     * Tests that the parent theme directory is correctly detected.
     930     *
     931     * @ticket 18298
     932     *
     933     * @covers ::get_template_directory
     934     */
     935    public function test_get_template_directory() {
     936        switch_theme( 'block-theme-child' );
     937        $this->assertSame( realpath( DIR_TESTDATA ) . '/themedir1/block-theme', get_template_directory() );
     938    }
     939
     940    /**
     941     * Tests that get_stylesheet_directory() behaves correctly with filters.
     942     *
     943     * @ticket 18298
     944     * @dataProvider data_get_stylesheet_directory_with_filter
     945     *
     946     * @covers ::get_stylesheet_directory
     947     *
     948     * @param string   $theme     Theme slug / directory name.
     949     * @param string   $hook_name Filter hook name.
     950     * @param callable $callback  Filter callback.
     951     * @param string   $expected  Expected stylesheet directory with the filter active.
     952     */
     953    public function test_get_stylesheet_directory_with_filter( $theme, $hook_name, $callback, $expected ) {
     954        switch_theme( $theme );
     955
     956        // Add filter, then call get_stylesheet_directory() to compute value.
     957        add_filter( $hook_name, $callback );
     958        $this->assertSame( $expected, get_stylesheet_directory(), 'Stylesheet directory returned incorrect result not considering filters' );
     959
     960        // Remove filter again, then ensure result is recalculated and not the same as before.
     961        remove_filter( $hook_name, $callback );
     962        $this->assertNotSame( $expected, get_stylesheet_directory(), 'Stylesheet directory returned previous value even though filters were removed' );
     963    }
     964
     965    /**
     966     * Data provider for `test_get_stylesheet_directory_with_filter()`.
     967     *
     968     * @return array[]
     969     */
     970    public function data_get_stylesheet_directory_with_filter() {
     971        return array(
     972            'with stylesheet_directory filter' => array(
     973                'block-theme',
     974                'stylesheet_directory',
     975                static function ( $dir ) {
     976                    return str_replace( realpath( DIR_TESTDATA ) . '/themedir1', '/fantasy-dir', $dir );
     977                },
     978                '/fantasy-dir/block-theme',
     979            ),
     980            'with theme_root filter'           => array(
     981                'block-theme',
     982                'theme_root',
     983                static function () {
     984                    return '/fantasy-dir';
     985                },
     986                '/fantasy-dir/block-theme',
     987            ),
     988            'with stylesheet filter'           => array(
     989                'block-theme',
     990                'stylesheet',
     991                static function () {
     992                    return 'another-theme';
     993                },
     994                // Because the theme does not exist, `get_theme_root()` returns the default themes directory.
     995                WP_CONTENT_DIR . '/themes/another-theme',
     996            ),
     997        );
     998    }
     999
     1000    /**
     1001     * Tests that get_template_directory() behaves correctly with filters.
     1002     *
     1003     * @ticket 18298
     1004     * @dataProvider data_get_template_directory_with_filter
     1005     *
     1006     * @covers ::get_template_directory
     1007     *
     1008     * @param string   $theme     Theme slug / directory name.
     1009     * @param string   $hook_name Filter hook name.
     1010     * @param callable $callback  Filter callback.
     1011     * @param string   $expected  Expected template directory with the filter active.
     1012     */
     1013    public function test_get_template_directory_with_filter( $theme, $hook_name, $callback, $expected ) {
     1014        switch_theme( $theme );
     1015
     1016        // Add filter, then call get_template_directory() to compute value.
     1017        add_filter( $hook_name, $callback );
     1018        $this->assertSame( $expected, get_template_directory(), 'Template directory returned incorrect result not considering filters' );
     1019
     1020        // Remove filter again, then ensure result is recalculated and not the same as before.
     1021        remove_filter( $hook_name, $callback );
     1022        $this->assertNotSame( $expected, get_template_directory(), 'Template directory returned previous value even though filters were removed' );
     1023    }
     1024
     1025    /**
     1026     * Data provider for `test_get_template_directory_with_filter()`.
     1027     *
     1028     * @return array[]
     1029     */
     1030    public function data_get_template_directory_with_filter() {
     1031        return array(
     1032            'with template_directory filter' => array(
     1033                'block-theme',
     1034                'template_directory',
     1035                static function ( $dir ) {
     1036                    return str_replace( realpath( DIR_TESTDATA ) . '/themedir1', '/fantasy-dir', $dir );
     1037                },
     1038                '/fantasy-dir/block-theme',
     1039            ),
     1040            'with theme_root filter'         => array(
     1041                'block-theme',
     1042                'theme_root',
     1043                static function () {
     1044                    return '/fantasy-dir';
     1045                },
     1046                '/fantasy-dir/block-theme',
     1047            ),
     1048            'with template filter'           => array(
     1049                'block-theme',
     1050                'template',
     1051                static function () {
     1052                    return 'another-theme';
     1053                },
     1054                // Because the theme does not exist, `get_theme_root()` returns the default themes directory.
     1055                WP_CONTENT_DIR . '/themes/another-theme',
     1056            ),
     1057        );
     1058    }
     1059
     1060    /**
    8581061     * Helper function to ensure that a block theme is available and active.
    8591062     */
Note: See TracChangeset for help on using the changeset viewer.