[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/ -> template.php (source)

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


Generated : Fri Nov 14 08:20:08 2025 Cross-referenced by PHPXref