[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

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

   1  <?php
   2  /**
   3   * WordPress Query API
   4   *
   5   * The query API attempts to get which part of WordPress the user is on. It
   6   * also provides functionality for getting URL query information.
   7   *
   8   * @link https://developer.wordpress.org/themes/basics/the-loop/ More information on The Loop.
   9   *
  10   * @package WordPress
  11   * @subpackage Query
  12   */
  13  
  14  /**
  15   * Retrieves the value of a query variable in the WP_Query class.
  16   *
  17   * @since 1.5.0
  18   * @since 3.9.0 The `$default_value` argument was introduced.
  19   *
  20   * @global WP_Query $wp_query WordPress Query object.
  21   *
  22   * @param string $query_var     The variable key to retrieve.
  23   * @param mixed  $default_value Optional. Value to return if the query variable is not set.
  24   *                              Default empty string.
  25   * @return mixed Contents of the query variable.
  26   */
  27  function get_query_var( $query_var, $default_value = '' ) {
  28      global $wp_query;
  29      return $wp_query->get( $query_var, $default_value );
  30  }
  31  
  32  /**
  33   * Retrieves the currently queried object.
  34   *
  35   * Wrapper for WP_Query::get_queried_object().
  36   *
  37   * @since 3.1.0
  38   *
  39   * @global WP_Query $wp_query WordPress Query object.
  40   *
  41   * @return WP_Term|WP_Post_Type|WP_Post|WP_User|null The queried object.
  42   */
  43  function get_queried_object() {
  44      global $wp_query;
  45      return $wp_query->get_queried_object();
  46  }
  47  
  48  /**
  49   * Retrieves the ID of the currently queried object.
  50   *
  51   * Wrapper for WP_Query::get_queried_object_id().
  52   *
  53   * @since 3.1.0
  54   *
  55   * @global WP_Query $wp_query WordPress Query object.
  56   *
  57   * @return int ID of the queried object.
  58   */
  59  function get_queried_object_id() {
  60      global $wp_query;
  61      return $wp_query->get_queried_object_id();
  62  }
  63  
  64  /**
  65   * Sets the value of a query variable in the WP_Query class.
  66   *
  67   * @since 2.2.0
  68   *
  69   * @global WP_Query $wp_query WordPress Query object.
  70   *
  71   * @param string $query_var Query variable key.
  72   * @param mixed  $value     Query variable value.
  73   */
  74  function set_query_var( $query_var, $value ) {
  75      global $wp_query;
  76      $wp_query->set( $query_var, $value );
  77  }
  78  
  79  /**
  80   * Sets up The Loop with query parameters.
  81   *
  82   * Note: This function will completely override the main query and isn't intended for use
  83   * by plugins or themes. Its overly-simplistic approach to modifying the main query can be
  84   * problematic and should be avoided wherever possible. In most cases, there are better,
  85   * more performant options for modifying the main query such as via the {@see 'pre_get_posts'}
  86   * action within WP_Query.
  87   *
  88   * This must not be used within the WordPress Loop.
  89   *
  90   * @since 1.5.0
  91   *
  92   * @global WP_Query $wp_query WordPress Query object.
  93   *
  94   * @param array|string $query Array or string of WP_Query arguments.
  95   * @return WP_Post[]|int[] Array of post objects or post IDs.
  96   */
  97  function query_posts( $query ) {
  98      $GLOBALS['wp_query'] = new WP_Query();
  99      return $GLOBALS['wp_query']->query( $query );
 100  }
 101  
 102  /**
 103   * Destroys the previous query and sets up a new query.
 104   *
 105   * This should be used after query_posts() and before another query_posts().
 106   * This will remove obscure bugs that occur when the previous WP_Query object
 107   * is not destroyed properly before another is set up.
 108   *
 109   * @since 2.3.0
 110   *
 111   * @global WP_Query $wp_query     WordPress Query object.
 112   * @global WP_Query $wp_the_query Copy of the global WP_Query instance created during wp_reset_query().
 113   */
 114  function wp_reset_query() {
 115      $GLOBALS['wp_query'] = $GLOBALS['wp_the_query'];
 116      wp_reset_postdata();
 117  }
 118  
 119  /**
 120   * After looping through a separate query, this function restores
 121   * the $post global to the current post in the main query.
 122   *
 123   * @since 3.0.0
 124   *
 125   * @global WP_Query $wp_query WordPress Query object.
 126   */
 127  function wp_reset_postdata() {
 128      global $wp_query;
 129  
 130      if ( isset( $wp_query ) ) {
 131          $wp_query->reset_postdata();
 132      }
 133  }
 134  
 135  /*
 136   * Query type checks.
 137   */
 138  
 139  /**
 140   * Determines whether the query is for an existing archive page.
 141   *
 142   * Archive pages include category, tag, author, date, custom post type,
 143   * and custom taxonomy based archives.
 144   *
 145   * For more information on this and similar theme functions, check out
 146   * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/
 147   * Conditional Tags} article in the Theme Developer Handbook.
 148   *
 149   * @since 1.5.0
 150   *
 151   * @see is_category()
 152   * @see is_tag()
 153   * @see is_author()
 154   * @see is_date()
 155   * @see is_post_type_archive()
 156   * @see is_tax()
 157   * @global WP_Query $wp_query WordPress Query object.
 158   *
 159   * @return bool Whether the query is for an existing archive page.
 160   */
 161  function is_archive() {
 162      global $wp_query;
 163  
 164      if ( ! isset( $wp_query ) ) {
 165          _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
 166          return false;
 167      }
 168  
 169      return $wp_query->is_archive();
 170  }
 171  
 172  /**
 173   * Determines whether the query is for an existing post type archive page.
 174   *
 175   * For more information on this and similar theme functions, check out
 176   * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/
 177   * Conditional Tags} article in the Theme Developer Handbook.
 178   *
 179   * @since 3.1.0
 180   *
 181   * @global WP_Query $wp_query WordPress Query object.
 182   *
 183   * @param string|string[] $post_types Optional. Post type or array of posts types
 184   *                                    to check against. Default empty.
 185   * @return bool Whether the query is for an existing post type archive page.
 186   */
 187  function is_post_type_archive( $post_types = '' ) {
 188      global $wp_query;
 189  
 190      if ( ! isset( $wp_query ) ) {
 191          _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
 192          return false;
 193      }
 194  
 195      return $wp_query->is_post_type_archive( $post_types );
 196  }
 197  
 198  /**
 199   * Determines whether the query is for an existing attachment page.
 200   *
 201   * For more information on this and similar theme functions, check out
 202   * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/
 203   * Conditional Tags} article in the Theme Developer Handbook.
 204   *
 205   * @since 2.0.0
 206   *
 207   * @global WP_Query $wp_query WordPress Query object.
 208   *
 209   * @param int|string|int[]|string[] $attachment Optional. Attachment ID, title, slug, or array of such
 210   *                                              to check against. Default empty.
 211   * @return bool Whether the query is for an existing attachment page.
 212   */
 213  function is_attachment( $attachment = '' ) {
 214      global $wp_query;
 215  
 216      if ( ! isset( $wp_query ) ) {
 217          _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
 218          return false;
 219      }
 220  
 221      return $wp_query->is_attachment( $attachment );
 222  }
 223  
 224  /**
 225   * Determines whether the query is for an existing author archive page.
 226   *
 227   * If the $author parameter is specified, this function will additionally
 228   * check if the query is for one of the authors specified.
 229   *
 230   * For more information on this and similar theme functions, check out
 231   * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/
 232   * Conditional Tags} article in the Theme Developer Handbook.
 233   *
 234   * @since 1.5.0
 235   *
 236   * @global WP_Query $wp_query WordPress Query object.
 237   *
 238   * @param int|string|int[]|string[] $author Optional. User ID, nickname, nicename, or array of such
 239   *                                          to check against. Default empty.
 240   * @return bool Whether the query is for an existing author archive page.
 241   */
 242  function is_author( $author = '' ) {
 243      global $wp_query;
 244  
 245      if ( ! isset( $wp_query ) ) {
 246          _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
 247          return false;
 248      }
 249  
 250      return $wp_query->is_author( $author );
 251  }
 252  
 253  /**
 254   * Determines whether the query is for an existing category archive page.
 255   *
 256   * If the $category parameter is specified, this function will additionally
 257   * check if the query is for one of the categories specified.
 258   *
 259   * For more information on this and similar theme functions, check out
 260   * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/
 261   * Conditional Tags} article in the Theme Developer Handbook.
 262   *
 263   * @since 1.5.0
 264   *
 265   * @global WP_Query $wp_query WordPress Query object.
 266   *
 267   * @param int|string|int[]|string[] $category Optional. Category ID, name, slug, or array of such
 268   *                                            to check against. Default empty.
 269   * @return bool Whether the query is for an existing category archive page.
 270   */
 271  function is_category( $category = '' ) {
 272      global $wp_query;
 273  
 274      if ( ! isset( $wp_query ) ) {
 275          _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
 276          return false;
 277      }
 278  
 279      return $wp_query->is_category( $category );
 280  }
 281  
 282  /**
 283   * Determines whether the query is for an existing tag archive page.
 284   *
 285   * If the $tag parameter is specified, this function will additionally
 286   * check if the query is for one of the tags specified.
 287   *
 288   * For more information on this and similar theme functions, check out
 289   * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/
 290   * Conditional Tags} article in the Theme Developer Handbook.
 291   *
 292   * @since 2.3.0
 293   *
 294   * @global WP_Query $wp_query WordPress Query object.
 295   *
 296   * @param int|string|int[]|string[] $tag Optional. Tag ID, name, slug, or array of such
 297   *                                       to check against. Default empty.
 298   * @return bool Whether the query is for an existing tag archive page.
 299   */
 300  function is_tag( $tag = '' ) {
 301      global $wp_query;
 302  
 303      if ( ! isset( $wp_query ) ) {
 304          _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
 305          return false;
 306      }
 307  
 308      return $wp_query->is_tag( $tag );
 309  }
 310  
 311  /**
 312   * Determines whether the query is for an existing custom taxonomy archive page.
 313   *
 314   * If the $taxonomy parameter is specified, this function will additionally
 315   * check if the query is for that specific $taxonomy.
 316   *
 317   * If the $term parameter is specified in addition to the $taxonomy parameter,
 318   * this function will additionally check if the query is for one of the terms
 319   * specified.
 320   *
 321   * For more information on this and similar theme functions, check out
 322   * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/
 323   * Conditional Tags} article in the Theme Developer Handbook.
 324   *
 325   * @since 2.5.0
 326   *
 327   * @global WP_Query $wp_query WordPress Query object.
 328   *
 329   * @param string|string[]           $taxonomy Optional. Taxonomy slug or slugs to check against.
 330   *                                            Default empty.
 331   * @param int|string|int[]|string[] $term     Optional. Term ID, name, slug, or array of such
 332   *                                            to check against. Default empty.
 333   * @return bool Whether the query is for an existing custom taxonomy archive page.
 334   *              True for custom taxonomy archive pages, false for built-in taxonomies
 335   *              (category and tag archives).
 336   */
 337  function is_tax( $taxonomy = '', $term = '' ) {
 338      global $wp_query;
 339  
 340      if ( ! isset( $wp_query ) ) {
 341          _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
 342          return false;
 343      }
 344  
 345      return $wp_query->is_tax( $taxonomy, $term );
 346  }
 347  
 348  /**
 349   * Determines whether the query is for an existing date archive.
 350   *
 351   * For more information on this and similar theme functions, check out
 352   * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/
 353   * Conditional Tags} article in the Theme Developer Handbook.
 354   *
 355   * @since 1.5.0
 356   *
 357   * @global WP_Query $wp_query WordPress Query object.
 358   *
 359   * @return bool Whether the query is for an existing date archive.
 360   */
 361  function is_date() {
 362      global $wp_query;
 363  
 364      if ( ! isset( $wp_query ) ) {
 365          _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
 366          return false;
 367      }
 368  
 369      return $wp_query->is_date();
 370  }
 371  
 372  /**
 373   * Determines whether the query is for an existing day archive.
 374   *
 375   * A conditional check to test whether the page is a date-based archive page displaying posts for the current day.
 376   *
 377   * For more information on this and similar theme functions, check out
 378   * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/
 379   * Conditional Tags} article in the Theme Developer Handbook.
 380   *
 381   * @since 1.5.0
 382   *
 383   * @global WP_Query $wp_query WordPress Query object.
 384   *
 385   * @return bool Whether the query is for an existing day archive.
 386   */
 387  function is_day() {
 388      global $wp_query;
 389  
 390      if ( ! isset( $wp_query ) ) {
 391          _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
 392          return false;
 393      }
 394  
 395      return $wp_query->is_day();
 396  }
 397  
 398  /**
 399   * Determines whether the query is for a feed.
 400   *
 401   * For more information on this and similar theme functions, check out
 402   * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/
 403   * Conditional Tags} article in the Theme Developer Handbook.
 404   *
 405   * @since 1.5.0
 406   *
 407   * @global WP_Query $wp_query WordPress Query object.
 408   *
 409   * @param string|string[] $feeds Optional. Feed type or array of feed types
 410   *                                         to check against. Default empty.
 411   * @return bool Whether the query is for a feed.
 412   */
 413  function is_feed( $feeds = '' ) {
 414      global $wp_query;
 415  
 416      if ( ! isset( $wp_query ) ) {
 417          _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
 418          return false;
 419      }
 420  
 421      return $wp_query->is_feed( $feeds );
 422  }
 423  
 424  /**
 425   * Is the query for a comments feed?
 426   *
 427   * @since 3.0.0
 428   *
 429   * @global WP_Query $wp_query WordPress Query object.
 430   *
 431   * @return bool Whether the query is for a comments feed.
 432   */
 433  function is_comment_feed() {
 434      global $wp_query;
 435  
 436      if ( ! isset( $wp_query ) ) {
 437          _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
 438          return false;
 439      }
 440  
 441      return $wp_query->is_comment_feed();
 442  }
 443  
 444  /**
 445   * Determines whether the query is for the front page of the site.
 446   *
 447   * This is for what is displayed at your site's main URL.
 448   *
 449   * Depends on the site's "Front page displays" Reading Settings 'show_on_front' and 'page_on_front'.
 450   *
 451   * If you set a static page for the front page of your site, this function will return
 452   * true when viewing that page.
 453   *
 454   * Otherwise the same as {@see is_home()}.
 455   *
 456   * For more information on this and similar theme functions, check out
 457   * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/
 458   * Conditional Tags} article in the Theme Developer Handbook.
 459   *
 460   * @since 2.5.0
 461   *
 462   * @global WP_Query $wp_query WordPress Query object.
 463   *
 464   * @return bool Whether the query is for the front page of the site.
 465   */
 466  function is_front_page() {
 467      global $wp_query;
 468  
 469      if ( ! isset( $wp_query ) ) {
 470          _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
 471          return false;
 472      }
 473  
 474      return $wp_query->is_front_page();
 475  }
 476  
 477  /**
 478   * Determines whether the query is for the blog homepage.
 479   *
 480   * The blog homepage is the page that shows the time-based blog content of the site.
 481   *
 482   * is_home() is dependent on the site's "Front page displays" Reading Settings 'show_on_front'
 483   * and 'page_for_posts'.
 484   *
 485   * If a static page is set for the front page of the site, this function will return true only
 486   * on the page you set as the "Posts page".
 487   *
 488   * For more information on this and similar theme functions, check out
 489   * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/
 490   * Conditional Tags} article in the Theme Developer Handbook.
 491   *
 492   * @since 1.5.0
 493   *
 494   * @see is_front_page()
 495   * @global WP_Query $wp_query WordPress Query object.
 496   *
 497   * @return bool Whether the query is for the blog homepage.
 498   */
 499  function is_home() {
 500      global $wp_query;
 501  
 502      if ( ! isset( $wp_query ) ) {
 503          _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
 504          return false;
 505      }
 506  
 507      return $wp_query->is_home();
 508  }
 509  
 510  /**
 511   * Determines whether the query is for the Privacy Policy page.
 512   *
 513   * The Privacy Policy page is the page that shows the Privacy Policy content of the site.
 514   *
 515   * is_privacy_policy() is dependent on the site's "Change your Privacy Policy page" Privacy Settings 'wp_page_for_privacy_policy'.
 516   *
 517   * This function will return true only on the page you set as the "Privacy Policy page".
 518   *
 519   * For more information on this and similar theme functions, check out
 520   * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/
 521   * Conditional Tags} article in the Theme Developer Handbook.
 522   *
 523   * @since 5.2.0
 524   *
 525   * @global WP_Query $wp_query WordPress Query object.
 526   *
 527   * @return bool Whether the query is for the Privacy Policy page.
 528   */
 529  function is_privacy_policy() {
 530      global $wp_query;
 531  
 532      if ( ! isset( $wp_query ) ) {
 533          _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
 534          return false;
 535      }
 536  
 537      return $wp_query->is_privacy_policy();
 538  }
 539  
 540  /**
 541   * Determines whether the query is for an existing month archive.
 542   *
 543   * For more information on this and similar theme functions, check out
 544   * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/
 545   * Conditional Tags} article in the Theme Developer Handbook.
 546   *
 547   * @since 1.5.0
 548   *
 549   * @global WP_Query $wp_query WordPress Query object.
 550   *
 551   * @return bool Whether the query is for an existing month archive.
 552   */
 553  function is_month() {
 554      global $wp_query;
 555  
 556      if ( ! isset( $wp_query ) ) {
 557          _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
 558          return false;
 559      }
 560  
 561      return $wp_query->is_month();
 562  }
 563  
 564  /**
 565   * Determines whether the query is for an existing single page.
 566   *
 567   * If the $page parameter is specified, this function will additionally
 568   * check if the query is for one of the pages specified.
 569   *
 570   * For more information on this and similar theme functions, check out
 571   * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/
 572   * Conditional Tags} article in the Theme Developer Handbook.
 573   *
 574   * @since 1.5.0
 575   *
 576   * @see is_single()
 577   * @see is_singular()
 578   * @global WP_Query $wp_query WordPress Query object.
 579   *
 580   * @param int|string|int[]|string[] $page Optional. Page ID, title, slug, or array of such
 581   *                                        to check against. Default empty.
 582   * @return bool Whether the query is for an existing single page.
 583   */
 584  function is_page( $page = '' ) {
 585      global $wp_query;
 586  
 587      if ( ! isset( $wp_query ) ) {
 588          _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
 589          return false;
 590      }
 591  
 592      return $wp_query->is_page( $page );
 593  }
 594  
 595  /**
 596   * Determines whether the query is for a paged result and not for the first page.
 597   *
 598   * For more information on this and similar theme functions, check out
 599   * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/
 600   * Conditional Tags} article in the Theme Developer Handbook.
 601   *
 602   * @since 1.5.0
 603   *
 604   * @global WP_Query $wp_query WordPress Query object.
 605   *
 606   * @return bool Whether the query is for a paged result.
 607   */
 608  function is_paged() {
 609      global $wp_query;
 610  
 611      if ( ! isset( $wp_query ) ) {
 612          _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
 613          return false;
 614      }
 615  
 616      return $wp_query->is_paged();
 617  }
 618  
 619  /**
 620   * Determines whether the query is for a post or page preview.
 621   *
 622   * For more information on this and similar theme functions, check out
 623   * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/
 624   * Conditional Tags} article in the Theme Developer Handbook.
 625   *
 626   * @since 2.0.0
 627   *
 628   * @global WP_Query $wp_query WordPress Query object.
 629   *
 630   * @return bool Whether the query is for a post or page preview.
 631   */
 632  function is_preview() {
 633      global $wp_query;
 634  
 635      if ( ! isset( $wp_query ) ) {
 636          _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
 637          return false;
 638      }
 639  
 640      return $wp_query->is_preview();
 641  }
 642  
 643  /**
 644   * Is the query for the robots.txt file?
 645   *
 646   * @since 2.1.0
 647   *
 648   * @global WP_Query $wp_query WordPress Query object.
 649   *
 650   * @return bool Whether the query is for the robots.txt file.
 651   */
 652  function is_robots() {
 653      global $wp_query;
 654  
 655      if ( ! isset( $wp_query ) ) {
 656          _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
 657          return false;
 658      }
 659  
 660      return $wp_query->is_robots();
 661  }
 662  
 663  /**
 664   * Is the query for the favicon.ico file?
 665   *
 666   * @since 5.4.0
 667   *
 668   * @global WP_Query $wp_query WordPress Query object.
 669   *
 670   * @return bool Whether the query is for the favicon.ico file.
 671   */
 672  function is_favicon() {
 673      global $wp_query;
 674  
 675      if ( ! isset( $wp_query ) ) {
 676          _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
 677          return false;
 678      }
 679  
 680      return $wp_query->is_favicon();
 681  }
 682  
 683  /**
 684   * Is the query for a sitemap?
 685   *
 686   * @since 7.1.0
 687   *
 688   * @global WP_Query $wp_query WordPress Query object.
 689   *
 690   * @return bool Whether the query is for a sitemap.
 691   */
 692  function is_sitemap(): bool {
 693      global $wp_query;
 694  
 695      if ( ! isset( $wp_query ) ) {
 696          _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '7.1.0' );
 697          return false;
 698      }
 699  
 700      return $wp_query->is_sitemap();
 701  }
 702  
 703  /**
 704   * Determines whether the query is for random content.
 705   *
 706   * True for a blog home, archive or search request that includes the `random` query
 707   * variable, for example `example.com/random/` or `example.com/random/category/news/`.
 708   *
 709   * @since 7.2.0
 710   *
 711   * @see wp_is_random_content_query()
 712   * @global WP_Query $wp_query WordPress Query object.
 713   *
 714   * @return bool Whether the query is for random content.
 715   */
 716  function is_random(): bool {
 717      global $wp_query;
 718  
 719      if ( ! isset( $wp_query ) ) {
 720          _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '7.2.0' );
 721          return false;
 722      }
 723  
 724      return $wp_query->is_random();
 725  }
 726  
 727  /**
 728   * Determines whether the query is for a search.
 729   *
 730   * For more information on this and similar theme functions, check out
 731   * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/
 732   * Conditional Tags} article in the Theme Developer Handbook.
 733   *
 734   * @since 1.5.0
 735   *
 736   * @global WP_Query $wp_query WordPress Query object.
 737   *
 738   * @return bool Whether the query is for a search.
 739   */
 740  function is_search() {
 741      global $wp_query;
 742  
 743      if ( ! isset( $wp_query ) ) {
 744          _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
 745          return false;
 746      }
 747  
 748      return $wp_query->is_search();
 749  }
 750  
 751  /**
 752   * Determines whether the query is for an existing single post.
 753   *
 754   * Works for any post type, except attachments and pages
 755   *
 756   * If the $post parameter is specified, this function will additionally
 757   * check if the query is for one of the Posts specified.
 758   *
 759   * For more information on this and similar theme functions, check out
 760   * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/
 761   * Conditional Tags} article in the Theme Developer Handbook.
 762   *
 763   * @since 1.5.0
 764   *
 765   * @see is_page()
 766   * @see is_singular()
 767   * @global WP_Query $wp_query WordPress Query object.
 768   *
 769   * @param int|string|int[]|string[] $post Optional. Post ID, title, slug, or array of such
 770   *                                        to check against. Default empty.
 771   * @return bool Whether the query is for an existing single post.
 772   */
 773  function is_single( $post = '' ) {
 774      global $wp_query;
 775  
 776      if ( ! isset( $wp_query ) ) {
 777          _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
 778          return false;
 779      }
 780  
 781      return $wp_query->is_single( $post );
 782  }
 783  
 784  /**
 785   * Determines whether the query is for an existing single post of any post type
 786   * (post, attachment, page, custom post types).
 787   *
 788   * If the $post_types parameter is specified, this function will additionally
 789   * check if the query is for one of the Posts Types specified.
 790   *
 791   * For more information on this and similar theme functions, check out
 792   * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/
 793   * Conditional Tags} article in the Theme Developer Handbook.
 794   *
 795   * @since 1.5.0
 796   *
 797   * @see is_page()
 798   * @see is_single()
 799   * @global WP_Query $wp_query WordPress Query object.
 800   *
 801   * @param string|string[] $post_types Optional. Post type or array of post types
 802   *                                    to check against. Default empty.
 803   * @return bool Whether the query is for an existing single post
 804   *              or any of the given post types.
 805   */
 806  function is_singular( $post_types = '' ) {
 807      global $wp_query;
 808  
 809      if ( ! isset( $wp_query ) ) {
 810          _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
 811          return false;
 812      }
 813  
 814      return $wp_query->is_singular( $post_types );
 815  }
 816  
 817  /**
 818   * Determines whether the query is for a specific time.
 819   *
 820   * For more information on this and similar theme functions, check out
 821   * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/
 822   * Conditional Tags} article in the Theme Developer Handbook.
 823   *
 824   * @since 1.5.0
 825   *
 826   * @global WP_Query $wp_query WordPress Query object.
 827   *
 828   * @return bool Whether the query is for a specific time.
 829   */
 830  function is_time() {
 831      global $wp_query;
 832  
 833      if ( ! isset( $wp_query ) ) {
 834          _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
 835          return false;
 836      }
 837  
 838      return $wp_query->is_time();
 839  }
 840  
 841  /**
 842   * Determines whether the query is for a trackback endpoint call.
 843   *
 844   * For more information on this and similar theme functions, check out
 845   * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/
 846   * Conditional Tags} article in the Theme Developer Handbook.
 847   *
 848   * @since 1.5.0
 849   *
 850   * @global WP_Query $wp_query WordPress Query object.
 851   *
 852   * @return bool Whether the query is for a trackback endpoint call.
 853   */
 854  function is_trackback() {
 855      global $wp_query;
 856  
 857      if ( ! isset( $wp_query ) ) {
 858          _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
 859          return false;
 860      }
 861  
 862      return $wp_query->is_trackback();
 863  }
 864  
 865  /**
 866   * Determines whether the query is for an existing year archive.
 867   *
 868   * For more information on this and similar theme functions, check out
 869   * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/
 870   * Conditional Tags} article in the Theme Developer Handbook.
 871   *
 872   * @since 1.5.0
 873   *
 874   * @global WP_Query $wp_query WordPress Query object.
 875   *
 876   * @return bool Whether the query is for an existing year archive.
 877   */
 878  function is_year() {
 879      global $wp_query;
 880  
 881      if ( ! isset( $wp_query ) ) {
 882          _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
 883          return false;
 884      }
 885  
 886      return $wp_query->is_year();
 887  }
 888  
 889  /**
 890   * Determines whether the query has resulted in a 404 (returns no results).
 891   *
 892   * For more information on this and similar theme functions, check out
 893   * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/
 894   * Conditional Tags} article in the Theme Developer Handbook.
 895   *
 896   * @since 1.5.0
 897   *
 898   * @global WP_Query $wp_query WordPress Query object.
 899   *
 900   * @return bool Whether the query is a 404 error.
 901   */
 902  function is_404() {
 903      global $wp_query;
 904  
 905      if ( ! isset( $wp_query ) ) {
 906          _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
 907          return false;
 908      }
 909  
 910      return $wp_query->is_404();
 911  }
 912  
 913  /**
 914   * Is the query for an embedded post?
 915   *
 916   * @since 4.4.0
 917   *
 918   * @global WP_Query $wp_query WordPress Query object.
 919   *
 920   * @return bool Whether the query is for an embedded post.
 921   */
 922  function is_embed() {
 923      global $wp_query;
 924  
 925      if ( ! isset( $wp_query ) ) {
 926          _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
 927          return false;
 928      }
 929  
 930      return $wp_query->is_embed();
 931  }
 932  
 933  /**
 934   * Determines whether the query is the main query.
 935   *
 936   * For more information on this and similar theme functions, check out
 937   * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/
 938   * Conditional Tags} article in the Theme Developer Handbook.
 939   *
 940   * @since 3.3.0
 941   *
 942   * @global WP_Query $wp_query WordPress Query object.
 943   *
 944   * @return bool Whether the query is the main query.
 945   */
 946  function is_main_query() {
 947      global $wp_query;
 948  
 949      if ( ! isset( $wp_query ) ) {
 950          _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '6.1.0' );
 951          return false;
 952      }
 953  
 954      if ( 'pre_get_posts' === current_filter() ) {
 955          _doing_it_wrong(
 956              __FUNCTION__,
 957              sprintf(
 958                  /* translators: 1: pre_get_posts, 2: WP_Query->is_main_query(), 3: is_main_query(), 4: Documentation URL. */
 959                  __( 'In %1$s, use the %2$s method, not the %3$s function. See %4$s.' ),
 960                  '<code>pre_get_posts</code>',
 961                  '<code>WP_Query->is_main_query()</code>',
 962                  '<code>is_main_query()</code>',
 963                  __( 'https://developer.wordpress.org/reference/functions/is_main_query/' )
 964              ),
 965              '3.7.0'
 966          );
 967      }
 968  
 969      return $wp_query->is_main_query();
 970  }
 971  
 972  /*
 973   * The Loop. Post loop control.
 974   */
 975  
 976  /**
 977   * Determines whether current WordPress query has posts to loop over.
 978   *
 979   * @since 1.5.0
 980   *
 981   * @global WP_Query $wp_query WordPress Query object.
 982   *
 983   * @phpstan-impure
 984   *
 985   * @return bool True if posts are available, false if end of the loop.
 986   */
 987  function have_posts() {
 988      global $wp_query;
 989  
 990      if ( ! isset( $wp_query ) ) {
 991          return false;
 992      }
 993  
 994      return $wp_query->have_posts();
 995  }
 996  
 997  /**
 998   * Determines whether the caller is in the Loop.
 999   *
1000   * For more information on this and similar theme functions, check out
1001   * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/
1002   * Conditional Tags} article in the Theme Developer Handbook.
1003   *
1004   * @since 2.0.0
1005   *
1006   * @global WP_Query $wp_query WordPress Query object.
1007   *
1008   * @return bool True if caller is within loop, false if loop hasn't started or ended.
1009   */
1010  function in_the_loop() {
1011      global $wp_query;
1012  
1013      if ( ! isset( $wp_query ) ) {
1014          return false;
1015      }
1016  
1017      return $wp_query->in_the_loop;
1018  }
1019  
1020  /**
1021   * Rewind the loop posts.
1022   *
1023   * @since 1.5.0
1024   *
1025   * @global WP_Query $wp_query WordPress Query object.
1026   */
1027  function rewind_posts() {
1028      global $wp_query;
1029  
1030      if ( ! isset( $wp_query ) ) {
1031          return;
1032      }
1033  
1034      $wp_query->rewind_posts();
1035  }
1036  
1037  /**
1038   * Iterate the post index in the loop.
1039   *
1040   * @since 1.5.0
1041   *
1042   * @global WP_Query $wp_query WordPress Query object.
1043   */
1044  function the_post() {
1045      global $wp_query;
1046  
1047      if ( ! isset( $wp_query ) ) {
1048          return;
1049      }
1050  
1051      $wp_query->the_post();
1052  }
1053  
1054  /*
1055   * Comments loop.
1056   */
1057  
1058  /**
1059   * Determines whether current WordPress query has comments to loop over.
1060   *
1061   * @since 2.2.0
1062   *
1063   * @global WP_Query $wp_query WordPress Query object.
1064   *
1065   * @phpstan-impure
1066   *
1067   * @return bool True if comments are available, false if no more comments.
1068   */
1069  function have_comments() {
1070      global $wp_query;
1071  
1072      if ( ! isset( $wp_query ) ) {
1073          return false;
1074      }
1075  
1076      return $wp_query->have_comments();
1077  }
1078  
1079  /**
1080   * Iterate comment index in the comment loop.
1081   *
1082   * @since 2.2.0
1083   *
1084   * @global WP_Query $wp_query WordPress Query object.
1085   */
1086  function the_comment() {
1087      global $wp_query;
1088  
1089      if ( ! isset( $wp_query ) ) {
1090          return;
1091      }
1092  
1093      $wp_query->the_comment();
1094  }
1095  
1096  /**
1097   * Redirect old slugs to the correct permalink.
1098   *
1099   * Attempts to find the current slug from the past slugs.
1100   *
1101   * @since 2.1.0
1102   */
1103  function wp_old_slug_redirect() {
1104      if ( is_404() && '' !== get_query_var( 'name' ) ) {
1105          // Guess the current post type based on the query vars.
1106          if ( get_query_var( 'post_type' ) ) {
1107              $post_type = get_query_var( 'post_type' );
1108          } elseif ( get_query_var( 'attachment' ) ) {
1109              $post_type = 'attachment';
1110          } elseif ( get_query_var( 'pagename' ) ) {
1111              $post_type = 'page';
1112          } else {
1113              $post_type = 'post';
1114          }
1115  
1116          if ( is_array( $post_type ) ) {
1117              if ( count( $post_type ) > 1 ) {
1118                  return;
1119              }
1120              $post_type = reset( $post_type );
1121          }
1122  
1123          // Do not attempt redirect for hierarchical post types.
1124          if ( is_post_type_hierarchical( $post_type ) ) {
1125              return;
1126          }
1127  
1128          $id = _find_post_by_old_slug( $post_type );
1129  
1130          if ( ! $id ) {
1131              $id = _find_post_by_old_date( $post_type );
1132          }
1133  
1134          /**
1135           * Filters the old slug redirect post ID.
1136           *
1137           * @since 4.9.3
1138           *
1139           * @param int $id The redirect post ID.
1140           */
1141          $id = apply_filters( 'old_slug_redirect_post_id', $id );
1142  
1143          if ( ! $id ) {
1144              return;
1145          }
1146  
1147          $link = get_permalink( $id );
1148  
1149          if ( get_query_var( 'paged' ) > 1 ) {
1150              $link = user_trailingslashit( trailingslashit( $link ) . 'page/' . get_query_var( 'paged' ) );
1151          } elseif ( is_embed() ) {
1152              $link = user_trailingslashit( trailingslashit( $link ) . 'embed' );
1153          }
1154  
1155          /**
1156           * Filters the old slug redirect URL.
1157           *
1158           * @since 4.4.0
1159           *
1160           * @param string $link The redirect URL.
1161           */
1162          $link = apply_filters( 'old_slug_redirect_url', $link );
1163  
1164          if ( ! $link ) {
1165              return;
1166          }
1167  
1168          wp_redirect( $link, 301 ); // Permanent redirect.
1169          exit;
1170      }
1171  }
1172  
1173  /**
1174   * Find the post ID for redirecting an old slug.
1175   *
1176   * @since 4.9.3
1177   * @access private
1178   *
1179   * @see wp_old_slug_redirect()
1180   * @global wpdb $wpdb WordPress database abstraction object.
1181   *
1182   * @param string $post_type The current post type based on the query vars.
1183   * @return int The Post ID.
1184   */
1185  function _find_post_by_old_slug( $post_type ) {
1186      global $wpdb;
1187  
1188      $query = $wpdb->prepare( "SELECT post_id FROM $wpdb->postmeta, $wpdb->posts WHERE ID = post_id AND post_type = %s AND meta_key = '_wp_old_slug' AND meta_value = %s", $post_type, get_query_var( 'name' ) );
1189  
1190      /*
1191       * If year, monthnum, or day have been specified, make our query more precise
1192       * just in case there are multiple identical _wp_old_slug values.
1193       */
1194      if ( get_query_var( 'year' ) ) {
1195          $query .= $wpdb->prepare( ' AND YEAR(post_date) = %d', get_query_var( 'year' ) );
1196      }
1197      if ( get_query_var( 'monthnum' ) ) {
1198          $query .= $wpdb->prepare( ' AND MONTH(post_date) = %d', get_query_var( 'monthnum' ) );
1199      }
1200      if ( get_query_var( 'day' ) ) {
1201          $query .= $wpdb->prepare( ' AND DAYOFMONTH(post_date) = %d', get_query_var( 'day' ) );
1202      }
1203  
1204      $key          = md5( $query );
1205      $last_changed = wp_cache_get_last_changed( 'posts' );
1206      $cache_key    = "find_post_by_old_slug:$key";
1207      $cache        = wp_cache_get_salted( $cache_key, 'post-queries', $last_changed );
1208      if ( false !== $cache ) {
1209          $id = $cache;
1210      } else {
1211          $id = (int) $wpdb->get_var( $query );
1212          wp_cache_set_salted( $cache_key, $id, 'post-queries', $last_changed );
1213      }
1214  
1215      return $id;
1216  }
1217  
1218  /**
1219   * Find the post ID for redirecting an old date.
1220   *
1221   * @since 4.9.3
1222   * @access private
1223   *
1224   * @see wp_old_slug_redirect()
1225   * @global wpdb $wpdb WordPress database abstraction object.
1226   *
1227   * @param string $post_type The current post type based on the query vars.
1228   * @return int The Post ID.
1229   */
1230  function _find_post_by_old_date( $post_type ) {
1231      global $wpdb;
1232  
1233      $date_query = '';
1234      if ( get_query_var( 'year' ) ) {
1235          $date_query .= $wpdb->prepare( ' AND YEAR(pm_date.meta_value) = %d', get_query_var( 'year' ) );
1236      }
1237      if ( get_query_var( 'monthnum' ) ) {
1238          $date_query .= $wpdb->prepare( ' AND MONTH(pm_date.meta_value) = %d', get_query_var( 'monthnum' ) );
1239      }
1240      if ( get_query_var( 'day' ) ) {
1241          $date_query .= $wpdb->prepare( ' AND DAYOFMONTH(pm_date.meta_value) = %d', get_query_var( 'day' ) );
1242      }
1243  
1244      $id = 0;
1245      if ( $date_query ) {
1246          $query        = $wpdb->prepare( "SELECT post_id FROM $wpdb->postmeta AS pm_date, $wpdb->posts WHERE ID = post_id AND post_type = %s AND meta_key = '_wp_old_date' AND post_name = %s" . $date_query, $post_type, get_query_var( 'name' ) );
1247          $key          = md5( $query );
1248          $last_changed = wp_cache_get_last_changed( 'posts' );
1249          $cache_key    = "find_post_by_old_date:$key";
1250          $cache        = wp_cache_get_salted( $cache_key, 'post-queries', $last_changed );
1251          if ( false !== $cache ) {
1252              $id = $cache;
1253          } else {
1254              $id = (int) $wpdb->get_var( $query );
1255              if ( ! $id ) {
1256                  // Check to see if an old slug matches the old date.
1257                  $id = (int) $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->posts, $wpdb->postmeta AS pm_slug, $wpdb->postmeta AS pm_date WHERE ID = pm_slug.post_id AND ID = pm_date.post_id AND post_type = %s AND pm_slug.meta_key = '_wp_old_slug' AND pm_slug.meta_value = %s AND pm_date.meta_key = '_wp_old_date'" . $date_query, $post_type, get_query_var( 'name' ) ) );
1258              }
1259              wp_cache_set_salted( $cache_key, $id, 'post-queries', $last_changed );
1260          }
1261      }
1262  
1263      return $id;
1264  }
1265  
1266  /**
1267   * Determines whether random content redirects are enabled.
1268   *
1269   * @since 7.2.0
1270   *
1271   * @return bool True if random content redirects are enabled, false otherwise.
1272   */
1273  function wp_is_random_content_redirect_enabled(): bool {
1274      /**
1275       * Filters whether random content redirects are enabled.
1276       *
1277       * Random content redirects add rewrite rules for URLs such as `/random/`, so the
1278       * rewrite rules need to be flushed after changing the value returned by this filter.
1279       *
1280       * @since 7.2.0
1281       *
1282       * @param bool $enabled Whether random content redirects are enabled. Default true.
1283       */
1284      return (bool) apply_filters( 'wp_enable_random_content_redirect', true );
1285  }
1286  
1287  /**
1288   * Determines whether a query is a request for random content.
1289   *
1290   * A random content request is a front end, main query for the blog home, an archive,
1291   * or search results that includes the `random` query variable and at least one
1292   * `randomable` post type. For example
1293   * `example.com/random/`, `example.com/random/category/news/` or `example.com/?s=foo&random`.
1294   *
1295   * Singular (including embeds) and feed requests are never random content requests,
1296   * nor are requests using an HTTP method other than GET or HEAD.
1297   *
1298   * @since 7.2.0
1299   *
1300   * @param WP_Query $query The query to check.
1301   * @return bool True if the query is a request for random content, false otherwise.
1302   */
1303  function wp_is_random_content_query( WP_Query $query ): bool {
1304      if (
1305          ! $query->is_random()
1306          || ! $query->is_main_query()
1307          || is_admin()
1308          || ! wp_is_random_content_redirect_enabled()
1309      ) {
1310          return false;
1311      }
1312  
1313      $request_method = isset( $_SERVER['REQUEST_METHOD'] ) && is_string( $_SERVER['REQUEST_METHOD'] ) ? strtoupper( $_SERVER['REQUEST_METHOD'] ) : '';
1314      if ( 'GET' !== $request_method && 'HEAD' !== $request_method ) {
1315          return false;
1316      }
1317  
1318      return array() !== wp_get_random_content_post_types( $query );
1319  }
1320  
1321  /**
1322   * Retrieves the post types eligible for selection by a random content request.
1323   *
1324   * These are the post types the query would otherwise return, limited to those
1325   * registered as `randomable`.
1326   *
1327   * @since 7.2.0
1328   *
1329   * @see register_post_type()
1330   *
1331   * @param WP_Query $query The query to retrieve the post types for.
1332   * @return string[] Post type names eligible for random selection.
1333   *
1334   * @phpstan-return list<string>
1335   */
1336  function wp_get_random_content_post_types( WP_Query $query ): array {
1337      $post_types = $query->get( 'post_type' );
1338  
1339      if ( empty( $post_types ) && $query->is_tax() && $query->tax_query instanceof WP_Tax_Query ) {
1340          // Match WP_Query::get_posts(): searchable post types using any of the queried taxonomies.
1341          $taxonomies = array_keys( $query->tax_query->queried_terms );
1342          $post_types = array();
1343  
1344          foreach ( get_post_types( array( 'exclude_from_search' => false ) ) as $post_type ) {
1345              $object_taxonomies = 'attachment' === $post_type ? get_taxonomies_for_attachments() : get_object_taxonomies( $post_type );
1346              if ( array_intersect( $taxonomies, $object_taxonomies ) ) {
1347                  $post_types[] = $post_type;
1348              }
1349          }
1350      } elseif ( empty( $post_types ) && ! $query->is_search() ) {
1351          $post_types = array( 'post' );
1352      }
1353  
1354      if ( empty( $post_types ) || 'any' === $post_types ) {
1355          $post_types = get_post_types( array( 'exclude_from_search' => false ) );
1356      }
1357  
1358      $random_post_types = array();
1359  
1360      foreach ( (array) $post_types as $post_type ) {
1361          // Plugins may set invalid values on the `post_type` query variable.
1362          if ( ! is_string( $post_type ) ) {
1363              continue;
1364          }
1365  
1366          $post_type_object = get_post_type_object( $post_type );
1367  
1368          if ( $post_type_object && $post_type_object->randomable ) {
1369              $random_post_types[] = $post_type_object->name;
1370          }
1371      }
1372  
1373      return $random_post_types;
1374  }
1375  
1376  /**
1377   * Limits the main query to posts eligible for random content requests.
1378   *
1379   * The main query is used to select the random post so the request runs the same
1380   * query, and the same filters, as the archive it is randomizing. As such, callbacks
1381   * on the {@see 'pre_get_posts'} action may modify the posts eligible for selection,
1382   * for example to limit the post types or exclude a category:
1383   *
1384   *     add_action(
1385   *         'pre_get_posts',
1386   *         static function ( WP_Query $query ) {
1387   *             if ( wp_is_random_content_query( $query ) ) {
1388   *                 $query->set( 'category__not_in', array( 1 ) );
1389   *             }
1390   *         }
1391   *     );
1392   *
1393   * Only randomable post types and published posts without a password are eligible.
1394   * The number and order of posts are enforced by {@see wp_random_content_post_limits()} and
1395   * {@see wp_random_content_posts_orderby()}, so callbacks modifying them on archives do not
1396   * affect the selection.
1397   *
1398   * Added to the {@see 'pre_get_posts'} action by {@see WP::handle_random()} while the
1399   * random content query runs, so it runs after other callbacks.
1400   *
1401   * @since 7.2.0
1402   *
1403   * @param WP_Query $query The WP_Query instance (passed by reference).
1404   */
1405  function wp_random_content_pre_get_posts( $query ): void {
1406      if ( ! wp_is_random_content_query( $query ) ) {
1407          return;
1408      }
1409  
1410      $query->set( 'post_type', wp_get_random_content_post_types( $query ) );
1411      $query->set( 'post_status', 'publish' );
1412      $query->set( 'has_password', false );
1413      $query->set( 'ignore_sticky_posts', true );
1414      $query->set( 'no_found_rows', true );
1415      $query->set( 'update_post_meta_cache', false );
1416      $query->set( 'update_post_term_cache', false );
1417      // The order and number of posts are enforced by filters, which a suppressed query skips.
1418      $query->set( 'suppress_filters', false );
1419  }
1420  
1421  /**
1422   * Orders the random content query randomly.
1423   *
1424   * Added to the {@see 'posts_orderby'} filter by {@see WP::handle_random()} while the random
1425   * content query runs. Enforcing the order in SQL prevents {@see 'pre_get_posts'}
1426   * callbacks setting the order of posts on archives from affecting the selection.
1427   *
1428   * @since 7.2.0
1429   *
1430   * @param string   $orderby The ORDER BY clause of the query.
1431   * @param WP_Query $query   The WP_Query instance.
1432   * @return string The ORDER BY clause of the query.
1433   */
1434  function wp_random_content_posts_orderby( $orderby, WP_Query $query ): string {
1435      if ( wp_is_random_content_query( $query ) ) {
1436          return 'RAND()';
1437      }
1438  
1439      return (string) $orderby;
1440  }
1441  
1442  /**
1443   * Limits the random content query to a single post.
1444   *
1445   * Added to the {@see 'post_limits'} filter by {@see WP::handle_random()} while the random
1446   * content query runs. Enforcing the limit in SQL prevents {@see 'pre_get_posts'}
1447   * callbacks setting the number of posts, offset or page on archives from affecting
1448   * the selection.
1449   *
1450   * @since 7.2.0
1451   *
1452   * @param string   $limits The LIMIT clause of the query.
1453   * @param WP_Query $query  The WP_Query instance.
1454   * @return string The LIMIT clause of the query.
1455   */
1456  function wp_random_content_post_limits( $limits, WP_Query $query ): string {
1457      if ( wp_is_random_content_query( $query ) ) {
1458          return 'LIMIT 0, 1';
1459      }
1460  
1461      return (string) $limits;
1462  }
1463  
1464  /**
1465   * Prevents the random content query from being split into multiple database queries.
1466   *
1467   * Randomly ordered queries are not cached, so there is no benefit to querying the post
1468   * IDs separately to the post objects. Disabling the split allows the random post to be
1469   * retrieved in a single database query.
1470   *
1471   * @since 7.2.0
1472   *
1473   * @param bool     $split_the_query Whether or not to split the query.
1474   * @param WP_Query $query           The WP_Query instance.
1475   * @return bool Whether or not to split the query.
1476   */
1477  function wp_random_content_split_the_query( $split_the_query, WP_Query $query ): bool {
1478      if ( wp_is_random_content_query( $query ) ) {
1479          return false;
1480      }
1481  
1482      return (bool) $split_the_query;
1483  }
1484  
1485  /**
1486   * Redirects random content requests to the randomly selected post.
1487   *
1488   * Called by {@see WP::handle_random()} immediately after the main query selects the
1489   * random post, to avoid unnecessary processing before the redirect. As the redirect
1490   * is sent before {@see WP::send_headers()}, the headers preventing the redirect being
1491   * cached or indexed are sent with it.
1492   *
1493   * If no post is selected, for example on an empty archive, {@see WP::handle_random()}
1494   * handles the request as a 404. If the post's permalink is on another site, the
1495   * request is handled as a regular archive request.
1496   *
1497   * @since 7.2.0
1498   *
1499   * @global WP_Query $wp_query WordPress Query object.
1500   */
1501  function wp_random_content_redirect(): void {
1502      global $wp_query;
1503  
1504      if ( ! wp_is_random_content_query( $wp_query ) || ! $wp_query->post instanceof WP_Post ) {
1505          return;
1506      }
1507  
1508      $location = get_permalink( $wp_query->post );
1509  
1510      // Only consider redirecting permitted locations so wp_safe_redirect doesn't redirect to the fallback.
1511      if ( ! $location || ! wp_validate_redirect( $location, '' ) ) {
1512          return;
1513      }
1514  
1515      nocache_headers();
1516  
1517      if ( ! headers_sent() ) {
1518          header( 'X-Robots-Tag: noindex, follow' );
1519      }
1520  
1521      if ( wp_safe_redirect( $location, 302 ) ) {
1522          exit;
1523      }
1524  }
1525  
1526  /**
1527   * Flushes the rewrite rules when a page or post at the random content base changes.
1528   *
1529   * A published page at the random base, for example `example.com/random/`, takes
1530   * precedence over random content redirects. So does a published post when the
1531   * permalink structure places posts at the same URL, for example `/%postname%/`,
1532   * see {@see WP_Rewrite::post_permalinks_share_random_base()}. As the rewrite rules account
1533   * for the page or post, they need to be regenerated when it is published,
1534   * unpublished, renamed, moved or deleted.
1535   *
1536   * Runs on the {@see 'wp_after_insert_post'} and {@see 'after_delete_post'} actions.
1537   *
1538   * @since 7.2.0
1539   *
1540   * @see WP_Rewrite::random_rewrite_rules()
1541   * @global WP_Rewrite $wp_rewrite WordPress rewrite component.
1542   *
1543   * @param int          $post_id     Post ID.
1544   * @param WP_Post      $post        Post object.
1545   * @param bool         $update      Optional. Whether this is an existing post being updated. Default false.
1546   * @param WP_Post|null $post_before Optional. Post object before the update, null for new posts. Default null.
1547   */
1548  function wp_random_content_flush_rewrite_rules_for_post( int $post_id, WP_Post $post, bool $update = false, ?WP_Post $post_before = null ): void {
1549      global $wp_rewrite;
1550  
1551      if ( ! $wp_rewrite->using_permalinks() || ! wp_is_random_content_redirect_enabled() ) {
1552          return;
1553      }
1554  
1555      foreach ( array( $post, $post_before ) as $page ) {
1556          if (
1557              $page instanceof WP_Post
1558              && $wp_rewrite->get_random_base() === $page->post_name
1559              && (
1560                  ( 'page' === $page->post_type && 0 === (int) $page->post_parent )
1561                  || ( 'post' === $page->post_type && $wp_rewrite->post_permalinks_share_random_base() )
1562              )
1563          ) {
1564              flush_rewrite_rules( false );
1565              return;
1566          }
1567      }
1568  }
1569  
1570  /**
1571   * Set up global post data.
1572   *
1573   * @since 1.5.0
1574   * @since 4.4.0 Added the ability to pass a post ID to `$post`.
1575   *
1576   * @global WP_Query $wp_query WordPress Query object.
1577   *
1578   * @param WP_Post|object|int $post WP_Post instance or Post ID/object.
1579   * @return bool True when finished.
1580   */
1581  function setup_postdata( $post ) {
1582      global $wp_query;
1583  
1584      if ( ! empty( $wp_query ) && $wp_query instanceof WP_Query ) {
1585          return $wp_query->setup_postdata( $post );
1586      }
1587  
1588      return false;
1589  }
1590  
1591  /**
1592   * Generates post data.
1593   *
1594   * @since 5.2.0
1595   *
1596   * @global WP_Query $wp_query WordPress Query object.
1597   *
1598   * @param WP_Post|object|int $post WP_Post instance or Post ID/object.
1599   * @return array|false Elements of post, or false on failure.
1600   */
1601  function generate_postdata( $post ) {
1602      global $wp_query;
1603  
1604      if ( ! empty( $wp_query ) && $wp_query instanceof WP_Query ) {
1605          return $wp_query->generate_postdata( $post );
1606      }
1607  
1608      return false;
1609  }


Generated : Sun Oct 25 08:20:35 2026 Cross-referenced by PHPXref