| [ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Template loading functions. 4 * 5 * @package WordPress 6 * @subpackage Template 7 */ 8 9 /** 10 * Retrieves path to a template. 11 * 12 * Used to quickly retrieve the path of a template without including the file 13 * extension. It will also check the parent theme, if the file exists, with 14 * the use of locate_template(). Allows for more generic template location 15 * without the use of the other get_*_template() functions. 16 * 17 * @since 1.5.0 18 * 19 * @param string $type Filename without extension. 20 * @param string[] $templates An optional list of template candidates. 21 * @return string Full path to template file. 22 */ 23 function get_query_template( $type, $templates = array() ) { 24 $type = preg_replace( '|[^a-z0-9-]+|', '', $type ); 25 26 if ( empty( $templates ) ) { 27 $templates = array( "{$type}.php" ); 28 } 29 30 /** 31 * Filters the list of template filenames that are searched for when retrieving a template to use. 32 * 33 * The dynamic portion of the hook name, `$type`, refers to the filename -- minus the file 34 * extension and any non-alphanumeric characters delimiting words -- of the file to load. 35 * The last element in the array should always be the fallback template for this query type. 36 * 37 * Possible hook names include: 38 * 39 * - `404_template_hierarchy` 40 * - `archive_template_hierarchy` 41 * - `attachment_template_hierarchy` 42 * - `author_template_hierarchy` 43 * - `category_template_hierarchy` 44 * - `date_template_hierarchy` 45 * - `embed_template_hierarchy` 46 * - `frontpage_template_hierarchy` 47 * - `home_template_hierarchy` 48 * - `index_template_hierarchy` 49 * - `page_template_hierarchy` 50 * - `paged_template_hierarchy` 51 * - `privacypolicy_template_hierarchy` 52 * - `search_template_hierarchy` 53 * - `single_template_hierarchy` 54 * - `singular_template_hierarchy` 55 * - `tag_template_hierarchy` 56 * - `taxonomy_template_hierarchy` 57 * 58 * @since 4.7.0 59 * 60 * @param string[] $templates A list of template candidates, in descending order of priority. 61 */ 62 $templates = apply_filters( "{$type}_template_hierarchy", $templates ); 63 64 $template = locate_template( $templates ); 65 66 $template = locate_block_template( $template, $type, $templates ); 67 68 /** 69 * Filters the path of the queried template by type. 70 * 71 * The dynamic portion of the hook name, `$type`, refers to the filename -- minus the file 72 * extension and any non-alphanumeric characters delimiting words -- of the file to load. 73 * This hook also applies to various types of files loaded as part of the Template Hierarchy. 74 * 75 * Possible hook names include: 76 * 77 * - `404_template` 78 * - `archive_template` 79 * - `attachment_template` 80 * - `author_template` 81 * - `category_template` 82 * - `date_template` 83 * - `embed_template` 84 * - `frontpage_template` 85 * - `home_template` 86 * - `index_template` 87 * - `page_template` 88 * - `paged_template` 89 * - `privacypolicy_template` 90 * - `search_template` 91 * - `single_template` 92 * - `singular_template` 93 * - `tag_template` 94 * - `taxonomy_template` 95 * 96 * @since 1.5.0 97 * @since 4.8.0 The `$type` and `$templates` parameters were added. 98 * 99 * @param string $template Path to the template. See locate_template(). 100 * @param string $type Sanitized filename without extension. 101 * @param string[] $templates A list of template candidates, in descending order of priority. 102 */ 103 return apply_filters( "{$type}_template", $template, $type, $templates ); 104 } 105 106 /** 107 * Retrieves path of index template in current or parent template. 108 * 109 * The template hierarchy and template path are filterable via the {@see '$type_template_hierarchy'} 110 * and {@see '$type_template'} dynamic hooks, where `$type` is 'index'. 111 * 112 * @since 3.0.0 113 * 114 * @see get_query_template() 115 * 116 * @return string Full path to index template file. 117 */ 118 function get_index_template() { 119 return get_query_template( 'index' ); 120 } 121 122 /** 123 * Retrieves path of 404 template in current or parent template. 124 * 125 * The template hierarchy and template path are filterable via the {@see '$type_template_hierarchy'} 126 * and {@see '$type_template'} dynamic hooks, where `$type` is '404'. 127 * 128 * @since 1.5.0 129 * 130 * @see get_query_template() 131 * 132 * @return string Full path to 404 template file. 133 */ 134 function get_404_template() { 135 return get_query_template( '404' ); 136 } 137 138 /** 139 * Retrieves path of archive template in current or parent template. 140 * 141 * The template hierarchy and template path are filterable via the {@see '$type_template_hierarchy'} 142 * and {@see '$type_template'} dynamic hooks, where `$type` is 'archive'. 143 * 144 * @since 1.5.0 145 * 146 * @see get_query_template() 147 * 148 * @return string Full path to archive template file. 149 */ 150 function get_archive_template() { 151 $post_types = array_filter( (array) get_query_var( 'post_type' ) ); 152 153 $templates = array(); 154 155 if ( count( $post_types ) === 1 ) { 156 $post_type = reset( $post_types ); 157 $templates[] = "archive-{$post_type}.php"; 158 } 159 $templates[] = 'archive.php'; 160 161 return get_query_template( 'archive', $templates ); 162 } 163 164 /** 165 * Retrieves path of post type archive template in current or parent template. 166 * 167 * The template hierarchy and template path are filterable via the {@see '$type_template_hierarchy'} 168 * and {@see '$type_template'} dynamic hooks, where `$type` is 'archive'. 169 * 170 * @since 3.7.0 171 * 172 * @see get_archive_template() 173 * 174 * @return string Full path to archive template file. 175 */ 176 function get_post_type_archive_template() { 177 $post_type = get_query_var( 'post_type' ); 178 if ( is_array( $post_type ) ) { 179 $post_type = reset( $post_type ); 180 } 181 182 $obj = get_post_type_object( $post_type ); 183 if ( ! ( $obj instanceof WP_Post_Type ) || ! $obj->has_archive ) { 184 return ''; 185 } 186 187 return get_archive_template(); 188 } 189 190 /** 191 * Retrieves path of author template in current or parent template. 192 * 193 * The hierarchy for this template looks like: 194 * 195 * 1. author-{nicename}.php 196 * 2. author-{id}.php 197 * 3. author.php 198 * 199 * An example of this is: 200 * 201 * 1. author-john.php 202 * 2. author-1.php 203 * 3. author.php 204 * 205 * The template hierarchy and template path are filterable via the {@see '$type_template_hierarchy'} 206 * and {@see '$type_template'} dynamic hooks, where `$type` is 'author'. 207 * 208 * @since 1.5.0 209 * 210 * @see get_query_template() 211 * 212 * @return string Full path to author template file. 213 */ 214 function get_author_template() { 215 $author = get_queried_object(); 216 217 $templates = array(); 218 219 if ( $author instanceof WP_User ) { 220 $templates[] = "author-{$author->user_nicename}.php"; 221 $templates[] = "author-{$author->ID}.php"; 222 } 223 $templates[] = 'author.php'; 224 225 return get_query_template( 'author', $templates ); 226 } 227 228 /** 229 * Retrieves path of category template in current or parent template. 230 * 231 * The hierarchy for this template looks like: 232 * 233 * 1. category-{slug}.php 234 * 2. category-{id}.php 235 * 3. category.php 236 * 237 * An example of this is: 238 * 239 * 1. category-news.php 240 * 2. category-2.php 241 * 3. category.php 242 * 243 * The template hierarchy and template path are filterable via the {@see '$type_template_hierarchy'} 244 * and {@see '$type_template'} dynamic hooks, where `$type` is 'category'. 245 * 246 * @since 1.5.0 247 * @since 4.7.0 The decoded form of `category-{slug}.php` was added to the top of the 248 * template hierarchy when the category slug contains multibyte characters. 249 * 250 * @see get_query_template() 251 * 252 * @return string Full path to category template file. 253 */ 254 function get_category_template() { 255 $category = get_queried_object(); 256 257 $templates = array(); 258 259 if ( ! empty( $category->slug ) ) { 260 261 $slug_decoded = urldecode( $category->slug ); 262 if ( $slug_decoded !== $category->slug ) { 263 $templates[] = "category-{$slug_decoded}.php"; 264 } 265 266 $templates[] = "category-{$category->slug}.php"; 267 $templates[] = "category-{$category->term_id}.php"; 268 } 269 $templates[] = 'category.php'; 270 271 return get_query_template( 'category', $templates ); 272 } 273 274 /** 275 * Retrieves path of tag template in current or parent template. 276 * 277 * The hierarchy for this template looks like: 278 * 279 * 1. tag-{slug}.php 280 * 2. tag-{id}.php 281 * 3. tag.php 282 * 283 * An example of this is: 284 * 285 * 1. tag-wordpress.php 286 * 2. tag-3.php 287 * 3. tag.php 288 * 289 * The template hierarchy and template path are filterable via the {@see '$type_template_hierarchy'} 290 * and {@see '$type_template'} dynamic hooks, where `$type` is 'tag'. 291 * 292 * @since 2.3.0 293 * @since 4.7.0 The decoded form of `tag-{slug}.php` was added to the top of the 294 * template hierarchy when the tag slug contains multibyte characters. 295 * 296 * @see get_query_template() 297 * 298 * @return string Full path to tag template file. 299 */ 300 function get_tag_template() { 301 $tag = get_queried_object(); 302 303 $templates = array(); 304 305 if ( ! empty( $tag->slug ) ) { 306 307 $slug_decoded = urldecode( $tag->slug ); 308 if ( $slug_decoded !== $tag->slug ) { 309 $templates[] = "tag-{$slug_decoded}.php"; 310 } 311 312 $templates[] = "tag-{$tag->slug}.php"; 313 $templates[] = "tag-{$tag->term_id}.php"; 314 } 315 $templates[] = 'tag.php'; 316 317 return get_query_template( 'tag', $templates ); 318 } 319 320 /** 321 * Retrieves path of custom taxonomy term template in current or parent template. 322 * 323 * The hierarchy for this template looks like: 324 * 325 * 1. taxonomy-{taxonomy_slug}-{term_slug}.php 326 * 2. taxonomy-{taxonomy_slug}-{term_id}.php 327 * 3. taxonomy-{taxonomy_slug}.php 328 * 4. taxonomy.php 329 * 330 * An example of this is: 331 * 332 * 1. taxonomy-location-texas.php 333 * 2. taxonomy-location-67.php 334 * 3. taxonomy-location.php 335 * 4. taxonomy.php 336 * 337 * The template hierarchy and template path are filterable via the {@see '$type_template_hierarchy'} 338 * and {@see '$type_template'} dynamic hooks, where `$type` is 'taxonomy'. 339 * 340 * @since 2.5.0 341 * @since 4.7.0 The decoded form of `taxonomy-{taxonomy_slug}-{term_slug}.php` was added to the top of the 342 * template hierarchy when the term slug contains multibyte characters. 343 * @since 6.9.0 Added `taxonomy-{taxonomy_slug}-{term_id}.php` to the hierarchy. 344 * 345 * @see get_query_template() 346 * 347 * @return string Full path to custom taxonomy term template file. 348 */ 349 function get_taxonomy_template() { 350 $term = get_queried_object(); 351 352 $templates = array(); 353 354 if ( ! empty( $term->slug ) ) { 355 $taxonomy = $term->taxonomy; 356 357 $slug_decoded = urldecode( $term->slug ); 358 if ( $slug_decoded !== $term->slug ) { 359 $templates[] = "taxonomy-$taxonomy-{$slug_decoded}.php"; 360 } 361 362 $templates[] = "taxonomy-$taxonomy-{$term->slug}.php"; 363 $templates[] = "taxonomy-$taxonomy-{$term->term_id}.php"; 364 $templates[] = "taxonomy-$taxonomy.php"; 365 } 366 $templates[] = 'taxonomy.php'; 367 368 return get_query_template( 'taxonomy', $templates ); 369 } 370 371 /** 372 * Retrieves path of date template in current or parent template. 373 * 374 * The template hierarchy and template path are filterable via the {@see '$type_template_hierarchy'} 375 * and {@see '$type_template'} dynamic hooks, where `$type` is 'date'. 376 * 377 * @since 1.5.0 378 * 379 * @see get_query_template() 380 * 381 * @return string Full path to date template file. 382 */ 383 function get_date_template() { 384 return get_query_template( 'date' ); 385 } 386 387 /** 388 * Retrieves path of home template in current or parent template. 389 * 390 * The template hierarchy and template path are filterable via the {@see '$type_template_hierarchy'} 391 * and {@see '$type_template'} dynamic hooks, where `$type` is 'home'. 392 * 393 * @since 1.5.0 394 * 395 * @see get_query_template() 396 * 397 * @return string Full path to home template file. 398 */ 399 function get_home_template() { 400 $templates = array( 'home.php', 'index.php' ); 401 402 return get_query_template( 'home', $templates ); 403 } 404 405 /** 406 * Retrieves path of front page template in current or parent template. 407 * 408 * The template hierarchy and template path are filterable via the {@see '$type_template_hierarchy'} 409 * and {@see '$type_template'} dynamic hooks, where `$type` is 'frontpage'. 410 * 411 * @since 3.0.0 412 * 413 * @see get_query_template() 414 * 415 * @return string Full path to front page template file. 416 */ 417 function get_front_page_template() { 418 $templates = array( 'front-page.php' ); 419 420 return get_query_template( 'frontpage', $templates ); 421 } 422 423 /** 424 * Retrieves path of Privacy Policy page template in current or parent template. 425 * 426 * The template hierarchy and template path are filterable via the {@see '$type_template_hierarchy'} 427 * and {@see '$type_template'} dynamic hooks, where `$type` is 'privacypolicy'. 428 * 429 * @since 5.2.0 430 * 431 * @see get_query_template() 432 * 433 * @return string Full path to privacy policy template file. 434 */ 435 function get_privacy_policy_template() { 436 $templates = array( 'privacy-policy.php' ); 437 438 return get_query_template( 'privacypolicy', $templates ); 439 } 440 441 /** 442 * Retrieves path of page template in current or parent template. 443 * 444 * Note: For block themes, use locate_block_template() function instead. 445 * 446 * The hierarchy for this template looks like: 447 * 448 * 1. {Page Template}.php 449 * 2. page-{page_name}.php 450 * 3. page-{id}.php 451 * 4. page.php 452 * 453 * An example of this is: 454 * 455 * 1. page-templates/full-width.php 456 * 2. page-about.php 457 * 3. page-4.php 458 * 4. page.php 459 * 460 * The template hierarchy and template path are filterable via the {@see '$type_template_hierarchy'} 461 * and {@see '$type_template'} dynamic hooks, where `$type` is 'page'. 462 * 463 * @since 1.5.0 464 * @since 4.7.0 The decoded form of `page-{page_name}.php` was added to the top of the 465 * template hierarchy when the page name contains multibyte characters. 466 * 467 * @see get_query_template() 468 * 469 * @return string Full path to page template file. 470 */ 471 function get_page_template() { 472 $id = get_queried_object_id(); 473 $template = get_page_template_slug(); 474 $pagename = get_query_var( 'pagename' ); 475 476 if ( ! $pagename && $id ) { 477 /* 478 * If a static page is set as the front page, $pagename will not be set. 479 * Retrieve it from the queried object. 480 */ 481 $post = get_queried_object(); 482 if ( $post ) { 483 $pagename = $post->post_name; 484 } 485 } 486 487 $templates = array(); 488 if ( $template && 0 === validate_file( $template ) ) { 489 $templates[] = $template; 490 } 491 if ( $pagename ) { 492 $pagename_decoded = urldecode( $pagename ); 493 if ( $pagename_decoded !== $pagename && 0 === validate_file( $pagename_decoded ) ) { 494 $templates[] = "page-{$pagename_decoded}.php"; 495 } 496 $templates[] = "page-{$pagename}.php"; 497 } 498 if ( $id ) { 499 $templates[] = "page-{$id}.php"; 500 } 501 $templates[] = 'page.php'; 502 503 return get_query_template( 'page', $templates ); 504 } 505 506 /** 507 * Retrieves path of search template in current or parent template. 508 * 509 * The template hierarchy and template path are filterable via the {@see '$type_template_hierarchy'} 510 * and {@see '$type_template'} dynamic hooks, where `$type` is 'search'. 511 * 512 * @since 1.5.0 513 * 514 * @see get_query_template() 515 * 516 * @return string Full path to search template file. 517 */ 518 function get_search_template() { 519 return get_query_template( 'search' ); 520 } 521 522 /** 523 * Retrieves path of single template in current or parent template. Applies to single Posts, 524 * single Attachments, and single custom post types. 525 * 526 * The hierarchy for this template looks like: 527 * 528 * 1. {Post Type Template}.php 529 * 2. single-{post_type}-{post_name}.php 530 * 3. single-{post_type}.php 531 * 4. single.php 532 * 533 * An example of this is: 534 * 535 * 1. templates/full-width.php 536 * 2. single-post-hello-world.php 537 * 3. single-post.php 538 * 4. single.php 539 * 540 * The template hierarchy and template path are filterable via the {@see '$type_template_hierarchy'} 541 * and {@see '$type_template'} dynamic hooks, where `$type` is 'single'. 542 * 543 * @since 1.5.0 544 * @since 4.4.0 `single-{post_type}-{post_name}.php` was added to the top of the template hierarchy. 545 * @since 4.7.0 The decoded form of `single-{post_type}-{post_name}.php` was added to the top of the 546 * template hierarchy when the post name contains multibyte characters. 547 * @since 4.7.0 `{Post Type Template}.php` was added to the top of the template hierarchy. 548 * 549 * @see get_query_template() 550 * 551 * @return string Full path to single template file. 552 */ 553 function get_single_template() { 554 $object = get_queried_object(); 555 556 $templates = array(); 557 558 if ( ! empty( $object->post_type ) ) { 559 $template = get_page_template_slug( $object ); 560 if ( $template && 0 === validate_file( $template ) ) { 561 $templates[] = $template; 562 } 563 564 $name_decoded = urldecode( $object->post_name ); 565 if ( $name_decoded !== $object->post_name ) { 566 $templates[] = "single-{$object->post_type}-{$name_decoded}.php"; 567 } 568 569 $templates[] = "single-{$object->post_type}-{$object->post_name}.php"; 570 $templates[] = "single-{$object->post_type}.php"; 571 } 572 573 $templates[] = 'single.php'; 574 575 return get_query_template( 'single', $templates ); 576 } 577 578 /** 579 * Retrieves an embed template path in the current or parent template. 580 * 581 * The hierarchy for this template looks like: 582 * 583 * 1. embed-{post_type}-{post_format}.php 584 * 2. embed-{post_type}.php 585 * 3. embed.php 586 * 587 * An example of this is: 588 * 589 * 1. embed-post-audio.php 590 * 2. embed-post.php 591 * 3. embed.php 592 * 593 * The template hierarchy and template path are filterable via the {@see '$type_template_hierarchy'} 594 * and {@see '$type_template'} dynamic hooks, where `$type` is 'embed'. 595 * 596 * @since 4.5.0 597 * 598 * @see get_query_template() 599 * 600 * @return string Full path to embed template file. 601 */ 602 function get_embed_template() { 603 $object = get_queried_object(); 604 605 $templates = array(); 606 607 if ( ! empty( $object->post_type ) ) { 608 $post_format = get_post_format( $object ); 609 if ( $post_format ) { 610 $templates[] = "embed-{$object->post_type}-{$post_format}.php"; 611 } 612 $templates[] = "embed-{$object->post_type}.php"; 613 } 614 615 $templates[] = 'embed.php'; 616 617 return get_query_template( 'embed', $templates ); 618 } 619 620 /** 621 * Retrieves the path of the singular template in current or parent template. 622 * 623 * The template hierarchy and template path are filterable via the {@see '$type_template_hierarchy'} 624 * and {@see '$type_template'} dynamic hooks, where `$type` is 'singular'. 625 * 626 * @since 4.3.0 627 * 628 * @see get_query_template() 629 * 630 * @return string Full path to singular template file. 631 */ 632 function get_singular_template() { 633 return get_query_template( 'singular' ); 634 } 635 636 /** 637 * Retrieves path of attachment template in current or parent template. 638 * 639 * The hierarchy for this template looks like: 640 * 641 * 1. {mime_type}-{sub_type}.php 642 * 2. {sub_type}.php 643 * 3. {mime_type}.php 644 * 4. attachment.php 645 * 646 * An example of this is: 647 * 648 * 1. image-jpeg.php 649 * 2. jpeg.php 650 * 3. image.php 651 * 4. attachment.php 652 * 653 * The template hierarchy and template path are filterable via the {@see '$type_template_hierarchy'} 654 * and {@see '$type_template'} dynamic hooks, where `$type` is 'attachment'. 655 * 656 * @since 2.0.0 657 * @since 4.3.0 The order of the mime type logic was reversed so the hierarchy is more logical. 658 * 659 * @see get_query_template() 660 * 661 * @return string Full path to attachment template file. 662 */ 663 function get_attachment_template() { 664 $attachment = get_queried_object(); 665 666 $templates = array(); 667 668 if ( $attachment ) { 669 if ( str_contains( $attachment->post_mime_type, '/' ) ) { 670 list( $type, $subtype ) = explode( '/', $attachment->post_mime_type ); 671 } else { 672 list( $type, $subtype ) = array( $attachment->post_mime_type, '' ); 673 } 674 675 if ( ! empty( $subtype ) ) { 676 $templates[] = "{$type}-{$subtype}.php"; 677 $templates[] = "{$subtype}.php"; 678 } 679 $templates[] = "{$type}.php"; 680 } 681 $templates[] = 'attachment.php'; 682 683 return get_query_template( 'attachment', $templates ); 684 } 685 686 /** 687 * Set up the globals used for template loading. 688 * 689 * @since 6.5.0 690 * 691 * @global string $wp_stylesheet_path Path to current theme's stylesheet directory. 692 * @global string $wp_template_path Path to current theme's template directory. 693 */ 694 function wp_set_template_globals() { 695 global $wp_stylesheet_path, $wp_template_path; 696 697 $wp_stylesheet_path = get_stylesheet_directory(); 698 $wp_template_path = get_template_directory(); 699 } 700 701 /** 702 * Determines whether a template found by locate_template() may be loaded. 703 * 704 * @since 7.1.2 705 * @access private 706 * 707 * @global string $wp_stylesheet_path Path to current theme's stylesheet directory. 708 * @global string $wp_template_path Path to current theme's template directory. 709 * 710 * @param string $path Path to an existing template file. 711 * @return bool Whether the template may be loaded. 712 */ 713 function _wp_is_template_path_allowed( $path ) { 714 global $wp_stylesheet_path, $wp_template_path; 715 716 // A file path that exists and does not contain `..` is allowed. 717 if ( 0 === preg_match( '#(?:^|/)\.\.[. ]*(?:/|$)#', wp_normalize_path( $path ) ) ) { 718 return true; 719 } 720 721 // Resolve the true location of the requested file for later comparison. 722 $real_path = realpath( $path ); 723 724 if ( false === $real_path ) { 725 return false; 726 } 727 728 $real_path = trailingslashit( wp_normalize_path( $real_path ) ); 729 730 $directories = array( 731 $wp_stylesheet_path, 732 $wp_template_path, 733 ABSPATH . WPINC . '/theme-compat', 734 ); 735 736 // If a theme is in a subdirectory, accept templates from its direct parent directory. 737 if ( str_contains( get_stylesheet(), '/' ) ) { 738 $directories[] = dirname( $wp_stylesheet_path ); 739 } 740 741 // If a parent theme is in a subdirectory, accept templates from its direct parent directory. 742 if ( str_contains( get_template(), '/' ) ) { 743 $directories[] = dirname( $wp_template_path ); 744 } 745 746 foreach ( $directories as $directory ) { 747 $real_directory = realpath( $directory ); 748 749 if ( false === $real_directory ) { 750 continue; 751 } 752 753 // The true location of the requested file must be inside one of the allowed directories. 754 if ( str_starts_with( $real_path, trailingslashit( wp_normalize_path( $real_directory ) ) ) ) { 755 return true; 756 } 757 } 758 759 return false; 760 } 761 762 /** 763 * Retrieves the name of the highest priority template file that exists. 764 * 765 * Searches in the stylesheet directory before the template directory and 766 * wp-includes/theme-compat so that themes which inherit from a parent theme 767 * can just overload one file. 768 * 769 * @since 2.7.0 770 * @since 5.5.0 The `$args` parameter was added. 771 * @since 7.1.2 A template name containing `..` is only located if it resolves inside the theme. 772 * 773 * @global string $wp_stylesheet_path Path to current theme's stylesheet directory. 774 * @global string $wp_template_path Path to current theme's template directory. 775 * 776 * @param string|array $template_names Template file(s) to search for, in order. 777 * @param bool $load If true the template file will be loaded if it is found. 778 * @param bool $load_once Whether to require_once or require. Has no effect if `$load` is false. 779 * Default true. 780 * @param array $args Optional. Additional arguments passed to the template. 781 * Default empty array. 782 * @return string The template filename if one is located. 783 */ 784 function locate_template( $template_names, $load = false, $load_once = true, $args = array() ) { 785 global $wp_stylesheet_path, $wp_template_path; 786 787 if ( ! isset( $wp_stylesheet_path ) || ! isset( $wp_template_path ) ) { 788 wp_set_template_globals(); 789 } 790 791 $is_child_theme = is_child_theme(); 792 793 $located = ''; 794 foreach ( (array) $template_names as $template_name ) { 795 if ( ! $template_name ) { 796 continue; 797 } 798 if ( file_exists( $wp_stylesheet_path . '/' . $template_name ) ) { 799 $candidate = $wp_stylesheet_path . '/' . $template_name; 800 } elseif ( $is_child_theme && file_exists( $wp_template_path . '/' . $template_name ) ) { 801 $candidate = $wp_template_path . '/' . $template_name; 802 } elseif ( file_exists( ABSPATH . WPINC . '/theme-compat/' . $template_name ) ) { 803 $candidate = ABSPATH . WPINC . '/theme-compat/' . $template_name; 804 } else { 805 continue; 806 } 807 808 if ( _wp_is_template_path_allowed( $candidate ) ) { 809 $located = $candidate; 810 break; 811 } 812 } 813 814 if ( $load && '' !== $located ) { 815 load_template( $located, $load_once, $args ); 816 } 817 818 return $located; 819 } 820 821 /** 822 * Requires the template file with WordPress environment. 823 * 824 * The globals are set up for the template file to ensure that the WordPress 825 * environment is available from within the function. The query variables are 826 * also available. 827 * 828 * @since 1.5.0 829 * @since 5.5.0 The `$args` parameter was added. 830 * 831 * @global array $posts 832 * @global WP_Post $post Global post object. 833 * @global bool $wp_did_header 834 * @global WP_Query $wp_query WordPress Query object. 835 * @global WP_Rewrite $wp_rewrite WordPress rewrite component. 836 * @global wpdb $wpdb WordPress database abstraction object. 837 * @global string $wp_version 838 * @global WP $wp Current WordPress environment instance. 839 * @global int $id 840 * @global WP_Comment $comment Global comment object. 841 * @global int $user_ID 842 * 843 * @param string $_template_file Path to template file. 844 * @param bool $load_once Whether to require_once or require. Default true. 845 * @param array $args Optional. Additional arguments passed to the template. 846 * Default empty array. 847 */ 848 function load_template( $_template_file, $load_once = true, $args = array() ) { 849 global $posts, $post, $wp_did_header, $wp_query, $wp_rewrite, $wpdb, $wp_version, $wp, $id, $comment, $user_ID; 850 851 /** @var array{ s?: scalar, ... } $query_vars */ 852 $query_vars = $wp_query->query_vars; 853 if ( is_array( $query_vars ) ) { 854 /* 855 * This use of extract() cannot be removed. There are many possible ways that 856 * templates could depend on variables that it creates existing, and no way to 857 * detect and deprecate it. 858 * 859 * Passing the EXTR_SKIP flag is the safest option, ensuring globals and 860 * function variables cannot be overwritten. 861 */ 862 // phpcs:ignore WordPress.PHP.DontExtract.extract_extract 863 extract( $query_vars, EXTR_SKIP ); 864 } 865 866 if ( isset( $s ) ) { 867 $s = esc_attr( (string) $s ); 868 } 869 870 /** 871 * Fires before a template file is loaded. 872 * 873 * @since 6.1.0 874 * 875 * @param string $_template_file The full path to the template file. 876 * @param bool $load_once Whether to require_once or require. 877 * @param array $args Additional arguments passed to the template. 878 */ 879 do_action( 'wp_before_load_template', $_template_file, $load_once, $args ); 880 881 if ( $load_once ) { 882 require_once $_template_file; 883 } else { 884 require $_template_file; 885 } 886 887 /** 888 * Fires after a template file is loaded. 889 * 890 * @since 6.1.0 891 * 892 * @param string $_template_file The full path to the template file. 893 * @param bool $load_once Whether to require_once or require. 894 * @param array $args Additional arguments passed to the template. 895 */ 896 do_action( 'wp_after_load_template', $_template_file, $load_once, $args ); 897 } 898 899 /** 900 * Checks whether the template should be output buffered for enhancement. 901 * 902 * By default, an output buffer is only started if a {@see 'wp_template_enhancement_output_buffer'} filter has been 903 * added by the time a template is included at the {@see 'wp_before_include_template'} action. This allows template 904 * responses to be streamed as much as possible when no template enhancements are registered to apply. 905 * 906 * @since 6.9.0 907 * 908 * @return bool Whether the template should be output-buffered for enhancement. 909 */ 910 function wp_should_output_buffer_template_for_enhancement(): bool { 911 /** 912 * Filters whether the template should be output-buffered for enhancement. 913 * 914 * By default, an output buffer is only started if a {@see 'wp_template_enhancement_output_buffer'} filter has been 915 * added or if a plugin has added a {@see 'wp_finalized_template_enhancement_output_buffer'} action. For this 916 * default to apply, either of the hooks must be added by the time the template is included at the 917 * {@see 'wp_before_include_template'} action. This allows template responses to be streamed unless the there is 918 * code which depends on an output buffer being opened. This filter allows a site to opt in to adding such template 919 * enhancement filters later during the rendering of the template. 920 * 921 * @since 6.9.0 922 * 923 * @param bool $use_output_buffer Whether an output buffer is started. 924 */ 925 return (bool) apply_filters( 'wp_should_output_buffer_template_for_enhancement', has_filter( 'wp_template_enhancement_output_buffer' ) || has_action( 'wp_finalized_template_enhancement_output_buffer' ) ); 926 } 927 928 /** 929 * Starts the template enhancement output buffer. 930 * 931 * This function is called immediately before the template is included. 932 * 933 * @since 6.9.0 934 * 935 * @return bool Whether the output buffer successfully started. 936 */ 937 function wp_start_template_enhancement_output_buffer(): bool { 938 if ( ! wp_should_output_buffer_template_for_enhancement() ) { 939 return false; 940 } 941 942 $started = ob_start( 943 'wp_finalize_template_enhancement_output_buffer', 944 0, // Unlimited buffer size so that entire output is passed to the filter. 945 /* 946 * Instead of the default PHP_OUTPUT_HANDLER_STDFLAGS (cleanable, flushable, and removable) being used for 947 * flags, the PHP_OUTPUT_HANDLER_FLUSHABLE flag must be omitted. If the buffer were flushable, then each time 948 * that ob_flush() is called, a fragment of the output would be sent into the output buffer callback. This 949 * output buffer is intended to capture the entire response for processing, as indicated by the chunk size of 0. 950 * So the buffer does not allow flushing to ensure the entire buffer can be processed, such as for optimizing an 951 * entire HTML document, where markup in the HEAD may need to be adjusted based on markup that appears late in 952 * the BODY. 953 * 954 * If this ends up being problematic, then PHP_OUTPUT_HANDLER_FLUSHABLE could be added to the $flags and the 955 * output buffer callback could check if the phase is PHP_OUTPUT_HANDLER_FLUSH and abort any subsequent 956 * processing while also emitting a _doing_it_wrong(). 957 * 958 * The output buffer needs to be removable because WordPress calls wp_ob_end_flush_all() and then calls 959 * wp_cache_close(). If the buffers are not all flushed before wp_cache_close() is closed, then some output buffer 960 * handlers (e.g. for caching plugins) may fail to be able to store the page output in the object cache. 961 * See <https://github.com/WordPress/performance/pull/1317#issuecomment-2271955356>. 962 */ 963 PHP_OUTPUT_HANDLER_STDFLAGS ^ PHP_OUTPUT_HANDLER_FLUSHABLE 964 ); 965 966 if ( $started ) { 967 /** 968 * Fires when the template enhancement output buffer has started. 969 * 970 * @since 6.9.0 971 */ 972 do_action( 'wp_template_enhancement_output_buffer_started' ); 973 } 974 975 return $started; 976 } 977 978 /** 979 * Finalizes the template enhancement output buffer. 980 * 981 * Checks to see if the output buffer is complete and contains HTML. If so, runs the content through 982 * the `wp_template_enhancement_output_buffer` filter. If not, the original content is returned. 983 * 984 * @since 6.9.0 985 * 986 * @see wp_start_template_enhancement_output_buffer() 987 * 988 * @param string $output Output buffer. 989 * @param int $phase Phase. 990 * @return string Finalized output buffer. 991 */ 992 function wp_finalize_template_enhancement_output_buffer( string $output, int $phase ): string { 993 // When the output is being cleaned (e.g. pending template is replaced with error page), do not send it through the filter. 994 if ( ( $phase & PHP_OUTPUT_HANDLER_CLEAN ) !== 0 ) { 995 return $output; 996 } 997 998 // Detect if the response is an HTML content type. 999 $is_html_content_type = null; 1000 $html_content_types = array( 'text/html', 'application/xhtml+xml' ); 1001 foreach ( headers_list() as $header ) { 1002 $header_parts = explode( ':', strtolower( $header ), 2 ); 1003 if ( 1004 count( $header_parts ) === 2 && 1005 'content-type' === $header_parts[0] 1006 ) { 1007 /* 1008 * This is looking for very specific content types, therefore it 1009 * doesn’t need to fully parse the header’s value. Instead, it needs 1010 * only assert that the content type is one of the static HTML types. 1011 * 1012 * Example: 1013 * 1014 * Content-Type: text/html; charset=utf8 1015 * Content-Type: text/html ;charset=latin4 1016 * Content-Type:application/xhtml+xml 1017 */ 1018 $media_type = trim( strtok( $header_parts[1], ';' ), " \t" ); 1019 $is_html_content_type = in_array( $media_type, $html_content_types, true ); 1020 break; // PHP only sends the first Content-Type header in the list. 1021 } 1022 } 1023 if ( null === $is_html_content_type ) { 1024 $is_html_content_type = in_array( ini_get( 'default_mimetype' ), $html_content_types, true ); 1025 } 1026 1027 // If the content type is not HTML, short-circuit since it is not relevant for enhancement. 1028 if ( ! $is_html_content_type ) { 1029 /** This action is documented in wp-includes/template.php */ 1030 do_action( 'wp_finalized_template_enhancement_output_buffer', $output ); 1031 return $output; 1032 } 1033 1034 $filtered_output = $output; 1035 1036 $did_just_catch = false; 1037 1038 $error_log = array(); 1039 set_error_handler( 1040 static function ( int $level, string $message, ?string $file = null, ?int $line = null ) use ( &$error_log, &$did_just_catch ) { 1041 // Switch a user error to an exception so that it can be caught and the buffer can be returned. 1042 if ( E_USER_ERROR === $level ) { 1043 throw new Exception( __( 'User error triggered:' ) . ' ' . $message ); 1044 } 1045 1046 // Display a caught exception as an error since it prevents any of the output buffer filters from applying. 1047 if ( $did_just_catch ) { 1048 $level = E_USER_ERROR; 1049 } 1050 1051 // Capture a reported error to be displayed by appending to the processed output buffer if display_errors is enabled. 1052 if ( error_reporting() & $level ) { 1053 $error_log[] = compact( 'level', 'message', 'file', 'line' ); 1054 } 1055 return false; 1056 } 1057 ); 1058 $original_display_errors = ini_get( 'display_errors' ); 1059 if ( $original_display_errors ) { 1060 ini_set( 'display_errors', 0 ); 1061 } 1062 1063 try { 1064 /** 1065 * Filters the template enhancement output buffer prior to sending to the client. 1066 * 1067 * This filter only applies the HTML output of an included template. This filter is a progressive enhancement 1068 * intended for applications such as optimizing markup to improve frontend page load performance. Sites must not 1069 * depend on this filter applying since they may opt to stream the responses instead. Callbacks for this filter 1070 * are highly discouraged from using regular expressions to do any kind of replacement on the output. Use the 1071 * HTML API (either `WP_HTML_Tag_Processor` or `WP_HTML_Processor`), or else use {@see DOM\HtmlDocument} as of 1072 * PHP 8.4 which fully supports HTML5. 1073 * 1074 * Do not print any output during this filter. While filters normally don't print anything, this is especially 1075 * important since this applies during an output buffer callback. Prior to PHP 8.5, the output will be silently 1076 * omitted, whereas afterward a deprecation notice will be emitted. 1077 * 1078 * Important: Because this filter is applied inside an output buffer callback (i.e. display handler), any 1079 * callbacks added to the filter must not attempt to start their own output buffers. Otherwise, PHP will raise a 1080 * fatal error: "Cannot use output buffering in output buffering display handlers." 1081 * 1082 * @since 6.9.0 1083 * 1084 * @param string $filtered_output HTML template enhancement output buffer. 1085 * @param string $output Original HTML template output buffer. 1086 */ 1087 $filtered_output = (string) apply_filters( 'wp_template_enhancement_output_buffer', $filtered_output, $output ); 1088 } catch ( Throwable $throwable ) { 1089 // Emit to the error log as a warning not as an error to prevent halting execution. 1090 $did_just_catch = true; 1091 trigger_error( 1092 sprintf( 1093 /* translators: %s is the throwable class name */ 1094 __( 'Uncaught "%s" thrown:' ), 1095 get_class( $throwable ) 1096 ) . ' ' . $throwable->getMessage(), 1097 E_USER_WARNING 1098 ); 1099 $did_just_catch = false; 1100 } 1101 1102 try { 1103 /** 1104 * Fires after the template enhancement output buffer has been finalized. 1105 * 1106 * This happens immediately before the template enhancement output buffer is flushed. No output may be printed 1107 * at this action; prior to PHP 8.5, the output will be silently omitted, whereas afterward a deprecation notice 1108 * will be emitted. Nevertheless, HTTP headers may be sent, which makes this action complimentary to the 1109 * {@see 'send_headers'} action, in which headers may be sent before the template has started rendering. In 1110 * contrast, this `wp_finalized_template_enhancement_output_buffer` action is the possible point at which HTTP 1111 * headers can be sent. This action does not fire if the "template enhancement output buffer" was not started. 1112 * This output buffer is automatically started if this action is added before 1113 * {@see wp_start_template_enhancement_output_buffer()} runs at the {@see 'wp_before_include_template'} action 1114 * with priority 1000. Before this point, the output buffer will also be started automatically if there was a 1115 * {@see 'wp_template_enhancement_output_buffer'} filter added, or if the 1116 * {@see 'wp_should_output_buffer_template_for_enhancement'} filter is made to return `true`. 1117 * 1118 * Important: Because this action fires inside an output buffer callback (i.e. display handler), any callbacks 1119 * added to the action must not attempt to start their own output buffers. Otherwise, PHP will raise a fatal 1120 * error: "Cannot use output buffering in output buffering display handlers." 1121 * 1122 * @since 6.9.0 1123 * 1124 * @param string $output Finalized output buffer. 1125 */ 1126 do_action( 'wp_finalized_template_enhancement_output_buffer', $filtered_output ); 1127 } catch ( Throwable $throwable ) { 1128 // Emit to the error log as a warning not as an error to prevent halting execution. 1129 $did_just_catch = true; 1130 trigger_error( 1131 sprintf( 1132 /* translators: %s is the class name */ 1133 __( 'Uncaught "%s" thrown:' ), 1134 get_class( $throwable ) 1135 ) . ' ' . $throwable->getMessage(), 1136 E_USER_WARNING 1137 ); 1138 $did_just_catch = false; 1139 } 1140 1141 // Append any errors to be displayed before returning flushing the buffer. 1142 if ( $original_display_errors && 'stderr' !== $original_display_errors ) { 1143 foreach ( $error_log as $error ) { 1144 switch ( $error['level'] ) { 1145 case E_USER_NOTICE: 1146 $type = 'Notice'; 1147 break; 1148 case E_USER_DEPRECATED: 1149 $type = 'Deprecated'; 1150 break; 1151 case E_USER_WARNING: 1152 $type = 'Warning'; 1153 break; 1154 default: 1155 $type = 'Error'; 1156 } 1157 1158 if ( ini_get( 'html_errors' ) ) { 1159 /* 1160 * Adapted from PHP internals: <https://github.com/php/php-src/blob/a979e9f897a90a580e883b1f39ce5673686ffc67/main/main.c#L1478>. 1161 * The self-closing tags are a vestige of the XHTML past! 1162 */ 1163 $format = "%s<br />\n<b>%s</b>: %s in <b>%s</b> on line <b>%s</b><br />\n%s"; 1164 } else { 1165 // Adapted from PHP internals: <https://github.com/php/php-src/blob/a979e9f897a90a580e883b1f39ce5673686ffc67/main/main.c#L1492>. 1166 $format = "%s\n%s: %s in %s on line %s\n%s"; 1167 } 1168 $filtered_output .= sprintf( 1169 $format, 1170 ini_get( 'error_prepend_string' ), 1171 $type, 1172 $error['message'], 1173 $error['file'], 1174 $error['line'], 1175 ini_get( 'error_append_string' ) 1176 ); 1177 } 1178 1179 ini_set( 'display_errors', $original_display_errors ); 1180 } 1181 1182 restore_error_handler(); 1183 1184 return $filtered_output; 1185 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Fri Oct 9 08:20:33 2026 | Cross-referenced by PHPXref |