| [ 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 ) { 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 * Retrieves the name of the highest priority template file that exists. 703 * 704 * Searches in the stylesheet directory before the template directory and 705 * wp-includes/theme-compat so that themes which inherit from a parent theme 706 * can just overload one file. 707 * 708 * @since 2.7.0 709 * @since 5.5.0 The `$args` parameter was added. 710 * 711 * @global string $wp_stylesheet_path Path to current theme's stylesheet directory. 712 * @global string $wp_template_path Path to current theme's template directory. 713 * 714 * @param string|array $template_names Template file(s) to search for, in order. 715 * @param bool $load If true the template file will be loaded if it is found. 716 * @param bool $load_once Whether to require_once or require. Has no effect if `$load` is false. 717 * Default true. 718 * @param array $args Optional. Additional arguments passed to the template. 719 * Default empty array. 720 * @return string The template filename if one is located. 721 */ 722 function locate_template( $template_names, $load = false, $load_once = true, $args = array() ) { 723 global $wp_stylesheet_path, $wp_template_path; 724 725 if ( ! isset( $wp_stylesheet_path ) || ! isset( $wp_template_path ) ) { 726 wp_set_template_globals(); 727 } 728 729 $is_child_theme = is_child_theme(); 730 731 $located = ''; 732 foreach ( (array) $template_names as $template_name ) { 733 if ( ! $template_name ) { 734 continue; 735 } 736 if ( file_exists( $wp_stylesheet_path . '/' . $template_name ) ) { 737 $located = $wp_stylesheet_path . '/' . $template_name; 738 break; 739 } elseif ( $is_child_theme && file_exists( $wp_template_path . '/' . $template_name ) ) { 740 $located = $wp_template_path . '/' . $template_name; 741 break; 742 } elseif ( file_exists( ABSPATH . WPINC . '/theme-compat/' . $template_name ) ) { 743 $located = ABSPATH . WPINC . '/theme-compat/' . $template_name; 744 break; 745 } 746 } 747 748 if ( $load && '' !== $located ) { 749 load_template( $located, $load_once, $args ); 750 } 751 752 return $located; 753 } 754 755 /** 756 * Requires the template file with WordPress environment. 757 * 758 * The globals are set up for the template file to ensure that the WordPress 759 * environment is available from within the function. The query variables are 760 * also available. 761 * 762 * @since 1.5.0 763 * @since 5.5.0 The `$args` parameter was added. 764 * 765 * @global array $posts 766 * @global WP_Post $post Global post object. 767 * @global bool $wp_did_header 768 * @global WP_Query $wp_query WordPress Query object. 769 * @global WP_Rewrite $wp_rewrite WordPress rewrite component. 770 * @global wpdb $wpdb WordPress database abstraction object. 771 * @global string $wp_version 772 * @global WP $wp Current WordPress environment instance. 773 * @global int $id 774 * @global WP_Comment $comment Global comment object. 775 * @global int $user_ID 776 * 777 * @param string $_template_file Path to template file. 778 * @param bool $load_once Whether to require_once or require. Default true. 779 * @param array $args Optional. Additional arguments passed to the template. 780 * Default empty array. 781 */ 782 function load_template( $_template_file, $load_once = true, $args = array() ) { 783 global $posts, $post, $wp_did_header, $wp_query, $wp_rewrite, $wpdb, $wp_version, $wp, $id, $comment, $user_ID; 784 785 /** @var array{ s?: scalar, ... } $query_vars */ 786 $query_vars = $wp_query->query_vars; 787 if ( is_array( $query_vars ) ) { 788 /* 789 * This use of extract() cannot be removed. There are many possible ways that 790 * templates could depend on variables that it creates existing, and no way to 791 * detect and deprecate it. 792 * 793 * Passing the EXTR_SKIP flag is the safest option, ensuring globals and 794 * function variables cannot be overwritten. 795 */ 796 // phpcs:ignore WordPress.PHP.DontExtract.extract_extract 797 extract( $query_vars, EXTR_SKIP ); 798 } 799 800 if ( isset( $s ) ) { 801 $s = esc_attr( (string) $s ); 802 } 803 804 /** 805 * Fires before a template file is loaded. 806 * 807 * @since 6.1.0 808 * 809 * @param string $_template_file The full path to the template file. 810 * @param bool $load_once Whether to require_once or require. 811 * @param array $args Additional arguments passed to the template. 812 */ 813 do_action( 'wp_before_load_template', $_template_file, $load_once, $args ); 814 815 if ( $load_once ) { 816 require_once $_template_file; 817 } else { 818 require $_template_file; 819 } 820 821 /** 822 * Fires after a template file is loaded. 823 * 824 * @since 6.1.0 825 * 826 * @param string $_template_file The full path to the template file. 827 * @param bool $load_once Whether to require_once or require. 828 * @param array $args Additional arguments passed to the template. 829 */ 830 do_action( 'wp_after_load_template', $_template_file, $load_once, $args ); 831 } 832 833 /** 834 * Checks whether the template should be output buffered for enhancement. 835 * 836 * By default, an output buffer is only started if a {@see 'wp_template_enhancement_output_buffer'} filter has been 837 * added by the time a template is included at the {@see 'wp_before_include_template'} action. This allows template 838 * responses to be streamed as much as possible when no template enhancements are registered to apply. 839 * 840 * @since 6.9.0 841 * 842 * @return bool Whether the template should be output-buffered for enhancement. 843 */ 844 function wp_should_output_buffer_template_for_enhancement(): bool { 845 /** 846 * Filters whether the template should be output-buffered for enhancement. 847 * 848 * By default, an output buffer is only started if a {@see 'wp_template_enhancement_output_buffer'} filter has been 849 * added or if a plugin has added a {@see 'wp_finalized_template_enhancement_output_buffer'} action. For this 850 * default to apply, either of the hooks must be added by the time the template is included at the 851 * {@see 'wp_before_include_template'} action. This allows template responses to be streamed unless the there is 852 * code which depends on an output buffer being opened. This filter allows a site to opt in to adding such template 853 * enhancement filters later during the rendering of the template. 854 * 855 * @since 6.9.0 856 * 857 * @param bool $use_output_buffer Whether an output buffer is started. 858 */ 859 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' ) ); 860 } 861 862 /** 863 * Starts the template enhancement output buffer. 864 * 865 * This function is called immediately before the template is included. 866 * 867 * @since 6.9.0 868 * 869 * @return bool Whether the output buffer successfully started. 870 */ 871 function wp_start_template_enhancement_output_buffer(): bool { 872 if ( ! wp_should_output_buffer_template_for_enhancement() ) { 873 return false; 874 } 875 876 $started = ob_start( 877 'wp_finalize_template_enhancement_output_buffer', 878 0, // Unlimited buffer size so that entire output is passed to the filter. 879 /* 880 * Instead of the default PHP_OUTPUT_HANDLER_STDFLAGS (cleanable, flushable, and removable) being used for 881 * flags, the PHP_OUTPUT_HANDLER_FLUSHABLE flag must be omitted. If the buffer were flushable, then each time 882 * that ob_flush() is called, a fragment of the output would be sent into the output buffer callback. This 883 * output buffer is intended to capture the entire response for processing, as indicated by the chunk size of 0. 884 * So the buffer does not allow flushing to ensure the entire buffer can be processed, such as for optimizing an 885 * entire HTML document, where markup in the HEAD may need to be adjusted based on markup that appears late in 886 * the BODY. 887 * 888 * If this ends up being problematic, then PHP_OUTPUT_HANDLER_FLUSHABLE could be added to the $flags and the 889 * output buffer callback could check if the phase is PHP_OUTPUT_HANDLER_FLUSH and abort any subsequent 890 * processing while also emitting a _doing_it_wrong(). 891 * 892 * The output buffer needs to be removable because WordPress calls wp_ob_end_flush_all() and then calls 893 * wp_cache_close(). If the buffers are not all flushed before wp_cache_close() is closed, then some output buffer 894 * handlers (e.g. for caching plugins) may fail to be able to store the page output in the object cache. 895 * See <https://github.com/WordPress/performance/pull/1317#issuecomment-2271955356>. 896 */ 897 PHP_OUTPUT_HANDLER_STDFLAGS ^ PHP_OUTPUT_HANDLER_FLUSHABLE 898 ); 899 900 if ( $started ) { 901 /** 902 * Fires when the template enhancement output buffer has started. 903 * 904 * @since 6.9.0 905 */ 906 do_action( 'wp_template_enhancement_output_buffer_started' ); 907 } 908 909 return $started; 910 } 911 912 /** 913 * Finalizes the template enhancement output buffer. 914 * 915 * Checks to see if the output buffer is complete and contains HTML. If so, runs the content through 916 * the `wp_template_enhancement_output_buffer` filter. If not, the original content is returned. 917 * 918 * @since 6.9.0 919 * 920 * @see wp_start_template_enhancement_output_buffer() 921 * 922 * @param string $output Output buffer. 923 * @param int $phase Phase. 924 * @return string Finalized output buffer. 925 */ 926 function wp_finalize_template_enhancement_output_buffer( string $output, int $phase ): string { 927 // When the output is being cleaned (e.g. pending template is replaced with error page), do not send it through the filter. 928 if ( ( $phase & PHP_OUTPUT_HANDLER_CLEAN ) !== 0 ) { 929 return $output; 930 } 931 932 // Detect if the response is an HTML content type. 933 $is_html_content_type = null; 934 $html_content_types = array( 'text/html', 'application/xhtml+xml' ); 935 foreach ( headers_list() as $header ) { 936 $header_parts = explode( ':', strtolower( $header ), 2 ); 937 if ( 938 count( $header_parts ) === 2 && 939 'content-type' === $header_parts[0] 940 ) { 941 /* 942 * This is looking for very specific content types, therefore it 943 * doesn’t need to fully parse the header’s value. Instead, it needs 944 * only assert that the content type is one of the static HTML types. 945 * 946 * Example: 947 * 948 * Content-Type: text/html; charset=utf8 949 * Content-Type: text/html ;charset=latin4 950 * Content-Type:application/xhtml+xml 951 */ 952 $media_type = trim( strtok( $header_parts[1], ';' ), " \t" ); 953 $is_html_content_type = in_array( $media_type, $html_content_types, true ); 954 break; // PHP only sends the first Content-Type header in the list. 955 } 956 } 957 if ( null === $is_html_content_type ) { 958 $is_html_content_type = in_array( ini_get( 'default_mimetype' ), $html_content_types, true ); 959 } 960 961 // If the content type is not HTML, short-circuit since it is not relevant for enhancement. 962 if ( ! $is_html_content_type ) { 963 /** This action is documented in wp-includes/template.php */ 964 do_action( 'wp_finalized_template_enhancement_output_buffer', $output ); 965 return $output; 966 } 967 968 $filtered_output = $output; 969 970 $did_just_catch = false; 971 972 $error_log = array(); 973 set_error_handler( 974 static function ( int $level, string $message, ?string $file = null, ?int $line = null ) use ( &$error_log, &$did_just_catch ) { 975 // Switch a user error to an exception so that it can be caught and the buffer can be returned. 976 if ( E_USER_ERROR === $level ) { 977 throw new Exception( __( 'User error triggered:' ) . ' ' . $message ); 978 } 979 980 // Display a caught exception as an error since it prevents any of the output buffer filters from applying. 981 if ( $did_just_catch ) { 982 $level = E_USER_ERROR; 983 } 984 985 // Capture a reported error to be displayed by appending to the processed output buffer if display_errors is enabled. 986 if ( error_reporting() & $level ) { 987 $error_log[] = compact( 'level', 'message', 'file', 'line' ); 988 } 989 return false; 990 } 991 ); 992 $original_display_errors = ini_get( 'display_errors' ); 993 if ( $original_display_errors ) { 994 ini_set( 'display_errors', 0 ); 995 } 996 997 try { 998 /** 999 * Filters the template enhancement output buffer prior to sending to the client. 1000 * 1001 * This filter only applies the HTML output of an included template. This filter is a progressive enhancement 1002 * intended for applications such as optimizing markup to improve frontend page load performance. Sites must not 1003 * depend on this filter applying since they may opt to stream the responses instead. Callbacks for this filter 1004 * are highly discouraged from using regular expressions to do any kind of replacement on the output. Use the 1005 * HTML API (either `WP_HTML_Tag_Processor` or `WP_HTML_Processor`), or else use {@see DOM\HtmlDocument} as of 1006 * PHP 8.4 which fully supports HTML5. 1007 * 1008 * Do not print any output during this filter. While filters normally don't print anything, this is especially 1009 * important since this applies during an output buffer callback. Prior to PHP 8.5, the output will be silently 1010 * omitted, whereas afterward a deprecation notice will be emitted. 1011 * 1012 * Important: Because this filter is applied inside an output buffer callback (i.e. display handler), any 1013 * callbacks added to the filter must not attempt to start their own output buffers. Otherwise, PHP will raise a 1014 * fatal error: "Cannot use output buffering in output buffering display handlers." 1015 * 1016 * @since 6.9.0 1017 * 1018 * @param string $filtered_output HTML template enhancement output buffer. 1019 * @param string $output Original HTML template output buffer. 1020 */ 1021 $filtered_output = (string) apply_filters( 'wp_template_enhancement_output_buffer', $filtered_output, $output ); 1022 } catch ( Throwable $throwable ) { 1023 // Emit to the error log as a warning not as an error to prevent halting execution. 1024 $did_just_catch = true; 1025 trigger_error( 1026 sprintf( 1027 /* translators: %s is the throwable class name */ 1028 __( 'Uncaught "%s" thrown:' ), 1029 get_class( $throwable ) 1030 ) . ' ' . $throwable->getMessage(), 1031 E_USER_WARNING 1032 ); 1033 $did_just_catch = false; 1034 } 1035 1036 try { 1037 /** 1038 * Fires after the template enhancement output buffer has been finalized. 1039 * 1040 * This happens immediately before the template enhancement output buffer is flushed. No output may be printed 1041 * at this action; prior to PHP 8.5, the output will be silently omitted, whereas afterward a deprecation notice 1042 * will be emitted. Nevertheless, HTTP headers may be sent, which makes this action complimentary to the 1043 * {@see 'send_headers'} action, in which headers may be sent before the template has started rendering. In 1044 * contrast, this `wp_finalized_template_enhancement_output_buffer` action is the possible point at which HTTP 1045 * headers can be sent. This action does not fire if the "template enhancement output buffer" was not started. 1046 * This output buffer is automatically started if this action is added before 1047 * {@see wp_start_template_enhancement_output_buffer()} runs at the {@see 'wp_before_include_template'} action 1048 * with priority 1000. Before this point, the output buffer will also be started automatically if there was a 1049 * {@see 'wp_template_enhancement_output_buffer'} filter added, or if the 1050 * {@see 'wp_should_output_buffer_template_for_enhancement'} filter is made to return `true`. 1051 * 1052 * Important: Because this action fires inside an output buffer callback (i.e. display handler), any callbacks 1053 * added to the action must not attempt to start their own output buffers. Otherwise, PHP will raise a fatal 1054 * error: "Cannot use output buffering in output buffering display handlers." 1055 * 1056 * @since 6.9.0 1057 * 1058 * @param string $output Finalized output buffer. 1059 */ 1060 do_action( 'wp_finalized_template_enhancement_output_buffer', $filtered_output ); 1061 } catch ( Throwable $throwable ) { 1062 // Emit to the error log as a warning not as an error to prevent halting execution. 1063 $did_just_catch = true; 1064 trigger_error( 1065 sprintf( 1066 /* translators: %s is the class name */ 1067 __( 'Uncaught "%s" thrown:' ), 1068 get_class( $throwable ) 1069 ) . ' ' . $throwable->getMessage(), 1070 E_USER_WARNING 1071 ); 1072 $did_just_catch = false; 1073 } 1074 1075 // Append any errors to be displayed before returning flushing the buffer. 1076 if ( $original_display_errors && 'stderr' !== $original_display_errors ) { 1077 foreach ( $error_log as $error ) { 1078 switch ( $error['level'] ) { 1079 case E_USER_NOTICE: 1080 $type = 'Notice'; 1081 break; 1082 case E_USER_DEPRECATED: 1083 $type = 'Deprecated'; 1084 break; 1085 case E_USER_WARNING: 1086 $type = 'Warning'; 1087 break; 1088 default: 1089 $type = 'Error'; 1090 } 1091 1092 if ( ini_get( 'html_errors' ) ) { 1093 /* 1094 * Adapted from PHP internals: <https://github.com/php/php-src/blob/a979e9f897a90a580e883b1f39ce5673686ffc67/main/main.c#L1478>. 1095 * The self-closing tags are a vestige of the XHTML past! 1096 */ 1097 $format = "%s<br />\n<b>%s</b>: %s in <b>%s</b> on line <b>%s</b><br />\n%s"; 1098 } else { 1099 // Adapted from PHP internals: <https://github.com/php/php-src/blob/a979e9f897a90a580e883b1f39ce5673686ffc67/main/main.c#L1492>. 1100 $format = "%s\n%s: %s in %s on line %s\n%s"; 1101 } 1102 $filtered_output .= sprintf( 1103 $format, 1104 ini_get( 'error_prepend_string' ), 1105 $type, 1106 $error['message'], 1107 $error['file'], 1108 $error['line'], 1109 ini_get( 'error_append_string' ) 1110 ); 1111 } 1112 1113 ini_set( 'display_errors', $original_display_errors ); 1114 } 1115 1116 restore_error_handler(); 1117 1118 return $filtered_output; 1119 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Sun Aug 30 08:20:25 2026 | Cross-referenced by PHPXref |