Make WordPress Core

Changeset 56635


Ignore:
Timestamp:
09/20/2023 05:25:26 PM (3 years 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.