[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

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

   1  <?php
   2  /**
   3   * Query API: WP_Query class
   4   *
   5   * @package WordPress
   6   * @subpackage Query
   7   * @since 4.7.0
   8   */
   9  
  10  /**
  11   * The WordPress Query class.
  12   *
  13   * @link https://developer.wordpress.org/reference/classes/wp_query/
  14   *
  15   * @since 1.5.0
  16   * @since 4.5.0 Removed the `$comments_popup` property.
  17   */
  18  #[AllowDynamicProperties]
  19  class WP_Query {
  20  
  21      /**
  22       * Query vars set by the user.
  23       *
  24       * @since 1.5.0
  25       * @var ?array
  26       */
  27      public $query;
  28  
  29      /**
  30       * Query vars, after parsing.
  31       *
  32       * @since 1.5.0
  33       * @var array
  34       */
  35      public $query_vars = array();
  36  
  37      /**
  38       * Taxonomy query, as passed to get_tax_sql().
  39       *
  40       * @since 3.1.0
  41       * @var WP_Tax_Query|null A taxonomy query instance.
  42       */
  43      public $tax_query;
  44  
  45      /**
  46       * Metadata query container.
  47       *
  48       * @since 3.2.0
  49       * @var WP_Meta_Query A meta query instance.
  50       */
  51      public $meta_query = false;
  52  
  53      /**
  54       * Date query container.
  55       *
  56       * @since 3.7.0
  57       * @var WP_Date_Query A date query instance.
  58       */
  59      public $date_query = false;
  60  
  61      /**
  62       * Holds the data for a single object that is queried.
  63       *
  64       * Holds the contents of a post, page, category, attachment.
  65       *
  66       * @since 1.5.0
  67       * @var WP_Term|WP_Post_Type|WP_Post|WP_User|null
  68       */
  69      public $queried_object;
  70  
  71      /**
  72       * The ID of the queried object.
  73       *
  74       * @since 1.5.0
  75       * @var ?int
  76       */
  77      public $queried_object_id;
  78  
  79      /**
  80       * SQL for the database query.
  81       *
  82       * @since 2.0.1
  83       * @var ?string
  84       */
  85      public $request;
  86  
  87      /**
  88       * Array of post objects or post IDs.
  89       *
  90       * @since 1.5.0
  91       * @var WP_Post[]|int[]|null
  92       */
  93      public $posts;
  94  
  95      /**
  96       * The number of posts for the current query.
  97       *
  98       * @since 1.5.0
  99       * @var int
 100       */
 101      public $post_count = 0;
 102  
 103      /**
 104       * Index of the current item in the loop.
 105       *
 106       * @since 1.5.0
 107       * @var int
 108       */
 109      public $current_post = -1;
 110  
 111      /**
 112       * Whether the caller is before the loop.
 113       *
 114       * @since 6.3.0
 115       * @var bool
 116       */
 117      public $before_loop = true;
 118  
 119      /**
 120       * Whether the loop has started and the caller is in the loop.
 121       *
 122       * @since 2.0.0
 123       * @var bool
 124       */
 125      public $in_the_loop = false;
 126  
 127      /**
 128       * The current post.
 129       *
 130       * This property does not get populated when the `fields` argument is set to
 131       * `ids` or `id=>parent`.
 132       *
 133       * @since 1.5.0
 134       * @var WP_Post|null
 135       */
 136      public $post;
 137  
 138      /**
 139       * The list of comments for current post.
 140       *
 141       * @since 2.2.0
 142       * @var ?WP_Comment[]
 143       */
 144      public $comments;
 145  
 146      /**
 147       * The number of comments for the posts.
 148       *
 149       * @since 2.2.0
 150       * @var int
 151       */
 152      public $comment_count = 0;
 153  
 154      /**
 155       * The index of the comment in the comment loop.
 156       *
 157       * @since 2.2.0
 158       * @var int
 159       */
 160      public $current_comment = -1;
 161  
 162      /**
 163       * Current comment object.
 164       *
 165       * @since 2.2.0
 166       * @var ?WP_Comment
 167       */
 168      public $comment;
 169  
 170      /**
 171       * The number of found posts for the current query.
 172       *
 173       * If limit clause was not used, equals $post_count.
 174       *
 175       * @since 2.1.0
 176       * @var int
 177       */
 178      public $found_posts = 0;
 179  
 180      /**
 181       * The number of pages.
 182       *
 183       * @since 2.1.0
 184       * @var int
 185       */
 186      public $max_num_pages = 0;
 187  
 188      /**
 189       * The number of comment pages.
 190       *
 191       * @since 2.7.0
 192       * @var int
 193       */
 194      public $max_num_comment_pages = 0;
 195  
 196      /**
 197       * Signifies whether the current query is for a single post.
 198       *
 199       * @since 1.5.0
 200       * @var bool
 201       */
 202      public $is_single = false;
 203  
 204      /**
 205       * Signifies whether the current query is for a preview.
 206       *
 207       * @since 2.0.0
 208       * @var bool
 209       */
 210      public $is_preview = false;
 211  
 212      /**
 213       * Signifies whether the current query is for a page.
 214       *
 215       * @since 1.5.0
 216       * @var bool
 217       */
 218      public $is_page = false;
 219  
 220      /**
 221       * Signifies whether the current query is for an archive.
 222       *
 223       * @since 1.5.0
 224       * @var bool
 225       */
 226      public $is_archive = false;
 227  
 228      /**
 229       * Signifies whether the current query is for a date archive.
 230       *
 231       * @since 1.5.0
 232       * @var bool
 233       */
 234      public $is_date = false;
 235  
 236      /**
 237       * Signifies whether the current query is for a year archive.
 238       *
 239       * @since 1.5.0
 240       * @var bool
 241       */
 242      public $is_year = false;
 243  
 244      /**
 245       * Signifies whether the current query is for a month archive.
 246       *
 247       * @since 1.5.0
 248       * @var bool
 249       */
 250      public $is_month = false;
 251  
 252      /**
 253       * Signifies whether the current query is for a day archive.
 254       *
 255       * @since 1.5.0
 256       * @var bool
 257       */
 258      public $is_day = false;
 259  
 260      /**
 261       * Signifies whether the current query is for a specific time.
 262       *
 263       * @since 1.5.0
 264       * @var bool
 265       */
 266      public $is_time = false;
 267  
 268      /**
 269       * Signifies whether the current query is for an author archive.
 270       *
 271       * @since 1.5.0
 272       * @var bool
 273       */
 274      public $is_author = false;
 275  
 276      /**
 277       * Signifies whether the current query is for a category archive.
 278       *
 279       * @since 1.5.0
 280       * @var bool
 281       */
 282      public $is_category = false;
 283  
 284      /**
 285       * Signifies whether the current query is for a tag archive.
 286       *
 287       * @since 2.3.0
 288       * @var bool
 289       */
 290      public $is_tag = false;
 291  
 292      /**
 293       * Signifies whether the current query is for a taxonomy archive.
 294       *
 295       * @since 2.5.0
 296       * @var bool
 297       */
 298      public $is_tax = false;
 299  
 300      /**
 301       * Signifies whether the current query is for a search.
 302       *
 303       * @since 1.5.0
 304       * @var bool
 305       */
 306      public $is_search = false;
 307  
 308      /**
 309       * Signifies whether the current query is for a feed.
 310       *
 311       * @since 1.5.0
 312       * @var bool
 313       */
 314      public $is_feed = false;
 315  
 316      /**
 317       * Signifies whether the current query is for a comment feed.
 318       *
 319       * @since 2.2.0
 320       * @var bool
 321       */
 322      public $is_comment_feed = false;
 323  
 324      /**
 325       * Signifies whether the current query is for trackback endpoint call.
 326       *
 327       * @since 1.5.0
 328       * @var bool
 329       */
 330      public $is_trackback = false;
 331  
 332      /**
 333       * Signifies whether the current query is for the site homepage.
 334       *
 335       * @since 1.5.0
 336       * @var bool
 337       */
 338      public $is_home = false;
 339  
 340      /**
 341       * Signifies whether the current query is for the Privacy Policy page.
 342       *
 343       * @since 5.2.0
 344       * @var bool
 345       */
 346      public $is_privacy_policy = false;
 347  
 348      /**
 349       * Signifies whether the current query couldn't find anything.
 350       *
 351       * @since 1.5.0
 352       * @var bool
 353       */
 354      public $is_404 = false;
 355  
 356      /**
 357       * Signifies whether the current query is for an embed.
 358       *
 359       * @since 4.4.0
 360       * @var bool
 361       */
 362      public $is_embed = false;
 363  
 364      /**
 365       * Signifies whether the current query is for a paged result and not for the first page.
 366       *
 367       * @since 1.5.0
 368       * @var bool
 369       */
 370      public $is_paged = false;
 371  
 372      /**
 373       * Signifies whether the current query is for an administrative interface page.
 374       *
 375       * @since 1.5.0
 376       * @var bool
 377       */
 378      public $is_admin = false;
 379  
 380      /**
 381       * Signifies whether the current query is for an attachment page.
 382       *
 383       * @since 2.0.0
 384       * @var bool
 385       */
 386      public $is_attachment = false;
 387  
 388      /**
 389       * Signifies whether the current query is for an existing single post of any post type
 390       * (post, attachment, page, custom post types).
 391       *
 392       * @since 2.1.0
 393       * @var bool
 394       */
 395      public $is_singular = false;
 396  
 397      /**
 398       * Signifies whether the current query is for the robots.txt file.
 399       *
 400       * @since 2.1.0
 401       * @var bool
 402       */
 403      public $is_robots = false;
 404  
 405      /**
 406       * Signifies whether the current query is for the favicon.ico file.
 407       *
 408       * @since 5.4.0
 409       * @var bool
 410       */
 411      public $is_favicon = false;
 412  
 413      /**
 414       * Signifies whether the current query is for a sitemap.
 415       *
 416       * @since 7.1.0
 417       */
 418      public bool $is_sitemap = false;
 419  
 420      /**
 421       * Signifies whether the current query is for the page_for_posts page.
 422       *
 423       * Basically, the homepage if the option isn't set for the static homepage.
 424       *
 425       * @since 2.1.0
 426       * @var bool
 427       */
 428      public $is_posts_page = false;
 429  
 430      /**
 431       * Signifies whether the current query is for a post type archive.
 432       *
 433       * @since 3.1.0
 434       * @var bool
 435       */
 436      public $is_post_type_archive = false;
 437  
 438      /**
 439       * Stores the ->query_vars state like md5(serialize( $this->query_vars ) ) so we know
 440       * whether we have to re-parse because something has changed
 441       *
 442       * @since 3.1.0
 443       * @var bool|string
 444       */
 445      private $query_vars_hash = false;
 446  
 447      /**
 448       * Whether query vars have changed since the initial parse_query() call. Used to catch modifications to query vars made
 449       * via pre_get_posts hooks.
 450       *
 451       * @since 3.1.1
 452       * @var bool
 453       */
 454      private $query_vars_changed = true;
 455  
 456      /**
 457       * Set if post thumbnails are cached
 458       *
 459       * @since 3.2.0
 460       * @var bool
 461       */
 462      public $thumbnails_cached = false;
 463  
 464      /**
 465       * Controls whether an attachment query should include filenames or not.
 466       *
 467       * @since 6.0.3
 468       * @var bool
 469       */
 470      protected $allow_query_attachment_by_filename = false;
 471  
 472      /**
 473       * Cached list of search stopwords.
 474       *
 475       * @since 3.7.0
 476       * @var ?array
 477       */
 478      private $stopwords;
 479  
 480      private $compat_fields = array( 'query_vars_hash', 'query_vars_changed' );
 481  
 482      private $compat_methods = array( 'init_query_flags', 'parse_tax_query' );
 483  
 484      /**
 485       * The cache key generated by the query.
 486       *
 487       * The cache key is generated by the method ::generate_cache_key() after the
 488       * query has been normalized.
 489       *
 490       * @since 6.8.0
 491       * @var string
 492       */
 493      private $query_cache_key = '';
 494  
 495      /**
 496       * Resets query flags to false.
 497       *
 498       * The query flags are what page info WordPress was able to figure out.
 499       *
 500       * @since 2.0.0
 501       */
 502  	private function init_query_flags() {
 503          $this->is_single            = false;
 504          $this->is_preview           = false;
 505          $this->is_page              = false;
 506          $this->is_archive           = false;
 507          $this->is_date              = false;
 508          $this->is_year              = false;
 509          $this->is_month             = false;
 510          $this->is_day               = false;
 511          $this->is_time              = false;
 512          $this->is_author            = false;
 513          $this->is_category          = false;
 514          $this->is_tag               = false;
 515          $this->is_tax               = false;
 516          $this->is_search            = false;
 517          $this->is_feed              = false;
 518          $this->is_comment_feed      = false;
 519          $this->is_trackback         = false;
 520          $this->is_home              = false;
 521          $this->is_privacy_policy    = false;
 522          $this->is_404               = false;
 523          $this->is_paged             = false;
 524          $this->is_admin             = false;
 525          $this->is_attachment        = false;
 526          $this->is_singular          = false;
 527          $this->is_robots            = false;
 528          $this->is_favicon           = false;
 529          $this->is_sitemap           = false;
 530          $this->is_posts_page        = false;
 531          $this->is_post_type_archive = false;
 532      }
 533  
 534      /**
 535       * Initiates object properties and sets default values.
 536       *
 537       * @since 1.5.0
 538       */
 539  	public function init() {
 540          unset( $this->posts );
 541          unset( $this->query );
 542          $this->query_vars = array();
 543          unset( $this->queried_object );
 544          unset( $this->queried_object_id );
 545          $this->post_count   = 0;
 546          $this->current_post = -1;
 547          $this->in_the_loop  = false;
 548          $this->before_loop  = true;
 549          unset( $this->request );
 550          unset( $this->post );
 551          unset( $this->comments );
 552          unset( $this->comment );
 553          $this->comment_count         = 0;
 554          $this->current_comment       = -1;
 555          $this->found_posts           = 0;
 556          $this->max_num_pages         = 0;
 557          $this->max_num_comment_pages = 0;
 558  
 559          $this->init_query_flags();
 560      }
 561  
 562      /**
 563       * Reparses the query vars.
 564       *
 565       * @since 1.5.0
 566       */
 567  	public function parse_query_vars() {
 568          $this->parse_query();
 569      }
 570  
 571      /**
 572       * Fills in the query variables, which do not exist within the parameter.
 573       *
 574       * @since 2.1.0
 575       * @since 4.5.0 Removed the `comments_popup` public query variable.
 576       *
 577       * @param array $query_vars Defined query variables.
 578       * @return array Complete query variables with undefined ones filled in empty.
 579       */
 580  	public function fill_query_vars( $query_vars ) {
 581          $keys = array(
 582              'error',
 583              'm',
 584              'p',
 585              'post_parent',
 586              'subpost',
 587              'subpost_id',
 588              'attachment',
 589              'attachment_id',
 590              'name',
 591              'pagename',
 592              'page_id',
 593              'second',
 594              'minute',
 595              'hour',
 596              'day',
 597              'monthnum',
 598              'year',
 599              'w',
 600              'category_name',
 601              'tag',
 602              'cat',
 603              'tag_id',
 604              'author',
 605              'author_name',
 606              'feed',
 607              'tb',
 608              'paged',
 609              'meta_key',
 610              'meta_value',
 611              'preview',
 612              's',
 613              'sentence',
 614              'title',
 615              'fields',
 616              'menu_order',
 617              'embed',
 618          );
 619  
 620          foreach ( $keys as $key ) {
 621              if ( ! isset( $query_vars[ $key ] ) ) {
 622                  $query_vars[ $key ] = '';
 623              }
 624          }
 625  
 626          $array_keys = array(
 627              'category__in',
 628              'category__not_in',
 629              'category__and',
 630              'post__in',
 631              'post__not_in',
 632              'post_name__in',
 633              'tag__in',
 634              'tag__not_in',
 635              'tag__and',
 636              'tag_slug__in',
 637              'tag_slug__and',
 638              'post_parent__in',
 639              'post_parent__not_in',
 640              'author__in',
 641              'author__not_in',
 642              'search_columns',
 643          );
 644  
 645          foreach ( $array_keys as $key ) {
 646              if ( ! isset( $query_vars[ $key ] ) ) {
 647                  $query_vars[ $key ] = array();
 648              }
 649          }
 650  
 651          return $query_vars;
 652      }
 653  
 654      /**
 655       * Parses a query string and sets query type booleans.
 656       *
 657       * @since 1.5.0
 658       * @since 4.2.0 Introduced the ability to order by specific clauses of a `$meta_query`, by passing the clause's
 659       *              array key to `$orderby`.
 660       * @since 4.4.0 Introduced `$post_name__in` and `$title` parameters. `$s` was updated to support excluded
 661       *              search terms, by prepending a hyphen.
 662       * @since 4.5.0 Removed the `$comments_popup` parameter.
 663       *              Introduced the `$comment_status` and `$ping_status` parameters.
 664       *              Introduced `RAND(x)` syntax for `$orderby`, which allows an integer seed value to random sorts.
 665       * @since 4.6.0 Added 'post_name__in' support for `$orderby`. Introduced the `$lazy_load_term_meta` argument.
 666       * @since 4.9.0 Introduced the `$comment_count` parameter.
 667       * @since 5.1.0 Introduced the `$meta_compare_key` parameter.
 668       * @since 5.3.0 Introduced the `$meta_type_key` parameter.
 669       * @since 6.1.0 Introduced the `$update_menu_item_cache` parameter.
 670       * @since 6.2.0 Introduced the `$search_columns` parameter.
 671       *
 672       * @param string|array $query {
 673       *     Optional. Array or string of Query parameters.
 674       *
 675       *     @type int             $attachment_id          Attachment post ID. Used for 'attachment' post_type.
 676       *     @type int|string      $author                 Author ID, or comma-separated list of IDs.
 677       *     @type string          $author_name            User 'user_nicename'.
 678       *     @type int[]           $author__in             An array of author IDs to query from.
 679       *     @type int[]           $author__not_in         An array of author IDs not to query from.
 680       *     @type bool            $cache_results          Whether to cache post information. Default true.
 681       *     @type int|string      $cat                    Category ID or comma-separated list of IDs (this or any children).
 682       *     @type int[]           $category__and          An array of category IDs (AND in).
 683       *     @type int[]           $category__in           An array of category IDs (OR in, no children).
 684       *     @type int[]           $category__not_in       An array of category IDs (NOT in).
 685       *     @type string          $category_name          Use category slug (not name, this or any children).
 686       *     @type array|int       $comment_count          Filter results by comment count. Provide an integer to match
 687       *                                                   comment count exactly. Provide an array with integer 'value'
 688       *                                                   and 'compare' operator ('=', '!=', '>', '>=', '<', '<=' ) to
 689       *                                                   compare against comment_count in a specific way.
 690       *     @type string          $comment_status         Comment status.
 691       *     @type int             $comments_per_page      The number of comments to return per page.
 692       *                                                   Default 'comments_per_page' option.
 693       *     @type array           $date_query             An associative array of WP_Date_Query arguments.
 694       *                                                   See WP_Date_Query::__construct().
 695       *     @type int             $day                    Day of the month. Default empty. Accepts numbers 1-31.
 696       *     @type bool            $exact                  Whether to search by exact keyword. Default false.
 697       *     @type string          $fields                 Post fields to query for. Accepts:
 698       *                                                   - '' Returns an array of complete post objects (`WP_Post[]`).
 699       *                                                   - 'ids' Returns an array of post IDs (`int[]`).
 700       *                                                   - 'id=>parent' Returns an associative array of parent post IDs,
 701       *                                                     keyed by post ID (`int[]`).
 702       *                                                   Default ''.
 703       *     @type int             $hour                   Hour of the day. Default empty. Accepts numbers 0-23.
 704       *     @type int|bool        $ignore_sticky_posts    Whether to ignore sticky posts or not. Setting this to false
 705       *                                                   excludes stickies from 'post__in'. Accepts 1|true, 0|false.
 706       *                                                   Default false.
 707       *     @type int             $m                      Combination YearMonth. Accepts any four-digit year and month
 708       *                                                   numbers 01-12. Default empty.
 709       *     @type string|string[] $meta_key               Meta key or keys to filter by.
 710       *     @type string|string[] $meta_value             Meta value or values to filter by.
 711       *     @type string          $meta_compare           MySQL operator used for comparing the meta value.
 712       *                                                   See WP_Meta_Query::__construct() for accepted values and default value.
 713       *     @type string          $meta_compare_key       MySQL operator used for comparing the meta key.
 714       *                                                   See WP_Meta_Query::__construct() for accepted values and default value.
 715       *     @type string          $meta_type              MySQL data type that the meta_value column will be CAST to for comparisons.
 716       *                                                   See WP_Meta_Query::__construct() for accepted values and default value.
 717       *     @type string          $meta_type_key          MySQL data type that the meta_key column will be CAST to for comparisons.
 718       *                                                   See WP_Meta_Query::__construct() for accepted values and default value.
 719       *     @type array           $meta_query             An associative array of WP_Meta_Query arguments.
 720       *                                                   See WP_Meta_Query::__construct() for accepted values.
 721       *     @type int             $menu_order             The menu order of the posts.
 722       *     @type int             $minute                 Minute of the hour. Default empty. Accepts numbers 0-59.
 723       *     @type int             $monthnum               The two-digit month. Default empty. Accepts numbers 1-12.
 724       *     @type string          $name                   Post slug.
 725       *     @type bool            $nopaging               Show all posts (true) or paginate (false). Default false.
 726       *     @type bool            $no_found_rows          Whether to skip counting the total rows found. Enabling can improve
 727       *                                                   performance. Default false.
 728       *     @type int             $offset                 The number of posts to offset before retrieval.
 729       *     @type string          $order                  Designates ascending or descending order of posts. Default 'DESC'.
 730       *                                                   Accepts 'ASC', 'DESC'.
 731       *     @type string|array    $orderby                Sort retrieved posts by parameter. One or more options may be passed.
 732       *                                                   To use 'meta_value', or 'meta_value_num', 'meta_key=keyname' must be
 733       *                                                   also be defined. To sort by a specific `$meta_query` clause, use that
 734       *                                                   clause's array key. Accepts:
 735       *                                                   - 'none'
 736       *                                                   - 'name'
 737       *                                                   - 'author'
 738       *                                                   - 'date'
 739       *                                                   - 'title'
 740       *                                                   - 'modified'
 741       *                                                   - 'menu_order'
 742       *                                                   - 'parent'
 743       *                                                   - 'ID'
 744       *                                                   - 'rand'
 745       *                                                   - 'relevance'
 746       *                                                   - 'RAND(x)' (where 'x' is an integer seed value)
 747       *                                                   - 'comment_count'
 748       *                                                   - 'meta_value'
 749       *                                                   - 'meta_value_num'
 750       *                                                   - 'post__in'
 751       *                                                   - 'post_name__in'
 752       *                                                   - 'post_parent__in'
 753       *                                                   - The array keys of `$meta_query`.
 754       *                                                   Default is 'date', except when a search is being performed, when
 755       *                                                   the default is 'relevance'.
 756       *     @type int             $p                      Post ID.
 757       *     @type int             $page                   Show the number of posts that would show up on page X of a
 758       *                                                   static front page.
 759       *     @type int             $paged                  The number of the current page.
 760       *     @type int             $page_id                Page ID.
 761       *     @type string          $pagename               Page slug.
 762       *     @type string          $perm                   Show posts if user has the appropriate capability.
 763       *     @type string          $ping_status            Ping status.
 764       *     @type int[]           $post__in               An array of post IDs to retrieve, sticky posts will be included.
 765       *     @type int[]           $post__not_in           An array of post IDs not to retrieve. Note: a string of comma-
 766       *                                                   separated IDs will NOT work.
 767       *     @type string          $post_mime_type         The mime type of the post. Used for 'attachment' post_type.
 768       *     @type string[]        $post_name__in          An array of post slugs that results must match.
 769       *     @type int             $post_parent            Page ID to retrieve child pages for. Use 0 to only retrieve
 770       *                                                   top-level pages.
 771       *     @type int[]           $post_parent__in        An array containing parent page IDs to query child pages from.
 772       *     @type int[]           $post_parent__not_in    An array containing parent page IDs not to query child pages from.
 773       *     @type string|string[] $post_type              A post type slug (string) or array of post type slugs.
 774       *                                                   Default 'any' if using 'tax_query'.
 775       *     @type string|string[] $post_status            A post status (string) or array of post statuses.
 776       *     @type int             $posts_per_page         The number of posts to query for. Use -1 to request all posts.
 777       *     @type int             $posts_per_archive_page The number of posts to query for by archive page. Overrides
 778       *                                                   'posts_per_page' when is_archive(), or is_search() are true.
 779       *     @type string          $s                      Search keyword(s). Prepending a term with a hyphen will
 780       *                                                   exclude posts matching that term. Eg, 'pillow -sofa' will
 781       *                                                   return posts containing 'pillow' but not 'sofa'. The
 782       *                                                   character used for exclusion can be modified using the
 783       *                                                   the 'wp_query_search_exclusion_prefix' filter.
 784       *     @type string[]        $search_columns         Array of column names to be searched. Accepts 'post_title',
 785       *                                                   'post_excerpt' and 'post_content'. Default empty array.
 786       *     @type int             $second                 Second of the minute. Default empty. Accepts numbers 0-59.
 787       *     @type bool            $sentence               Whether to search by phrase. Default false.
 788       *     @type bool            $suppress_filters       Whether to suppress filters. Default false.
 789       *     @type string          $tag                    Tag slug. Comma-separated (either), Plus-separated (all).
 790       *     @type int[]           $tag__and               An array of tag IDs (AND in).
 791       *     @type int[]           $tag__in                An array of tag IDs (OR in).
 792       *     @type int[]           $tag__not_in            An array of tag IDs (NOT in).
 793       *     @type int             $tag_id                 Tag id or comma-separated list of IDs.
 794       *     @type string[]        $tag_slug__and          An array of tag slugs (AND in).
 795       *     @type string[]        $tag_slug__in           An array of tag slugs (OR in). unless 'ignore_sticky_posts' is
 796       *                                                   true. Note: a string of comma-separated IDs will NOT work.
 797       *     @type array           $tax_query              An associative array of WP_Tax_Query arguments.
 798       *                                                   See WP_Tax_Query::__construct().
 799       *     @type string          $title                  Post title.
 800       *     @type bool            $update_post_meta_cache Whether to update the post meta cache. Default true.
 801       *     @type bool            $update_post_term_cache Whether to update the post term cache. Default true.
 802       *     @type bool            $update_menu_item_cache Whether to update the menu item cache. Default false.
 803       *     @type bool            $lazy_load_term_meta    Whether to lazy-load term meta. Setting to false will
 804       *                                                   disable cache priming for term meta, so that each
 805       *                                                   get_term_meta() call will hit the database.
 806       *                                                   Defaults to the value of `$update_post_term_cache`.
 807       *     @type int             $w                      The week number of the year. Default empty. Accepts numbers 1-53.
 808       *     @type int             $year                   The four-digit year. Default empty. Accepts any four-digit year.
 809       * }
 810       */
 811  	public function parse_query( $query = '' ) {
 812          if ( ! empty( $query ) ) {
 813              $this->init();
 814              $this->query      = wp_parse_args( $query );
 815              $this->query_vars = $this->query;
 816          } elseif ( ! isset( $this->query ) ) {
 817              $this->query = $this->query_vars;
 818          }
 819  
 820          $this->query_vars         = $this->fill_query_vars( $this->query_vars );
 821          $query_vars               = &$this->query_vars;
 822          $this->query_vars_changed = true;
 823  
 824          if ( ! empty( $query_vars['robots'] ) ) {
 825              $this->is_robots = true;
 826          } elseif ( ! empty( $query_vars['favicon'] ) ) {
 827              $this->is_favicon = true;
 828          } elseif ( ! empty( $query_vars['sitemap'] ) ) {
 829              $this->is_sitemap = true;
 830          }
 831  
 832          if ( ! is_scalar( $query_vars['p'] ) || (int) $query_vars['p'] < 0 ) {
 833              $query_vars['p']     = 0;
 834              $query_vars['error'] = '404';
 835          } else {
 836              $query_vars['p'] = (int) $query_vars['p'];
 837          }
 838  
 839          $query_vars['page_id']  = is_scalar( $query_vars['page_id'] ) ? absint( $query_vars['page_id'] ) : 0;
 840          $query_vars['year']     = is_scalar( $query_vars['year'] ) ? absint( $query_vars['year'] ) : 0;
 841          $query_vars['monthnum'] = is_scalar( $query_vars['monthnum'] ) ? absint( $query_vars['monthnum'] ) : 0;
 842          $query_vars['day']      = is_scalar( $query_vars['day'] ) ? absint( $query_vars['day'] ) : 0;
 843          $query_vars['w']        = is_scalar( $query_vars['w'] ) ? absint( $query_vars['w'] ) : 0;
 844          $query_vars['m']        = is_scalar( $query_vars['m'] ) ? preg_replace( '|[^0-9]|', '', $query_vars['m'] ) : '';
 845          $query_vars['paged']    = is_scalar( $query_vars['paged'] ) ? absint( $query_vars['paged'] ) : 0;
 846          $query_vars['cat']      = preg_replace( '|[^0-9,-]|', '', $query_vars['cat'] ); // Array or comma-separated list of positive or negative integers.
 847          $query_vars['author']   = is_scalar( $query_vars['author'] ) ? preg_replace( '|[^0-9,-]|', '', $query_vars['author'] ) : ''; // Comma-separated list of positive or negative integers.
 848          $query_vars['pagename'] = is_scalar( $query_vars['pagename'] ) ? trim( $query_vars['pagename'] ) : '';
 849          $query_vars['name']     = is_scalar( $query_vars['name'] ) ? trim( $query_vars['name'] ) : '';
 850          $query_vars['title']    = is_scalar( $query_vars['title'] ) ? trim( $query_vars['title'] ) : '';
 851  
 852          if ( is_scalar( $query_vars['hour'] ) && '' !== $query_vars['hour'] ) {
 853              $query_vars['hour'] = absint( $query_vars['hour'] );
 854          } else {
 855              $query_vars['hour'] = '';
 856          }
 857  
 858          if ( is_scalar( $query_vars['minute'] ) && '' !== $query_vars['minute'] ) {
 859              $query_vars['minute'] = absint( $query_vars['minute'] );
 860          } else {
 861              $query_vars['minute'] = '';
 862          }
 863  
 864          if ( is_scalar( $query_vars['second'] ) && '' !== $query_vars['second'] ) {
 865              $query_vars['second'] = absint( $query_vars['second'] );
 866          } else {
 867              $query_vars['second'] = '';
 868          }
 869  
 870          if ( is_scalar( $query_vars['menu_order'] ) && '' !== $query_vars['menu_order'] ) {
 871              $query_vars['menu_order'] = absint( $query_vars['menu_order'] );
 872          } else {
 873              $query_vars['menu_order'] = '';
 874          }
 875  
 876          // Fairly large, potentially too large, upper bound for search string lengths.
 877          if ( ! is_scalar( $query_vars['s'] ) || ( ! empty( $query_vars['s'] ) && strlen( $query_vars['s'] ) > 1600 ) ) {
 878              $query_vars['s'] = '';
 879          }
 880  
 881          // Compat. Map subpost to attachment.
 882          if ( is_scalar( $query_vars['subpost'] ) && '' != $query_vars['subpost'] ) {
 883              $query_vars['attachment'] = $query_vars['subpost'];
 884          }
 885          if ( is_scalar( $query_vars['subpost_id'] ) && '' != $query_vars['subpost_id'] ) {
 886              $query_vars['attachment_id'] = $query_vars['subpost_id'];
 887          }
 888  
 889          $query_vars['attachment_id'] = is_scalar( $query_vars['attachment_id'] ) ? absint( $query_vars['attachment_id'] ) : 0;
 890  
 891          if ( ( '' !== $query_vars['attachment'] ) || ! empty( $query_vars['attachment_id'] ) ) {
 892              $this->is_single     = true;
 893              $this->is_attachment = true;
 894          } elseif ( '' !== $query_vars['name'] ) {
 895              $this->is_single = true;
 896          } elseif ( $query_vars['p'] ) {
 897              $this->is_single = true;
 898          } elseif ( '' !== $query_vars['pagename'] || ! empty( $query_vars['page_id'] ) ) {
 899              $this->is_page   = true;
 900              $this->is_single = false;
 901          } else {
 902              // Look for archive queries. Dates, categories, authors, search, post type archives.
 903  
 904              if ( isset( $this->query['s'] ) ) {
 905                  $this->is_search = true;
 906              }
 907  
 908              if ( '' !== $query_vars['second'] ) {
 909                  $this->is_time = true;
 910                  $this->is_date = true;
 911              }
 912  
 913              if ( '' !== $query_vars['minute'] ) {
 914                  $this->is_time = true;
 915                  $this->is_date = true;
 916              }
 917  
 918              if ( '' !== $query_vars['hour'] ) {
 919                  $this->is_time = true;
 920                  $this->is_date = true;
 921              }
 922  
 923              if ( $query_vars['day'] ) {
 924                  if ( ! $this->is_date ) {
 925                      $date = sprintf( '%04d-%02d-%02d', $query_vars['year'], $query_vars['monthnum'], $query_vars['day'] );
 926                      if ( $query_vars['monthnum'] && $query_vars['year'] && ! wp_checkdate( $query_vars['monthnum'], $query_vars['day'], $query_vars['year'], $date ) ) {
 927                          $query_vars['error'] = '404';
 928                      } else {
 929                          $this->is_day  = true;
 930                          $this->is_date = true;
 931                      }
 932                  }
 933              }
 934  
 935              if ( $query_vars['monthnum'] ) {
 936                  if ( ! $this->is_date ) {
 937                      if ( 12 < $query_vars['monthnum'] ) {
 938                          $query_vars['error'] = '404';
 939                      } else {
 940                          $this->is_month = true;
 941                          $this->is_date  = true;
 942                      }
 943                  }
 944              }
 945  
 946              if ( $query_vars['year'] ) {
 947                  if ( ! $this->is_date ) {
 948                      $this->is_year = true;
 949                      $this->is_date = true;
 950                  }
 951              }
 952  
 953              if ( $query_vars['m'] ) {
 954                  $this->is_date = true;
 955                  if ( strlen( $query_vars['m'] ) > 9 ) {
 956                      $this->is_time = true;
 957                  } elseif ( strlen( $query_vars['m'] ) > 7 ) {
 958                      $this->is_day = true;
 959                  } elseif ( strlen( $query_vars['m'] ) > 5 ) {
 960                      $this->is_month = true;
 961                  } else {
 962                      $this->is_year = true;
 963                  }
 964              }
 965  
 966              if ( $query_vars['w'] ) {
 967                  $this->is_date = true;
 968              }
 969  
 970              $this->query_vars_hash = false;
 971              $this->parse_tax_query( $query_vars );
 972  
 973              foreach ( $this->tax_query->queries as $tax_query ) {
 974                  if ( ! is_array( $tax_query ) ) {
 975                      continue;
 976                  }
 977  
 978                  if ( isset( $tax_query['operator'] ) && 'NOT IN' !== $tax_query['operator'] ) {
 979                      switch ( $tax_query['taxonomy'] ) {
 980                          case 'category':
 981                              $this->is_category = true;
 982                              break;
 983                          case 'post_tag':
 984                              $this->is_tag = true;
 985                              break;
 986                          default:
 987                              $this->is_tax = true;
 988                      }
 989                  }
 990              }
 991              unset( $tax_query );
 992  
 993              if ( empty( $query_vars['author'] ) || ( '0' == $query_vars['author'] ) ) {
 994                  $this->is_author = false;
 995              } else {
 996                  $this->is_author = true;
 997              }
 998  
 999              if ( '' !== $query_vars['author_name'] ) {
1000                  $this->is_author = true;
1001              }
1002  
1003              if ( ! empty( $query_vars['post_type'] ) && ! is_array( $query_vars['post_type'] ) ) {
1004                  $post_type_obj = get_post_type_object( $query_vars['post_type'] );
1005                  if ( ! empty( $post_type_obj->has_archive ) ) {
1006                      $this->is_post_type_archive = true;
1007                  }
1008              }
1009  
1010              if ( $this->is_post_type_archive || $this->is_date || $this->is_author || $this->is_category || $this->is_tag || $this->is_tax ) {
1011                  $this->is_archive = true;
1012              }
1013          }
1014  
1015          if ( '' != $query_vars['feed'] ) {
1016              $this->is_feed = true;
1017          }
1018  
1019          if ( '' != $query_vars['embed'] ) {
1020              $this->is_embed = true;
1021          }
1022  
1023          if ( '' != $query_vars['tb'] ) {
1024              $this->is_trackback = true;
1025          }
1026  
1027          if ( '' != $query_vars['paged'] && ( (int) $query_vars['paged'] > 1 ) ) {
1028              $this->is_paged = true;
1029          }
1030  
1031          // If we're previewing inside the write screen.
1032          if ( '' != $query_vars['preview'] ) {
1033              $this->is_preview = true;
1034          }
1035  
1036          if ( is_admin() ) {
1037              $this->is_admin = true;
1038          }
1039  
1040          if ( str_contains( $query_vars['feed'], 'comments-' ) ) {
1041              $query_vars['feed']         = str_replace( 'comments-', '', $query_vars['feed'] );
1042              $query_vars['withcomments'] = 1;
1043          }
1044  
1045          $this->is_singular = $this->is_single || $this->is_page || $this->is_attachment;
1046  
1047          if ( $this->is_feed && ( ! empty( $query_vars['withcomments'] ) || ( empty( $query_vars['withoutcomments'] ) && $this->is_singular ) ) ) {
1048              $this->is_comment_feed = true;
1049          }
1050  
1051          if ( ! ( $this->is_singular || $this->is_archive || $this->is_search || $this->is_feed
1052                  || ( wp_is_serving_rest_request() && $this->is_main_query() )
1053                  || $this->is_trackback || $this->is_404 || $this->is_admin || $this->is_robots || $this->is_favicon || $this->is_sitemap ) ) {
1054              $this->is_home = true;
1055          }
1056  
1057          // Correct `is_*` for 'page_on_front' and 'page_for_posts'.
1058          if ( $this->is_home && 'page' === get_option( 'show_on_front' ) && get_option( 'page_on_front' ) ) {
1059              $_query = wp_parse_args( $this->query );
1060              // 'pagename' can be set and empty depending on matched rewrite rules. Ignore an empty 'pagename'.
1061              if ( isset( $_query['pagename'] ) && '' === $_query['pagename'] ) {
1062                  unset( $_query['pagename'] );
1063              }
1064  
1065              unset( $_query['embed'] );
1066  
1067              if ( empty( $_query ) || ! array_diff( array_keys( $_query ), array( 'preview', 'page', 'paged', 'cpage' ) ) ) {
1068                  $this->is_page         = true;
1069                  $this->is_home         = false;
1070                  $query_vars['page_id'] = get_option( 'page_on_front' );
1071                  // Correct <!--nextpage--> for 'page_on_front'.
1072                  if ( ! empty( $query_vars['paged'] ) ) {
1073                      $query_vars['page'] = $query_vars['paged'];
1074                      unset( $query_vars['paged'] );
1075                  }
1076              }
1077          }
1078  
1079          if ( '' !== $query_vars['pagename'] ) {
1080              $this->queried_object = get_page_by_path( $query_vars['pagename'] );
1081  
1082              if ( $this->queried_object && 'attachment' === $this->queried_object->post_type ) {
1083                  if ( preg_match( '/^[^%]*%(?:postname)%/', get_option( 'permalink_structure' ) ) ) {
1084                      // See if we also have a post with the same slug.
1085                      $post = get_page_by_path( $query_vars['pagename'], OBJECT, 'post' );
1086                      if ( $post ) {
1087                          $this->queried_object = $post;
1088                          $this->is_page        = false;
1089                          $this->is_single      = true;
1090                      }
1091                  }
1092              }
1093  
1094              if ( ! empty( $this->queried_object ) ) {
1095                  $this->queried_object_id = (int) $this->queried_object->ID;
1096              } else {
1097                  unset( $this->queried_object );
1098              }
1099  
1100              if ( 'page' === get_option( 'show_on_front' ) && isset( $this->queried_object_id ) && get_option( 'page_for_posts' ) == $this->queried_object_id ) {
1101                  $this->is_page       = false;
1102                  $this->is_home       = true;
1103                  $this->is_posts_page = true;
1104              }
1105  
1106              if ( isset( $this->queried_object_id ) && get_option( 'wp_page_for_privacy_policy' ) == $this->queried_object_id ) {
1107                  $this->is_privacy_policy = true;
1108              }
1109          }
1110  
1111          if ( $query_vars['page_id'] ) {
1112              if ( 'page' === get_option( 'show_on_front' ) && get_option( 'page_for_posts' ) == $query_vars['page_id'] ) {
1113                  $this->is_page       = false;
1114                  $this->is_home       = true;
1115                  $this->is_posts_page = true;
1116              }
1117  
1118              if ( get_option( 'wp_page_for_privacy_policy' ) == $query_vars['page_id'] ) {
1119                  $this->is_privacy_policy = true;
1120              }
1121          }
1122  
1123          if ( ! empty( $query_vars['post_type'] ) ) {
1124              if ( is_array( $query_vars['post_type'] ) ) {
1125                  $query_vars['post_type'] = array_map( 'sanitize_key', array_unique( $query_vars['post_type'] ) );
1126                  sort( $query_vars['post_type'] );
1127              } else {
1128                  $query_vars['post_type'] = sanitize_key( $query_vars['post_type'] );
1129              }
1130          }
1131  
1132          if ( ! empty( $query_vars['post_status'] ) ) {
1133              if ( is_array( $query_vars['post_status'] ) ) {
1134                  $query_vars['post_status'] = array_map( 'sanitize_key', array_unique( $query_vars['post_status'] ) );
1135                  sort( $query_vars['post_status'] );
1136              } else {
1137                  $query_vars['post_status'] = preg_replace( '|[^a-z0-9_,-]|', '', $query_vars['post_status'] );
1138              }
1139          }
1140  
1141          if ( $this->is_posts_page && ( ! isset( $query_vars['withcomments'] ) || ! $query_vars['withcomments'] ) ) {
1142              $this->is_comment_feed = false;
1143          }
1144  
1145          $this->is_singular = $this->is_single || $this->is_page || $this->is_attachment;
1146          // Done correcting `is_*` for 'page_on_front' and 'page_for_posts'.
1147  
1148          if ( '404' == $query_vars['error'] ) {
1149              $this->set_404();
1150          }
1151  
1152          $this->is_embed = $this->is_embed && ( $this->is_singular || $this->is_404 );
1153  
1154          $this->query_vars_hash    = md5( serialize( $this->query_vars ) );
1155          $this->query_vars_changed = false;
1156  
1157          /**
1158           * Fires after the main query vars have been parsed.
1159           *
1160           * @since 1.5.0
1161           *
1162           * @param WP_Query $query The WP_Query instance (passed by reference).
1163           */
1164          do_action_ref_array( 'parse_query', array( &$this ) );
1165      }
1166  
1167      /**
1168       * Parses various taxonomy related query vars.
1169       *
1170       * For BC, this method is not marked as protected. See [28987].
1171       *
1172       * @since 3.1.0
1173       *
1174       * @param array $query_vars The query variables. Passed by reference.
1175       */
1176  	public function parse_tax_query( &$query_vars ) {
1177          if ( ! empty( $query_vars['tax_query'] ) && is_array( $query_vars['tax_query'] ) ) {
1178              $tax_query = $query_vars['tax_query'];
1179          } else {
1180              $tax_query = array();
1181          }
1182  
1183          if ( ! empty( $query_vars['taxonomy'] ) && ! empty( $query_vars['term'] ) ) {
1184              $tax_query[] = array(
1185                  'taxonomy' => $query_vars['taxonomy'],
1186                  'terms'    => array( $query_vars['term'] ),
1187                  'field'    => 'slug',
1188              );
1189          }
1190  
1191          foreach ( get_taxonomies( array(), 'objects' ) as $taxonomy => $t ) {
1192              if ( 'post_tag' === $taxonomy ) {
1193                  continue; // Handled further down in the $query_vars['tag'] block.
1194              }
1195  
1196              if ( $t->query_var && ! empty( $query_vars[ $t->query_var ] ) ) {
1197                  $tax_query_defaults = array(
1198                      'taxonomy' => $taxonomy,
1199                      'field'    => 'slug',
1200                  );
1201  
1202                  if ( is_string( $query_vars[ $t->query_var ] ) && ! empty( $t->rewrite['hierarchical'] ) ) {
1203                      $query_vars[ $t->query_var ] = wp_basename( $query_vars[ $t->query_var ] );
1204                  }
1205  
1206                  $term = $query_vars[ $t->query_var ];
1207  
1208                  if ( ! is_array( $term ) ) {
1209                      $term = explode( ',', $term );
1210                      $term = array_map( 'trim', $term );
1211                  }
1212                  sort( $term );
1213                  $term = implode( ',', $term );
1214  
1215                  if ( str_contains( $term, '+' ) ) {
1216                      $terms = preg_split( '/[+]+/', $term );
1217                      foreach ( $terms as $term ) {
1218                          $tax_query[] = array_merge(
1219                              $tax_query_defaults,
1220                              array(
1221                                  'terms' => array( $term ),
1222                              )
1223                          );
1224                      }
1225                  } else {
1226                      $tax_query[] = array_merge(
1227                          $tax_query_defaults,
1228                          array(
1229                              'terms' => preg_split( '/[,]+/', $term ),
1230                          )
1231                      );
1232                  }
1233              }
1234          }
1235  
1236          // If query string 'cat' is an array, implode it.
1237          if ( is_array( $query_vars['cat'] ) ) {
1238              $query_vars['cat'] = implode( ',', $query_vars['cat'] );
1239          }
1240  
1241          // Category stuff.
1242  
1243          if ( ! empty( $query_vars['cat'] ) && ! $this->is_singular ) {
1244              $cat_in     = array();
1245              $cat_not_in = array();
1246  
1247              $cat_array = preg_split( '/[,\s]+/', urldecode( $query_vars['cat'] ) );
1248              $cat_array = array_map( 'intval', $cat_array );
1249              sort( $cat_array );
1250              $query_vars['cat'] = implode( ',', $cat_array );
1251  
1252              foreach ( $cat_array as $cat ) {
1253                  if ( $cat > 0 ) {
1254                      $cat_in[] = $cat;
1255                  } elseif ( $cat < 0 ) {
1256                      $cat_not_in[] = abs( $cat );
1257                  }
1258              }
1259  
1260              if ( ! empty( $cat_in ) ) {
1261                  $tax_query[] = array(
1262                      'taxonomy'         => 'category',
1263                      'terms'            => $cat_in,
1264                      'field'            => 'term_id',
1265                      'include_children' => true,
1266                  );
1267              }
1268  
1269              if ( ! empty( $cat_not_in ) ) {
1270                  $tax_query[] = array(
1271                      'taxonomy'         => 'category',
1272                      'terms'            => $cat_not_in,
1273                      'field'            => 'term_id',
1274                      'operator'         => 'NOT IN',
1275                      'include_children' => true,
1276                  );
1277              }
1278              unset( $cat_array, $cat_in, $cat_not_in );
1279          }
1280  
1281          if ( ! empty( $query_vars['category__and'] ) && 1 === count( (array) $query_vars['category__and'] ) ) {
1282              $query_vars['category__and'] = (array) $query_vars['category__and'];
1283              if ( ! isset( $query_vars['category__in'] ) ) {
1284                  $query_vars['category__in'] = array();
1285              }
1286              $query_vars['category__in'][] = absint( reset( $query_vars['category__and'] ) );
1287              unset( $query_vars['category__and'] );
1288          }
1289  
1290          if ( ! empty( $query_vars['category__in'] ) ) {
1291              $query_vars['category__in'] = array_map( 'absint', array_unique( (array) $query_vars['category__in'] ) );
1292              sort( $query_vars['category__in'] );
1293              $tax_query[] = array(
1294                  'taxonomy'         => 'category',
1295                  'terms'            => $query_vars['category__in'],
1296                  'field'            => 'term_id',
1297                  'include_children' => false,
1298              );
1299          }
1300  
1301          if ( ! empty( $query_vars['category__not_in'] ) ) {
1302              $query_vars['category__not_in'] = array_map( 'absint', array_unique( (array) $query_vars['category__not_in'] ) );
1303              sort( $query_vars['category__not_in'] );
1304              $tax_query[] = array(
1305                  'taxonomy'         => 'category',
1306                  'terms'            => $query_vars['category__not_in'],
1307                  'operator'         => 'NOT IN',
1308                  'include_children' => false,
1309              );
1310          }
1311  
1312          if ( ! empty( $query_vars['category__and'] ) ) {
1313              $query_vars['category__and'] = array_map( 'absint', array_unique( (array) $query_vars['category__and'] ) );
1314              sort( $query_vars['category__and'] );
1315              $tax_query[] = array(
1316                  'taxonomy'         => 'category',
1317                  'terms'            => $query_vars['category__and'],
1318                  'field'            => 'term_id',
1319                  'operator'         => 'AND',
1320                  'include_children' => false,
1321              );
1322          }
1323  
1324          // If query string 'tag' is array, implode it.
1325          if ( is_array( $query_vars['tag'] ) ) {
1326              $query_vars['tag'] = implode( ',', $query_vars['tag'] );
1327          }
1328  
1329          // Tag stuff.
1330  
1331          if ( '' !== $query_vars['tag'] && ! $this->is_singular && $this->query_vars_changed ) {
1332              if ( str_contains( $query_vars['tag'], ',' ) ) {
1333                  // @todo Handle normalizing `tag` query string.
1334                  $tags = preg_split( '/[,\r\n\t ]+/', $query_vars['tag'] );
1335                  foreach ( (array) $tags as $tag ) {
1336                      $tag                          = sanitize_term_field( 'slug', $tag, 0, 'post_tag', 'db' );
1337                      $query_vars['tag_slug__in'][] = $tag;
1338                      sort( $query_vars['tag_slug__in'] );
1339                  }
1340              } elseif ( preg_match( '/[+\r\n\t ]+/', $query_vars['tag'] ) || ! empty( $query_vars['cat'] ) ) {
1341                  $tags = preg_split( '/[+\r\n\t ]+/', $query_vars['tag'] );
1342                  foreach ( (array) $tags as $tag ) {
1343                      $tag                           = sanitize_term_field( 'slug', $tag, 0, 'post_tag', 'db' );
1344                      $query_vars['tag_slug__and'][] = $tag;
1345                  }
1346              } else {
1347                  $query_vars['tag']            = sanitize_term_field( 'slug', $query_vars['tag'], 0, 'post_tag', 'db' );
1348                  $query_vars['tag_slug__in'][] = $query_vars['tag'];
1349                  sort( $query_vars['tag_slug__in'] );
1350              }
1351          }
1352  
1353          if ( ! empty( $query_vars['tag_id'] ) ) {
1354              $query_vars['tag_id'] = absint( $query_vars['tag_id'] );
1355              $tax_query[]          = array(
1356                  'taxonomy' => 'post_tag',
1357                  'terms'    => $query_vars['tag_id'],
1358              );
1359          }
1360  
1361          if ( ! empty( $query_vars['tag__in'] ) ) {
1362              $query_vars['tag__in'] = array_map( 'absint', array_unique( (array) $query_vars['tag__in'] ) );
1363              sort( $query_vars['tag__in'] );
1364              $tax_query[] = array(
1365                  'taxonomy' => 'post_tag',
1366                  'terms'    => $query_vars['tag__in'],
1367              );
1368          }
1369  
1370          if ( ! empty( $query_vars['tag__not_in'] ) ) {
1371              $query_vars['tag__not_in'] = array_map( 'absint', array_unique( (array) $query_vars['tag__not_in'] ) );
1372              sort( $query_vars['tag__not_in'] );
1373              $tax_query[] = array(
1374                  'taxonomy' => 'post_tag',
1375                  'terms'    => $query_vars['tag__not_in'],
1376                  'operator' => 'NOT IN',
1377              );
1378          }
1379  
1380          if ( ! empty( $query_vars['tag__and'] ) ) {
1381              $query_vars['tag__and'] = array_map( 'absint', array_unique( (array) $query_vars['tag__and'] ) );
1382              sort( $query_vars['tag__and'] );
1383              $tax_query[] = array(
1384                  'taxonomy' => 'post_tag',
1385                  'terms'    => $query_vars['tag__and'],
1386                  'operator' => 'AND',
1387              );
1388          }
1389  
1390          if ( ! empty( $query_vars['tag_slug__in'] ) ) {
1391              $query_vars['tag_slug__in'] = array_map( 'sanitize_title_for_query', array_unique( (array) $query_vars['tag_slug__in'] ) );
1392              sort( $query_vars['tag_slug__in'] );
1393              $tax_query[] = array(
1394                  'taxonomy' => 'post_tag',
1395                  'terms'    => $query_vars['tag_slug__in'],
1396                  'field'    => 'slug',
1397              );
1398          }
1399  
1400          if ( ! empty( $query_vars['tag_slug__and'] ) ) {
1401              $query_vars['tag_slug__and'] = array_map( 'sanitize_title_for_query', array_unique( (array) $query_vars['tag_slug__and'] ) );
1402              sort( $query_vars['tag_slug__and'] );
1403              $tax_query[] = array(
1404                  'taxonomy' => 'post_tag',
1405                  'terms'    => $query_vars['tag_slug__and'],
1406                  'field'    => 'slug',
1407                  'operator' => 'AND',
1408              );
1409          }
1410  
1411          $this->tax_query = new WP_Tax_Query( $tax_query );
1412  
1413          /**
1414           * Fires after taxonomy-related query vars have been parsed.
1415           *
1416           * @since 3.7.0
1417           *
1418           * @param WP_Query $query The WP_Query instance.
1419           */
1420          do_action( 'parse_tax_query', $this );
1421      }
1422  
1423      /**
1424       * Generates SQL for the WHERE clause based on passed search terms.
1425       *
1426       * @since 3.7.0
1427       *
1428       * @global wpdb $wpdb WordPress database abstraction object.
1429       *
1430       * @param array $query_vars Query variables.
1431       * @return string WHERE clause.
1432       */
1433  	protected function parse_search( &$query_vars ) {
1434          global $wpdb;
1435  
1436          $search = '';
1437  
1438          // Added slashes screw with quote grouping when done early, so done later.
1439          $query_vars['s'] = stripslashes( $query_vars['s'] );
1440          if ( empty( $_GET['s'] ) && $this->is_main_query() ) {
1441              $query_vars['s'] = urldecode( $query_vars['s'] );
1442          }
1443          // There are no line breaks in <input /> fields.
1444          $query_vars['s']                  = str_replace( array( "\r", "\n" ), '', $query_vars['s'] );
1445          $query_vars['search_terms_count'] = 1;
1446          if ( ! empty( $query_vars['sentence'] ) ) {
1447              $query_vars['search_terms'] = array( $query_vars['s'] );
1448          } else {
1449              if ( preg_match_all( '/".*?("|$)|((?<=[\t ",+])|^)[^\t ",+]+/', $query_vars['s'], $matches ) ) {
1450                  $query_vars['search_terms_count'] = count( $matches[0] );
1451                  $query_vars['search_terms']       = $this->parse_search_terms( $matches[0] );
1452                  // If the search string has only short terms or stopwords, or is 10+ terms long, match it as sentence.
1453                  if ( empty( $query_vars['search_terms'] ) || count( $query_vars['search_terms'] ) > 9 ) {
1454                      $query_vars['search_terms'] = array( $query_vars['s'] );
1455                  }
1456              } else {
1457                  $query_vars['search_terms'] = array( $query_vars['s'] );
1458              }
1459          }
1460  
1461          $n                                  = ! empty( $query_vars['exact'] ) ? '' : '%';
1462          $searchand                          = '';
1463          $query_vars['search_orderby_title'] = array();
1464  
1465          $default_search_columns = array( 'post_title', 'post_excerpt', 'post_content' );
1466          $search_columns         = ! empty( $query_vars['search_columns'] ) ? $query_vars['search_columns'] : $default_search_columns;
1467          if ( ! is_array( $search_columns ) ) {
1468              $search_columns = array( $search_columns );
1469          }
1470  
1471          /**
1472           * Filters the columns to search in a WP_Query search.
1473           *
1474           * The supported columns are `post_title`, `post_excerpt` and `post_content`.
1475           * They are all included by default.
1476           *
1477           * @since 6.2.0
1478           *
1479           * @param string[] $search_columns Array of column names to be searched.
1480           * @param string   $search         Text being searched.
1481           * @param WP_Query $query          The current WP_Query instance.
1482           */
1483          $search_columns = (array) apply_filters( 'post_search_columns', $search_columns, $query_vars['s'], $this );
1484  
1485          // Use only supported search columns.
1486          $search_columns = array_intersect( $search_columns, $default_search_columns );
1487          if ( empty( $search_columns ) ) {
1488              $search_columns = $default_search_columns;
1489          }
1490  
1491          /**
1492           * Filters the prefix that indicates that a search term should be excluded from results.
1493           *
1494           * @since 4.7.0
1495           *
1496           * @param string $exclusion_prefix The prefix. Default '-'. Returning
1497           *                                 an empty value disables exclusions.
1498           */
1499          $exclusion_prefix = apply_filters( 'wp_query_search_exclusion_prefix', '-' );
1500  
1501          foreach ( $query_vars['search_terms'] as $term ) {
1502              // If there is an $exclusion_prefix, terms prefixed with it should be excluded.
1503              $exclude = $exclusion_prefix && str_starts_with( $term, $exclusion_prefix );
1504              if ( $exclude ) {
1505                  $like_op  = 'NOT LIKE';
1506                  $andor_op = 'AND';
1507                  $term     = substr( $term, 1 );
1508              } else {
1509                  $like_op  = 'LIKE';
1510                  $andor_op = 'OR';
1511              }
1512  
1513              if ( $n && ! $exclude ) {
1514                  $like                                 = '%' . $wpdb->esc_like( $term ) . '%';
1515                  $query_vars['search_orderby_title'][] = $wpdb->prepare( "{$wpdb->posts}.post_title LIKE %s", $like );
1516              }
1517  
1518              $like = $n . $wpdb->esc_like( $term ) . $n;
1519  
1520              $search_columns_parts = array();
1521              foreach ( $search_columns as $search_column ) {
1522                  $search_columns_parts[ $search_column ] = $wpdb->prepare( "({$wpdb->posts}.$search_column $like_op %s)", $like );
1523              }
1524  
1525              if ( ! empty( $this->allow_query_attachment_by_filename ) ) {
1526                  $search_columns_parts['attachment'] = $wpdb->prepare( "(sq1.meta_value $like_op %s)", $like );
1527              }
1528  
1529              $search .= "$searchand(" . implode( " $andor_op ", $search_columns_parts ) . ')';
1530  
1531              $searchand = ' AND ';
1532          }
1533  
1534          if ( ! empty( $search ) ) {
1535              $search = " AND ({$search}) ";
1536              if ( ! is_user_logged_in() ) {
1537                  $search .= " AND ({$wpdb->posts}.post_password = '') ";
1538              }
1539          }
1540  
1541          return $search;
1542      }
1543  
1544      /**
1545       * Checks if the terms are suitable for searching.
1546       *
1547       * Uses an array of stopwords (terms) that are excluded from the separate
1548       * term matching when searching for posts. The list of English stopwords is
1549       * the approximate search engines list, and is translatable.
1550       *
1551       * @since 3.7.0
1552       *
1553       * @param string[] $terms Array of terms to check.
1554       * @return string[] Terms that are not stopwords.
1555       */
1556  	protected function parse_search_terms( $terms ) {
1557          $strtolower = function_exists( 'mb_strtolower' ) ? 'mb_strtolower' : 'strtolower';
1558          $checked    = array();
1559  
1560          $stopwords = $this->get_search_stopwords();
1561  
1562          foreach ( $terms as $term ) {
1563              // Keep before/after spaces when term is for exact match.
1564              if ( preg_match( '/^".+"$/', $term ) ) {
1565                  $term = trim( $term, "\"'" );
1566              } else {
1567                  $term = trim( $term, "\"' " );
1568              }
1569  
1570              // Avoid single A-Z and single dashes.
1571              if ( ! $term || ( 1 === strlen( $term ) && preg_match( '/^[a-z\-]$/i', $term ) ) ) {
1572                  continue;
1573              }
1574  
1575              if ( in_array( call_user_func( $strtolower, $term ), $stopwords, true ) ) {
1576                  continue;
1577              }
1578  
1579              $checked[] = $term;
1580          }
1581  
1582          return $checked;
1583      }
1584  
1585      /**
1586       * Retrieves stopwords used when parsing search terms.
1587       *
1588       * @since 3.7.0
1589       *
1590       * @return string[] Stopwords.
1591       */
1592  	protected function get_search_stopwords() {
1593          if ( isset( $this->stopwords ) ) {
1594              return $this->stopwords;
1595          }
1596  
1597          /*
1598           * translators: This is a comma-separated list of very common words that should be excluded from a search,
1599           * like a, an, and the. These are usually called "stopwords". You should not simply translate these individual
1600           * words into your language. Instead, look for and provide commonly accepted stopwords in your language.
1601           */
1602          $words = explode(
1603              ',',
1604              _x(
1605                  'about,an,are,as,at,be,by,com,for,from,how,in,is,it,of,on,or,that,the,this,to,was,what,when,where,who,will,with,www',
1606                  'Comma-separated list of search stopwords in your language'
1607              )
1608          );
1609  
1610          $stopwords = array();
1611          foreach ( $words as $word ) {
1612              $word = trim( $word, "\r\n\t " );
1613              if ( $word ) {
1614                  $stopwords[] = $word;
1615              }
1616          }
1617  
1618          /**
1619           * Filters stopwords used when parsing search terms.
1620           *
1621           * @since 3.7.0
1622           *
1623           * @param string[] $stopwords Array of stopwords.
1624           */
1625          $this->stopwords = apply_filters( 'wp_search_stopwords', $stopwords );
1626          return $this->stopwords;
1627      }
1628  
1629      /**
1630       * Generates SQL for the ORDER BY condition based on passed search terms.
1631       *
1632       * @since 3.7.0
1633       *
1634       * @global wpdb $wpdb WordPress database abstraction object.
1635       *
1636       * @param array $query_vars Query variables.
1637       * @return string ORDER BY clause.
1638       */
1639  	protected function parse_search_order( &$query_vars ) {
1640          global $wpdb;
1641  
1642          if ( $query_vars['search_terms_count'] > 1 ) {
1643              $num_terms = count( $query_vars['search_orderby_title'] );
1644  
1645              // If the search terms contain negative queries, don't bother ordering by sentence matches.
1646              $like = '';
1647              if ( ! preg_match( '/(?:\s|^)\-/', $query_vars['s'] ) ) {
1648                  $like = '%' . $wpdb->esc_like( $query_vars['s'] ) . '%';
1649              }
1650  
1651              $search_orderby = '';
1652  
1653              // Sentence match in 'post_title'.
1654              if ( $like ) {
1655                  $search_orderby .= $wpdb->prepare( "WHEN {$wpdb->posts}.post_title LIKE %s THEN 1 ", $like );
1656              }
1657  
1658              /*
1659               * Sanity limit, sort as sentence when more than 6 terms
1660               * (few searches are longer than 6 terms and most titles are not).
1661               */
1662              if ( $num_terms < 7 ) {
1663                  // All words in title.
1664                  $search_orderby .= 'WHEN ' . implode( ' AND ', $query_vars['search_orderby_title'] ) . ' THEN 2 ';
1665                  // Any word in title, not needed when $num_terms == 1.
1666                  if ( $num_terms > 1 ) {
1667                      $search_orderby .= 'WHEN ' . implode( ' OR ', $query_vars['search_orderby_title'] ) . ' THEN 3 ';
1668                  }
1669              }
1670  
1671              // Sentence match in 'post_content' and 'post_excerpt'.
1672              if ( $like ) {
1673                  $search_orderby .= $wpdb->prepare( "WHEN {$wpdb->posts}.post_excerpt LIKE %s THEN 4 ", $like );
1674                  $search_orderby .= $wpdb->prepare( "WHEN {$wpdb->posts}.post_content LIKE %s THEN 5 ", $like );
1675              }
1676  
1677              if ( $search_orderby ) {
1678                  $search_orderby = '(CASE ' . $search_orderby . 'ELSE 6 END)';
1679              }
1680          } else {
1681              // Single word or sentence search.
1682              $search_orderby = reset( $query_vars['search_orderby_title'] ) . ' DESC';
1683          }
1684  
1685          return $search_orderby;
1686      }
1687  
1688      /**
1689       * Converts the given orderby alias (if allowed) to a properly-prefixed value.
1690       *
1691       * @since 4.0.0
1692       *
1693       * @global wpdb $wpdb WordPress database abstraction object.
1694       *
1695       * @param string $orderby Alias for the field to order by.
1696       * @return string|false Table-prefixed value to used in the ORDER clause. False otherwise.
1697       */
1698  	protected function parse_orderby( $orderby ) {
1699          global $wpdb;
1700  
1701          // Used to filter values.
1702          $allowed_keys = array(
1703              'post_name',
1704              'post_author',
1705              'post_date',
1706              'post_title',
1707              'post_modified',
1708              'post_parent',
1709              'post_type',
1710              'name',
1711              'author',
1712              'date',
1713              'title',
1714              'modified',
1715              'parent',
1716              'type',
1717              'ID',
1718              'menu_order',
1719              'comment_count',
1720              'rand',
1721              'post__in',
1722              'post_parent__in',
1723              'post_name__in',
1724          );
1725  
1726          $primary_meta_key   = '';
1727          $primary_meta_query = false;
1728          $meta_clauses       = $this->meta_query->get_clauses();
1729          if ( ! empty( $meta_clauses ) ) {
1730              $primary_meta_query = reset( $meta_clauses );
1731  
1732              if ( ! empty( $primary_meta_query['key'] ) ) {
1733                  $primary_meta_key = $primary_meta_query['key'];
1734                  $allowed_keys[]   = $primary_meta_key;
1735              }
1736  
1737              $allowed_keys[] = 'meta_value';
1738              $allowed_keys[] = 'meta_value_num';
1739              $allowed_keys   = array_merge( $allowed_keys, array_keys( $meta_clauses ) );
1740          }
1741  
1742          // If RAND() contains a seed value, sanitize and add to allowed keys.
1743          $rand_with_seed = false;
1744          if ( preg_match( '/RAND\(([0-9]+)\)/i', $orderby, $matches ) ) {
1745              $orderby        = sprintf( 'RAND(%s)', (int) $matches[1] );
1746              $allowed_keys[] = $orderby;
1747              $rand_with_seed = true;
1748          }
1749  
1750          if ( ! in_array( $orderby, $allowed_keys, true ) ) {
1751              return false;
1752          }
1753  
1754          $orderby_clause = '';
1755  
1756          switch ( $orderby ) {
1757              case 'post_name':
1758              case 'post_author':
1759              case 'post_date':
1760              case 'post_title':
1761              case 'post_modified':
1762              case 'post_parent':
1763              case 'post_type':
1764              case 'ID':
1765              case 'menu_order':
1766              case 'comment_count':
1767                  $orderby_clause = "{$wpdb->posts}.{$orderby}";
1768                  break;
1769              case 'rand':
1770                  $orderby_clause = 'RAND()';
1771                  break;
1772              case $primary_meta_key:
1773              case 'meta_value':
1774                  if ( ! empty( $primary_meta_query['type'] ) ) {
1775                      $orderby_clause = "CAST({$primary_meta_query['alias']}.meta_value AS {$primary_meta_query['cast']})";
1776                  } else {
1777                      $orderby_clause = "{$primary_meta_query['alias']}.meta_value";
1778                  }
1779                  break;
1780              case 'meta_value_num':
1781                  $orderby_clause = "{$primary_meta_query['alias']}.meta_value+0";
1782                  break;
1783              case 'post__in':
1784                  if ( ! empty( $this->query_vars['post__in'] ) ) {
1785                      $orderby_clause = "FIELD({$wpdb->posts}.ID," . implode( ',', array_map( 'absint', $this->query_vars['post__in'] ) ) . ')';
1786                  }
1787                  break;
1788              case 'post_parent__in':
1789                  if ( ! empty( $this->query_vars['post_parent__in'] ) ) {
1790                      $orderby_clause = "FIELD( {$wpdb->posts}.post_parent," . implode( ', ', array_map( 'absint', $this->query_vars['post_parent__in'] ) ) . ' )';
1791                  }
1792                  break;
1793              case 'post_name__in':
1794                  if ( ! empty( $this->query_vars['post_name__in'] ) ) {
1795                      $post_name__in        = array_map( 'sanitize_title_for_query', $this->query_vars['post_name__in'] );
1796                      $post_name__in_string = "'" . implode( "','", $post_name__in ) . "'";
1797                      $orderby_clause       = "FIELD( {$wpdb->posts}.post_name," . $post_name__in_string . ' )';
1798                  }
1799                  break;
1800              default:
1801                  if ( array_key_exists( $orderby, $meta_clauses ) ) {
1802                      // $orderby corresponds to a meta_query clause.
1803                      $meta_clause    = $meta_clauses[ $orderby ];
1804                      $orderby_clause = "CAST({$meta_clause['alias']}.meta_value AS {$meta_clause['cast']})";
1805                  } elseif ( $rand_with_seed ) {
1806                      $orderby_clause = $orderby;
1807                  } else {
1808                      // Default: order by post field.
1809                      $orderby_clause = "{$wpdb->posts}.post_" . sanitize_key( $orderby );
1810                  }
1811  
1812                  break;
1813          }
1814  
1815          return $orderby_clause;
1816      }
1817  
1818      /**
1819       * Parse an 'order' query variable and cast it to ASC or DESC as necessary.
1820       *
1821       * @since 4.0.0
1822       *
1823       * @param string $order The 'order' query variable.
1824       * @return string The sanitized 'order' query variable.
1825       */
1826  	protected function parse_order( $order ) {
1827          if ( ! is_string( $order ) || empty( $order ) ) {
1828              return 'DESC';
1829          }
1830  
1831          if ( 'ASC' === strtoupper( $order ) ) {
1832              return 'ASC';
1833          } else {
1834              return 'DESC';
1835          }
1836      }
1837  
1838      /**
1839       * Sets the 404 property and saves whether query is feed.
1840       *
1841       * @since 2.0.0
1842       */
1843  	public function set_404() {
1844          $is_feed = $this->is_feed;
1845  
1846          $this->init_query_flags();
1847          $this->is_404 = true;
1848  
1849          $this->is_feed = $is_feed;
1850  
1851          /**
1852           * Fires after a 404 is triggered.
1853           *
1854           * @since 5.5.0
1855           *
1856           * @param WP_Query $query The WP_Query instance (passed by reference).
1857           */
1858          do_action_ref_array( 'set_404', array( $this ) );
1859      }
1860  
1861      /**
1862       * Retrieves the value of a query variable.
1863       *
1864       * @since 1.5.0
1865       * @since 3.9.0 The `$default_value` argument was introduced.
1866       *
1867       * @param string $query_var     Query variable key.
1868       * @param mixed  $default_value Optional. Value to return if the query variable is not set.
1869       *                              Default empty string.
1870       * @return mixed Contents of the query variable.
1871       */
1872  	public function get( $query_var, $default_value = '' ) {
1873          return $this->query_vars[ $query_var ] ?? $default_value;
1874      }
1875  
1876      /**
1877       * Sets the value of a query variable.
1878       *
1879       * @since 1.5.0
1880       *
1881       * @param string $query_var Query variable key.
1882       * @param mixed  $value     Query variable value.
1883       */
1884  	public function set( $query_var, $value ) {
1885          $this->query_vars[ $query_var ] = $value;
1886      }
1887  
1888      /**
1889       * Retrieves an array of posts based on query variables.
1890       *
1891       * There are a few filters and actions that can be used to modify the post
1892       * database query.
1893       *
1894       * @since 1.5.0
1895       *
1896       * @global wpdb $wpdb WordPress database abstraction object.
1897       *
1898       * @return WP_Post[]|int[] Array of post objects or post IDs.
1899       */
1900  	public function get_posts() {
1901          global $wpdb;
1902  
1903          $this->parse_query();
1904  
1905          /**
1906           * Fires after the query variable object is created, but before the actual query is run.
1907           *
1908           * Note: If using conditional tags, use the method versions within the passed instance
1909           * (e.g. `$query->is_main_query()` instead of `is_main_query()`). This is because the functions
1910           * like `is_main_query()` test against the global `$wp_query` instance, not the passed one.
1911           *
1912           * @since 2.0.0
1913           *
1914           * @param WP_Query $query The WP_Query instance (passed by reference).
1915           */
1916          do_action_ref_array( 'pre_get_posts', array( &$this ) );
1917  
1918          // Locally scoped reference for easy of use.
1919          $query_vars = &$this->query_vars;
1920  
1921          // Fill again in case 'pre_get_posts' unset some vars.
1922          $query_vars = $this->fill_query_vars( $query_vars );
1923  
1924          /**
1925           * Filters whether an attachment query should include filenames or not.
1926           *
1927           * @since 6.0.3
1928           *
1929           * @param bool $allow_query_attachment_by_filename Whether or not to include filenames.
1930           */
1931          $this->allow_query_attachment_by_filename = apply_filters( 'wp_allow_query_attachment_by_filename', false );
1932          remove_all_filters( 'wp_allow_query_attachment_by_filename' );
1933  
1934          // Parse meta query.
1935          $this->meta_query = new WP_Meta_Query();
1936          $this->meta_query->parse_query_vars( $query_vars );
1937  
1938          // Set a flag if a 'pre_get_posts' hook changed the query vars.
1939          $hash = md5( serialize( $this->query_vars ) );
1940          if ( $hash !== $this->query_vars_hash ) {
1941              $this->query_vars_changed = true;
1942              $this->query_vars_hash    = $hash;
1943          }
1944          unset( $hash );
1945  
1946          // First let's clear some variables.
1947          $distinct         = '';
1948          $whichauthor      = '';
1949          $whichmimetype    = '';
1950          $where            = '';
1951          $limits           = '';
1952          $join             = '';
1953          $search           = '';
1954          $groupby          = '';
1955          $post_status_join = false;
1956          $page             = 1;
1957  
1958          if ( isset( $query_vars['caller_get_posts'] ) ) {
1959              _deprecated_argument(
1960                  'WP_Query',
1961                  '3.1.0',
1962                  sprintf(
1963                      /* translators: 1: caller_get_posts, 2: ignore_sticky_posts */
1964                      __( '%1$s is deprecated. Use %2$s instead.' ),
1965                      '<code>caller_get_posts</code>',
1966                      '<code>ignore_sticky_posts</code>'
1967                  )
1968              );
1969  
1970              if ( ! isset( $query_vars['ignore_sticky_posts'] ) ) {
1971                  $query_vars['ignore_sticky_posts'] = $query_vars['caller_get_posts'];
1972              }
1973          }
1974  
1975          if ( ! isset( $query_vars['ignore_sticky_posts'] ) ) {
1976              $query_vars['ignore_sticky_posts'] = false;
1977          }
1978  
1979          if ( ! isset( $query_vars['suppress_filters'] ) ) {
1980              $query_vars['suppress_filters'] = false;
1981          }
1982  
1983          if ( ! isset( $query_vars['cache_results'] ) ) {
1984              $query_vars['cache_results'] = true;
1985          }
1986  
1987          if ( ! isset( $query_vars['update_post_term_cache'] ) ) {
1988              $query_vars['update_post_term_cache'] = true;
1989          }
1990  
1991          if ( ! isset( $query_vars['update_menu_item_cache'] ) ) {
1992              $query_vars['update_menu_item_cache'] = false;
1993          }
1994  
1995          if ( ! isset( $query_vars['lazy_load_term_meta'] ) ) {
1996              $query_vars['lazy_load_term_meta'] = $query_vars['update_post_term_cache'];
1997          } elseif ( $query_vars['lazy_load_term_meta'] ) { // Lazy loading term meta only works if term caches are primed.
1998              $query_vars['update_post_term_cache'] = true;
1999          }
2000  
2001          if ( ! isset( $query_vars['update_post_meta_cache'] ) ) {
2002              $query_vars['update_post_meta_cache'] = true;
2003          }
2004  
2005          if ( ! isset( $query_vars['post_type'] ) ) {
2006              if ( $this->is_search ) {
2007                  $query_vars['post_type'] = 'any';
2008              } else {
2009                  $query_vars['post_type'] = '';
2010              }
2011          }
2012          $post_type = $query_vars['post_type'];
2013          if ( empty( $query_vars['posts_per_page'] ) ) {
2014              $query_vars['posts_per_page'] = get_option( 'posts_per_page' );
2015          }
2016          if ( isset( $query_vars['showposts'] ) && $query_vars['showposts'] ) {
2017              $query_vars['showposts']      = (int) $query_vars['showposts'];
2018              $query_vars['posts_per_page'] = $query_vars['showposts'];
2019          }
2020          if ( ( isset( $query_vars['posts_per_archive_page'] ) && 0 != $query_vars['posts_per_archive_page'] ) && ( $this->is_archive || $this->is_search ) ) {
2021              $query_vars['posts_per_page'] = $query_vars['posts_per_archive_page'];
2022          }
2023          if ( ! isset( $query_vars['nopaging'] ) ) {
2024              if ( -1 == $query_vars['posts_per_page'] ) {
2025                  $query_vars['nopaging'] = true;
2026              } else {
2027                  $query_vars['nopaging'] = false;
2028              }
2029          }
2030  
2031          if ( $this->is_feed ) {
2032              // This overrides 'posts_per_page'.
2033              if ( ! empty( $query_vars['posts_per_rss'] ) ) {
2034                  $query_vars['posts_per_page'] = $query_vars['posts_per_rss'];
2035              } else {
2036                  $query_vars['posts_per_page'] = get_option( 'posts_per_rss' );
2037              }
2038              $query_vars['nopaging'] = false;
2039          }
2040  
2041          $query_vars['posts_per_page'] = (int) $query_vars['posts_per_page'];
2042          if ( $query_vars['posts_per_page'] < -1 ) {
2043              $query_vars['posts_per_page'] = abs( $query_vars['posts_per_page'] );
2044          } elseif ( 0 === $query_vars['posts_per_page'] ) {
2045              $query_vars['posts_per_page'] = 1;
2046          }
2047  
2048          if ( ! isset( $query_vars['comments_per_page'] ) || 0 == $query_vars['comments_per_page'] ) {
2049              $query_vars['comments_per_page'] = get_option( 'comments_per_page' );
2050          }
2051  
2052          if ( $this->is_home && ( empty( $this->query ) || 'true' === $query_vars['preview'] ) && ( 'page' === get_option( 'show_on_front' ) ) && get_option( 'page_on_front' ) ) {
2053              $this->is_page         = true;
2054              $this->is_home         = false;
2055              $query_vars['page_id'] = get_option( 'page_on_front' );
2056          }
2057  
2058          if ( isset( $query_vars['page'] ) ) {
2059              $query_vars['page'] = is_scalar( $query_vars['page'] ) ? absint( trim( $query_vars['page'], '/' ) ) : 0;
2060          }
2061  
2062          // If true, forcibly turns off SQL_CALC_FOUND_ROWS even when limits are present.
2063          if ( isset( $query_vars['no_found_rows'] ) ) {
2064              $query_vars['no_found_rows'] = (bool) $query_vars['no_found_rows'];
2065          } else {
2066              $query_vars['no_found_rows'] = false;
2067          }
2068  
2069          switch ( $query_vars['fields'] ) {
2070              case 'ids':
2071                  $fields = "{$wpdb->posts}.ID";
2072                  break;
2073              case 'id=>parent':
2074                  $fields = "{$wpdb->posts}.ID, {$wpdb->posts}.post_parent";
2075                  break;
2076              case '':
2077                  /*
2078                   * Set the default to 'all'.
2079                   *
2080                   * This is used in `WP_Query::the_post` to determine if the
2081                   * entire post object has been queried.
2082                   */
2083                  $query_vars['fields'] = 'all';
2084                  // Falls through.
2085              default:
2086                  $fields = "{$wpdb->posts}.*";
2087          }
2088  
2089          if ( '' !== $query_vars['menu_order'] ) {
2090              $where .= " AND {$wpdb->posts}.menu_order = " . $query_vars['menu_order'];
2091          }
2092          // The "m" parameter is meant for months but accepts datetimes of varying specificity.
2093          if ( $query_vars['m'] ) {
2094              $where .= " AND YEAR({$wpdb->posts}.post_date)=" . substr( $query_vars['m'], 0, 4 );
2095              if ( strlen( $query_vars['m'] ) > 5 ) {
2096                  $where .= " AND MONTH({$wpdb->posts}.post_date)=" . substr( $query_vars['m'], 4, 2 );
2097              }
2098              if ( strlen( $query_vars['m'] ) > 7 ) {
2099                  $where .= " AND DAYOFMONTH({$wpdb->posts}.post_date)=" . substr( $query_vars['m'], 6, 2 );
2100              }
2101              if ( strlen( $query_vars['m'] ) > 9 ) {
2102                  $where .= " AND HOUR({$wpdb->posts}.post_date)=" . substr( $query_vars['m'], 8, 2 );
2103              }
2104              if ( strlen( $query_vars['m'] ) > 11 ) {
2105                  $where .= " AND MINUTE({$wpdb->posts}.post_date)=" . substr( $query_vars['m'], 10, 2 );
2106              }
2107              if ( strlen( $query_vars['m'] ) > 13 ) {
2108                  $where .= " AND SECOND({$wpdb->posts}.post_date)=" . substr( $query_vars['m'], 12, 2 );
2109              }
2110          }
2111  
2112          // Handle the other individual date parameters.
2113          $date_parameters = array();
2114  
2115          if ( '' !== $query_vars['hour'] ) {
2116              $date_parameters['hour'] = $query_vars['hour'];
2117          }
2118  
2119          if ( '' !== $query_vars['minute'] ) {
2120              $date_parameters['minute'] = $query_vars['minute'];
2121          }
2122  
2123          if ( '' !== $query_vars['second'] ) {
2124              $date_parameters['second'] = $query_vars['second'];
2125          }
2126  
2127          if ( $query_vars['year'] ) {
2128              $date_parameters['year'] = $query_vars['year'];
2129          }
2130  
2131          if ( $query_vars['monthnum'] ) {
2132              $date_parameters['monthnum'] = $query_vars['monthnum'];
2133          }
2134  
2135          if ( $query_vars['w'] ) {
2136              $date_parameters['week'] = $query_vars['w'];
2137          }
2138  
2139          if ( $query_vars['day'] ) {
2140              $date_parameters['day'] = $query_vars['day'];
2141          }
2142  
2143          if ( $date_parameters ) {
2144              $date_query = new WP_Date_Query( array( $date_parameters ) );
2145              $where     .= $date_query->get_sql();
2146          }
2147          unset( $date_parameters, $date_query );
2148  
2149          // Handle complex date queries.
2150          if ( ! empty( $query_vars['date_query'] ) ) {
2151              $this->date_query = new WP_Date_Query( $query_vars['date_query'] );
2152              $where           .= $this->date_query->get_sql();
2153          }
2154  
2155          // If we've got a post_type AND it's not "any" post_type.
2156          if ( ! empty( $query_vars['post_type'] ) && 'any' !== $query_vars['post_type'] ) {
2157              foreach ( (array) $query_vars['post_type'] as $_post_type ) {
2158                  $ptype_obj = get_post_type_object( $_post_type );
2159                  if ( ! $ptype_obj || ! $ptype_obj->query_var || empty( $query_vars[ $ptype_obj->query_var ] ) ) {
2160                      continue;
2161                  }
2162  
2163                  if ( ! $ptype_obj->hierarchical ) {
2164                      // Non-hierarchical post types can directly use 'name'.
2165                      $query_vars['name'] = $query_vars[ $ptype_obj->query_var ];
2166                  } else {
2167                      // Hierarchical post types will operate through 'pagename'.
2168                      $query_vars['pagename'] = $query_vars[ $ptype_obj->query_var ];
2169                      $query_vars['name']     = '';
2170                  }
2171  
2172                  // Only one request for a slug is possible, this is why name & pagename are overwritten above.
2173                  break;
2174              } // End foreach.
2175              unset( $ptype_obj );
2176          }
2177  
2178          if ( '' !== $query_vars['title'] ) {
2179              $where .= $wpdb->prepare( " AND {$wpdb->posts}.post_title = %s", stripslashes( $query_vars['title'] ) );
2180          }
2181  
2182          // Parameters related to 'post_name'.
2183          if ( '' !== $query_vars['name'] ) {
2184              $query_vars['name'] = sanitize_title_for_query( $query_vars['name'] );
2185              $where             .= " AND {$wpdb->posts}.post_name = '" . $query_vars['name'] . "'";
2186          } elseif ( '' !== $query_vars['pagename'] ) {
2187              if ( isset( $this->queried_object_id ) ) {
2188                  $reqpage = $this->queried_object_id;
2189              } else {
2190                  if ( 'page' !== $query_vars['post_type'] ) {
2191                      foreach ( (array) $query_vars['post_type'] as $_post_type ) {
2192                          $ptype_obj = get_post_type_object( $_post_type );
2193                          if ( ! $ptype_obj || ! $ptype_obj->hierarchical ) {
2194                              continue;
2195                          }
2196  
2197                          $reqpage = get_page_by_path( $query_vars['pagename'], OBJECT, $_post_type );
2198                          if ( $reqpage ) {
2199                              break;
2200                          }
2201                      }
2202                      unset( $ptype_obj );
2203                  } else {
2204                      $reqpage = get_page_by_path( $query_vars['pagename'] );
2205                  }
2206                  if ( ! empty( $reqpage ) ) {
2207                      $reqpage = $reqpage->ID;
2208                  } else {
2209                      $reqpage = 0;
2210                  }
2211              }
2212  
2213              $page_for_posts = get_option( 'page_for_posts' );
2214              if ( ( 'page' !== get_option( 'show_on_front' ) ) || empty( $page_for_posts ) || ( $reqpage != $page_for_posts ) ) {
2215                  $query_vars['pagename'] = sanitize_title_for_query( wp_basename( $query_vars['pagename'] ) );
2216                  $query_vars['name']     = $query_vars['pagename'];
2217                  $where                 .= " AND ({$wpdb->posts}.ID = '$reqpage')";
2218                  $reqpage_obj            = get_post( $reqpage );
2219                  if ( is_object( $reqpage_obj ) && 'attachment' === $reqpage_obj->post_type ) {
2220                      $this->is_attachment         = true;
2221                      $post_type                   = 'attachment';
2222                      $query_vars['post_type']     = 'attachment';
2223                      $this->is_page               = true;
2224                      $query_vars['attachment_id'] = $reqpage;
2225                  }
2226              }
2227          } elseif ( '' !== $query_vars['attachment'] ) {
2228              $query_vars['attachment'] = sanitize_title_for_query( wp_basename( $query_vars['attachment'] ) );
2229              $query_vars['name']       = $query_vars['attachment'];
2230              $where                   .= " AND {$wpdb->posts}.post_name = '" . $query_vars['attachment'] . "'";
2231          } elseif ( is_array( $query_vars['post_name__in'] ) && ! empty( $query_vars['post_name__in'] ) ) {
2232              $query_vars['post_name__in'] = array_map( 'sanitize_title_for_query', $query_vars['post_name__in'] );
2233              // Duplicate array before sorting to allow for the orderby clause.
2234              $post_name__in_for_where = array_unique( $query_vars['post_name__in'] );
2235              sort( $post_name__in_for_where );
2236              $post_name__in = "'" . implode( "','", $post_name__in_for_where ) . "'";
2237              $where        .= " AND {$wpdb->posts}.post_name IN ($post_name__in)";
2238          }
2239  
2240          // If an attachment is requested by number, let it supersede any post number.
2241          if ( $query_vars['attachment_id'] ) {
2242              $query_vars['p'] = absint( $query_vars['attachment_id'] );
2243          }
2244  
2245          // If a post number is specified, load that post.
2246          if ( $query_vars['p'] ) {
2247              $where .= " AND {$wpdb->posts}.ID = " . $query_vars['p'];
2248          } elseif ( $query_vars['post__in'] ) {
2249              // Duplicate array before sorting to allow for the orderby clause.
2250              $post__in_for_where = $query_vars['post__in'];
2251              $post__in_for_where = array_unique( array_map( 'absint', $post__in_for_where ) );
2252              sort( $post__in_for_where );
2253              $post__in = implode( ',', array_map( 'absint', $post__in_for_where ) );
2254              $where   .= " AND {$wpdb->posts}.ID IN ($post__in)";
2255          } elseif ( $query_vars['post__not_in'] ) {
2256              sort( $query_vars['post__not_in'] );
2257              $post__not_in = implode( ',', array_map( 'absint', $query_vars['post__not_in'] ) );
2258              $where       .= " AND {$wpdb->posts}.ID NOT IN ($post__not_in)";
2259          }
2260  
2261          if ( is_numeric( $query_vars['post_parent'] ) ) {
2262              $where .= $wpdb->prepare( " AND {$wpdb->posts}.post_parent = %d ", $query_vars['post_parent'] );
2263          } elseif ( $query_vars['post_parent__in'] ) {
2264              // Duplicate array before sorting to allow for the orderby clause.
2265              $post_parent__in_for_where = $query_vars['post_parent__in'];
2266              $post_parent__in_for_where = array_unique( array_map( 'absint', $post_parent__in_for_where ) );
2267              sort( $post_parent__in_for_where );
2268              $post_parent__in = implode( ',', array_map( 'absint', $post_parent__in_for_where ) );
2269              $where          .= " AND {$wpdb->posts}.post_parent IN ($post_parent__in)";
2270          } elseif ( $query_vars['post_parent__not_in'] ) {
2271              sort( $query_vars['post_parent__not_in'] );
2272              $post_parent__not_in = implode( ',', array_map( 'absint', $query_vars['post_parent__not_in'] ) );
2273              $where              .= " AND {$wpdb->posts}.post_parent NOT IN ($post_parent__not_in)";
2274          }
2275  
2276          if ( $query_vars['page_id'] ) {
2277              if ( ( 'page' !== get_option( 'show_on_front' ) ) || ( get_option( 'page_for_posts' ) != $query_vars['page_id'] ) ) {
2278                  $query_vars['p'] = $query_vars['page_id'];
2279                  $where           = " AND {$wpdb->posts}.ID = " . $query_vars['page_id'];
2280              }
2281          }
2282  
2283          // If a search pattern is specified, load the posts that match.
2284          if ( strlen( $query_vars['s'] ) ) {
2285              $search = $this->parse_search( $query_vars );
2286          }
2287  
2288          if ( ! $query_vars['suppress_filters'] ) {
2289              /**
2290               * Filters the search SQL that is used in the WHERE clause of WP_Query.
2291               *
2292               * @since 3.0.0
2293               *
2294               * @param string   $search Search SQL for WHERE clause.
2295               * @param WP_Query $query  The current WP_Query object.
2296               */
2297              $search = apply_filters_ref_array( 'posts_search', array( $search, &$this ) );
2298          }
2299  
2300          // Taxonomies.
2301          if ( ! $this->is_singular ) {
2302              $this->parse_tax_query( $query_vars );
2303  
2304              $clauses = $this->tax_query->get_sql( $wpdb->posts, 'ID' );
2305  
2306              $join  .= $clauses['join'];
2307              $where .= $clauses['where'];
2308          }
2309  
2310          if ( $this->is_tax ) {
2311              if ( empty( $post_type ) ) {
2312                  // Do a fully inclusive search for currently registered post types of queried taxonomies.
2313                  $post_type  = array();
2314                  $taxonomies = array_keys( $this->tax_query->queried_terms );
2315                  foreach ( get_post_types( array( 'exclude_from_search' => false ) ) as $pt ) {
2316                      $object_taxonomies = 'attachment' === $pt ? get_taxonomies_for_attachments() : get_object_taxonomies( $pt );
2317                      if ( array_intersect( $taxonomies, $object_taxonomies ) ) {
2318                          $post_type[] = $pt;
2319                      }
2320                  }
2321                  if ( ! $post_type ) {
2322                      $post_type = 'any';
2323                  } elseif ( count( $post_type ) === 1 ) {
2324                      $post_type = $post_type[0];
2325                  } else {
2326                      // Sort post types to ensure same cache key generation.
2327                      sort( $post_type );
2328                  }
2329  
2330                  $post_status_join = true;
2331              } elseif ( in_array( 'attachment', (array) $post_type, true ) ) {
2332                  $post_status_join = true;
2333              }
2334          }
2335  
2336          /*
2337           * Ensure that 'taxonomy', 'term', 'term_id', 'cat', and
2338           * 'category_name' vars are set for backward compatibility.
2339           */
2340          if ( ! empty( $this->tax_query->queried_terms ) ) {
2341  
2342              /*
2343               * Set 'taxonomy', 'term', and 'term_id' to the
2344               * first taxonomy other than 'post_tag' or 'category'.
2345               */
2346              if ( ! isset( $query_vars['taxonomy'] ) ) {
2347                  foreach ( $this->tax_query->queried_terms as $queried_taxonomy => $queried_items ) {
2348                      if ( empty( $queried_items['terms'][0] ) ) {
2349                          continue;
2350                      }
2351  
2352                      if ( ! in_array( $queried_taxonomy, array( 'category', 'post_tag' ), true ) ) {
2353                          $query_vars['taxonomy'] = $queried_taxonomy;
2354  
2355                          if ( 'slug' === $queried_items['field'] ) {
2356                              $query_vars['term'] = $queried_items['terms'][0];
2357                          } else {
2358                              $query_vars['term_id'] = $queried_items['terms'][0];
2359                          }
2360  
2361                          // Take the first one we find.
2362                          break;
2363                      }
2364                  }
2365              }
2366  
2367              // 'cat', 'category_name', 'tag_id'.
2368              foreach ( $this->tax_query->queried_terms as $queried_taxonomy => $queried_items ) {
2369                  if ( empty( $queried_items['terms'][0] ) ) {
2370                      continue;
2371                  }
2372  
2373                  if ( 'category' === $queried_taxonomy ) {
2374                      $the_cat = get_term_by( $queried_items['field'], $queried_items['terms'][0], 'category' );
2375                      if ( $the_cat ) {
2376                          $this->set( 'cat', $the_cat->term_id );
2377                          $this->set( 'category_name', $the_cat->slug );
2378                      }
2379                      unset( $the_cat );
2380                  }
2381  
2382                  if ( 'post_tag' === $queried_taxonomy ) {
2383                      $the_tag = get_term_by( $queried_items['field'], $queried_items['terms'][0], 'post_tag' );
2384                      if ( $the_tag ) {
2385                          $this->set( 'tag_id', $the_tag->term_id );
2386                      }
2387                      unset( $the_tag );
2388                  }
2389              }
2390          }
2391  
2392          if ( ! empty( $this->tax_query->queries ) || ! empty( $this->meta_query->queries ) || ! empty( $this->allow_query_attachment_by_filename ) ) {
2393              $groupby = "{$wpdb->posts}.ID";
2394          }
2395  
2396          // Author/user stuff.
2397  
2398          if ( ! empty( $query_vars['author'] ) && '0' != $query_vars['author'] ) {
2399              $query_vars['author'] = wp_slash( '' . urldecode( $query_vars['author'] ) );
2400              $authors              = array_unique( array_map( 'intval', preg_split( '/[,\s]+/', $query_vars['author'] ) ) );
2401              sort( $authors );
2402              foreach ( $authors as $author ) {
2403                  $key                  = $author > 0 ? 'author__in' : 'author__not_in';
2404                  $query_vars[ $key ][] = abs( $author );
2405              }
2406              $query_vars['author'] = implode( ',', $authors );
2407          }
2408  
2409          if ( ! empty( $query_vars['author__not_in'] ) ) {
2410              $author__not_in_id_list = wp_parse_id_list( $query_vars['author__not_in'] );
2411              if ( count( $author__not_in_id_list ) > 0 ) {
2412                  sort( $author__not_in_id_list );
2413                  $where .= sprintf(
2414                      " AND {$wpdb->posts}.post_author NOT IN (%s) ",
2415                      implode( ',', $author__not_in_id_list )
2416                  );
2417                  /** Update the query var for stable cache key generation in {@see self::generate_cache_key()}. */
2418                  $query_vars['author__not_in'] = $author__not_in_id_list;
2419              }
2420          } elseif ( ! empty( $query_vars['author__in'] ) ) {
2421              if ( is_array( $query_vars['author__in'] ) ) {
2422                  $query_vars['author__in'] = array_unique( array_map( 'absint', $query_vars['author__in'] ) );
2423                  sort( $query_vars['author__in'] );
2424              }
2425              $author__in = implode( ',', array_map( 'absint', array_unique( (array) $query_vars['author__in'] ) ) );
2426              $where     .= " AND {$wpdb->posts}.post_author IN ($author__in) ";
2427          }
2428  
2429          // Author stuff for nice URLs.
2430  
2431          if ( '' !== $query_vars['author_name'] ) {
2432              if ( str_contains( $query_vars['author_name'], '/' ) ) {
2433                  $query_vars['author_name'] = explode( '/', $query_vars['author_name'] );
2434                  if ( $query_vars['author_name'][ count( $query_vars['author_name'] ) - 1 ] ) {
2435                      $query_vars['author_name'] = $query_vars['author_name'][ count( $query_vars['author_name'] ) - 1 ]; // No trailing slash.
2436                  } else {
2437                      $query_vars['author_name'] = $query_vars['author_name'][ count( $query_vars['author_name'] ) - 2 ]; // There was a trailing slash.
2438                  }
2439              }
2440              $query_vars['author_name'] = sanitize_title_for_query( $query_vars['author_name'] );
2441              $query_vars['author']      = get_user_by( 'slug', $query_vars['author_name'] );
2442              if ( $query_vars['author'] ) {
2443                  $query_vars['author'] = $query_vars['author']->ID;
2444              }
2445              $whichauthor .= " AND ({$wpdb->posts}.post_author = " . absint( $query_vars['author'] ) . ')';
2446          }
2447  
2448          // Matching by comment count.
2449          if ( isset( $query_vars['comment_count'] ) ) {
2450              // Numeric comment count is converted to array format.
2451              if ( is_numeric( $query_vars['comment_count'] ) ) {
2452                  $query_vars['comment_count'] = array(
2453                      'value' => (int) $query_vars['comment_count'],
2454                  );
2455              }
2456  
2457              if ( isset( $query_vars['comment_count']['value'] ) ) {
2458                  $query_vars['comment_count'] = array_merge(
2459                      array(
2460                          'compare' => '=',
2461                      ),
2462                      $query_vars['comment_count']
2463                  );
2464  
2465                  // Fallback for invalid compare operators is '='.
2466                  $compare_operators = array( '=', '!=', '>', '>=', '<', '<=' );
2467                  if ( ! in_array( $query_vars['comment_count']['compare'], $compare_operators, true ) ) {
2468                      $query_vars['comment_count']['compare'] = '=';
2469                  }
2470  
2471                  $where .= $wpdb->prepare( " AND {$wpdb->posts}.comment_count {$query_vars['comment_count']['compare']} %d", $query_vars['comment_count']['value'] );
2472              }
2473          }
2474  
2475          // MIME-Type stuff for attachment browsing.
2476  
2477          if ( isset( $query_vars['post_mime_type'] ) && '' !== $query_vars['post_mime_type'] ) {
2478              $whichmimetype = wp_post_mime_type_where( $query_vars['post_mime_type'], $wpdb->posts );
2479          }
2480          $where .= $search . $whichauthor . $whichmimetype;
2481  
2482          if ( ! empty( $this->allow_query_attachment_by_filename ) ) {
2483              $join .= " LEFT JOIN {$wpdb->postmeta} AS sq1 ON ( {$wpdb->posts}.ID = sq1.post_id AND sq1.meta_key = '_wp_attached_file' )";
2484          }
2485  
2486          if ( ! empty( $this->meta_query->queries ) ) {
2487              $clauses = $this->meta_query->get_sql( 'post', $wpdb->posts, 'ID', $this );
2488              $join   .= $clauses['join'];
2489              $where  .= $clauses['where'];
2490          }
2491  
2492          $rand = ( isset( $query_vars['orderby'] ) && 'rand' === $query_vars['orderby'] );
2493          if ( ! isset( $query_vars['order'] ) ) {
2494              $query_vars['order'] = $rand ? '' : 'DESC';
2495          } else {
2496              $query_vars['order'] = $rand ? '' : $this->parse_order( $query_vars['order'] );
2497          }
2498  
2499          // These values of orderby should ignore the 'order' parameter.
2500          $force_asc = array( 'post__in', 'post_name__in', 'post_parent__in' );
2501          if ( isset( $query_vars['orderby'] ) && in_array( $query_vars['orderby'], $force_asc, true ) ) {
2502              $query_vars['order'] = '';
2503          }
2504  
2505          // Order by.
2506          if ( empty( $query_vars['orderby'] ) ) {
2507              /*
2508               * Boolean false or empty array blanks out ORDER BY,
2509               * while leaving the value unset or otherwise empty sets the default.
2510               */
2511              if ( isset( $query_vars['orderby'] ) && ( is_array( $query_vars['orderby'] ) || false === $query_vars['orderby'] ) ) {
2512                  $orderby = '';
2513              } else {
2514                  $orderby = "{$wpdb->posts}.post_date " . $query_vars['order'];
2515              }
2516          } elseif ( 'none' === $query_vars['orderby'] ) {
2517              $orderby = '';
2518          } else {
2519              $orderby_array = array();
2520              if ( is_array( $query_vars['orderby'] ) ) {
2521                  foreach ( $query_vars['orderby'] as $_orderby => $order ) {
2522                      $orderby = wp_slash( urldecode( $_orderby ) );
2523                      $parsed  = $this->parse_orderby( $orderby );
2524  
2525                      if ( ! $parsed ) {
2526                          continue;
2527                      }
2528  
2529                      $orderby_array[] = $parsed . ' ' . $this->parse_order( $order );
2530                  }
2531                  $orderby = implode( ', ', $orderby_array );
2532  
2533              } else {
2534                  $query_vars['orderby'] = urldecode( $query_vars['orderby'] );
2535                  $query_vars['orderby'] = wp_slash( $query_vars['orderby'] );
2536  
2537                  foreach ( explode( ' ', $query_vars['orderby'] ) as $i => $orderby ) {
2538                      $parsed = $this->parse_orderby( $orderby );
2539                      // Only allow certain values for safety.
2540                      if ( ! $parsed ) {
2541                          continue;
2542                      }
2543  
2544                      $orderby_array[] = $parsed;
2545                  }
2546                  $orderby = implode( ' ' . $query_vars['order'] . ', ', $orderby_array );
2547  
2548                  if ( empty( $orderby ) ) {
2549                      $orderby = "{$wpdb->posts}.post_date " . $query_vars['order'];
2550                  } elseif ( ! empty( $query_vars['order'] ) ) {
2551                      $orderby .= " {$query_vars['order']}";
2552                  }
2553              }
2554          }
2555  
2556          // Order search results by relevance only when another "orderby" is not specified in the query.
2557          if ( ! empty( $query_vars['s'] ) ) {
2558              $search_orderby = '';
2559              if ( ! empty( $query_vars['search_orderby_title'] ) && ( empty( $query_vars['orderby'] ) && ! $this->is_feed ) || ( isset( $query_vars['orderby'] ) && 'relevance' === $query_vars['orderby'] ) ) {
2560                  $search_orderby = $this->parse_search_order( $query_vars );
2561              }
2562  
2563              if ( ! $query_vars['suppress_filters'] ) {
2564                  /**
2565                   * Filters the ORDER BY used when ordering search results.
2566                   *
2567                   * @since 3.7.0
2568                   *
2569                   * @param string   $search_orderby The ORDER BY clause.
2570                   * @param WP_Query $query          The current WP_Query instance.
2571                   */
2572                  $search_orderby = apply_filters( 'posts_search_orderby', $search_orderby, $this );
2573              }
2574  
2575              if ( $search_orderby ) {
2576                  $orderby = $orderby ? $search_orderby . ', ' . $orderby : $search_orderby;
2577              }
2578          }
2579  
2580          if ( is_array( $post_type ) && count( $post_type ) > 1 ) {
2581              $post_type_cap = 'multiple_post_type';
2582          } else {
2583              if ( is_array( $post_type ) ) {
2584                  $post_type = reset( $post_type );
2585              }
2586              $post_type_object = get_post_type_object( $post_type );
2587              if ( empty( $post_type_object ) ) {
2588                  $post_type_cap = $post_type;
2589              }
2590          }
2591  
2592          if ( isset( $query_vars['post_password'] ) ) {
2593              $where .= $wpdb->prepare( " AND {$wpdb->posts}.post_password = %s", $query_vars['post_password'] );
2594              if ( empty( $query_vars['perm'] ) ) {
2595                  $query_vars['perm'] = 'readable';
2596              }
2597          } elseif ( isset( $query_vars['has_password'] ) ) {
2598              $where .= sprintf( " AND {$wpdb->posts}.post_password %s ''", $query_vars['has_password'] ? '!=' : '=' );
2599          }
2600  
2601          if ( ! empty( $query_vars['comment_status'] ) ) {
2602              $where .= $wpdb->prepare( " AND {$wpdb->posts}.comment_status = %s ", $query_vars['comment_status'] );
2603          }
2604  
2605          if ( ! empty( $query_vars['ping_status'] ) ) {
2606              $where .= $wpdb->prepare( " AND {$wpdb->posts}.ping_status = %s ", $query_vars['ping_status'] );
2607          }
2608  
2609          $skip_post_status = false;
2610          if ( 'any' === $post_type ) {
2611              $in_search_post_types = get_post_types( array( 'exclude_from_search' => false ) );
2612              if ( empty( $in_search_post_types ) ) {
2613                  $post_type_where  = ' AND 1=0 ';
2614                  $skip_post_status = true;
2615              } else {
2616                  $post_type_where = " AND {$wpdb->posts}.post_type IN ('" . implode( "', '", array_map( 'esc_sql', $in_search_post_types ) ) . "')";
2617              }
2618          } elseif ( ! empty( $post_type ) && is_array( $post_type ) ) {
2619              // Sort post types to ensure same cache key generation.
2620              sort( $post_type );
2621              $post_type_where = " AND {$wpdb->posts}.post_type IN ('" . implode( "', '", esc_sql( $post_type ) ) . "')";
2622          } elseif ( ! empty( $post_type ) ) {
2623              $post_type_where  = $wpdb->prepare( " AND {$wpdb->posts}.post_type = %s", $post_type );
2624              $post_type_object = get_post_type_object( $post_type );
2625          } elseif ( $this->is_attachment ) {
2626              $post_type_where  = " AND {$wpdb->posts}.post_type = 'attachment'";
2627              $post_type_object = get_post_type_object( 'attachment' );
2628          } elseif ( $this->is_page ) {
2629              $post_type_where  = " AND {$wpdb->posts}.post_type = 'page'";
2630              $post_type_object = get_post_type_object( 'page' );
2631          } else {
2632              $post_type_where  = " AND {$wpdb->posts}.post_type = 'post'";
2633              $post_type_object = get_post_type_object( 'post' );
2634          }
2635  
2636          $edit_cap = 'edit_post';
2637          $read_cap = 'read_post';
2638  
2639          if ( ! empty( $post_type_object ) ) {
2640              $edit_others_cap  = $post_type_object->cap->edit_others_posts;
2641              $read_private_cap = $post_type_object->cap->read_private_posts;
2642          } else {
2643              $edit_others_cap  = 'edit_others_' . $post_type_cap . 's';
2644              $read_private_cap = 'read_private_' . $post_type_cap . 's';
2645          }
2646  
2647          $user_id = get_current_user_id();
2648  
2649          $q_status = array();
2650          if ( $skip_post_status ) {
2651              $where .= $post_type_where;
2652          } elseif ( ! empty( $query_vars['post_status'] ) ) {
2653  
2654              $where .= $post_type_where;
2655  
2656              $statuswheres = array();
2657              $q_status     = $query_vars['post_status'];
2658              if ( ! is_array( $q_status ) ) {
2659                  $q_status = explode( ',', $q_status );
2660              }
2661              sort( $q_status );
2662              $r_status = array();
2663              $p_status = array();
2664              $e_status = array();
2665              if ( in_array( 'any', $q_status, true ) ) {
2666                  foreach ( get_post_stati( array( 'exclude_from_search' => true ) ) as $status ) {
2667                      if ( ! in_array( $status, $q_status, true ) ) {
2668                          $e_status[] = "{$wpdb->posts}.post_status <> '$status'";
2669                      }
2670                  }
2671              } else {
2672                  foreach ( get_post_stati() as $status ) {
2673                      if ( in_array( $status, $q_status, true ) ) {
2674                          if ( 'private' === $status ) {
2675                              $p_status[] = "{$wpdb->posts}.post_status = '$status'";
2676                          } else {
2677                              $r_status[] = "{$wpdb->posts}.post_status = '$status'";
2678                          }
2679                      }
2680                  }
2681              }
2682  
2683              if ( empty( $query_vars['perm'] ) || 'readable' !== $query_vars['perm'] ) {
2684                  $r_status = array_merge( $r_status, $p_status );
2685                  unset( $p_status );
2686              }
2687  
2688              if ( ! empty( $e_status ) ) {
2689                  $statuswheres[] = '(' . implode( ' AND ', $e_status ) . ')';
2690              }
2691              if ( ! empty( $r_status ) ) {
2692                  if ( ! empty( $query_vars['perm'] ) && 'editable' === $query_vars['perm'] && ! current_user_can( $edit_others_cap ) ) {
2693                      $statuswheres[] = "({$wpdb->posts}.post_author = $user_id " . 'AND (' . implode( ' OR ', $r_status ) . '))';
2694                  } else {
2695                      $statuswheres[] = '(' . implode( ' OR ', $r_status ) . ')';
2696                  }
2697              }
2698              if ( ! empty( $p_status ) ) {
2699                  if ( ! empty( $query_vars['perm'] ) && 'readable' === $query_vars['perm'] && ! current_user_can( $read_private_cap ) ) {
2700                      $statuswheres[] = "({$wpdb->posts}.post_author = $user_id " . 'AND (' . implode( ' OR ', $p_status ) . '))';
2701                  } else {
2702                      $statuswheres[] = '(' . implode( ' OR ', $p_status ) . ')';
2703                  }
2704              }
2705              if ( $post_status_join ) {
2706                  $join .= " LEFT JOIN {$wpdb->posts} AS p2 ON ({$wpdb->posts}.post_parent = p2.ID) ";
2707                  foreach ( $statuswheres as $index => $statuswhere ) {
2708                      $statuswheres[ $index ] = "($statuswhere OR ({$wpdb->posts}.post_status = 'inherit' AND " . str_replace( $wpdb->posts, 'p2', $statuswhere ) . '))';
2709                  }
2710              }
2711              $where_status = implode( ' OR ', $statuswheres );
2712              if ( ! empty( $where_status ) ) {
2713                  $where .= " AND ($where_status)";
2714              }
2715          } elseif ( ! $this->is_singular ) {
2716              if ( 'any' === $post_type ) {
2717                  $queried_post_types = get_post_types( array( 'exclude_from_search' => false ) );
2718              } elseif ( is_array( $post_type ) ) {
2719                  $queried_post_types = $post_type;
2720              } elseif ( ! empty( $post_type ) ) {
2721                  $queried_post_types = array( $post_type );
2722              } else {
2723                  $queried_post_types = array( 'post' );
2724              }
2725  
2726              if ( ! empty( $queried_post_types ) ) {
2727                  sort( $queried_post_types );
2728                  $status_type_clauses = array();
2729  
2730                  foreach ( $queried_post_types as $queried_post_type ) {
2731  
2732                      $queried_post_type_object = get_post_type_object( $queried_post_type );
2733  
2734                      $type_where = '(' . $wpdb->prepare( "{$wpdb->posts}.post_type = %s AND (", $queried_post_type );
2735  
2736                      // Public statuses.
2737                      $public_statuses = get_post_stati( array( 'public' => true ) );
2738                      $status_clauses  = array();
2739                      foreach ( $public_statuses as $public_status ) {
2740                          $status_clauses[] = "{$wpdb->posts}.post_status = '$public_status'";
2741                      }
2742                      $type_where .= implode( ' OR ', $status_clauses );
2743  
2744                      // Add protected states that should show in the admin all list.
2745                      if ( $this->is_admin ) {
2746                          $admin_all_statuses = get_post_stati(
2747                              array(
2748                                  'protected'              => true,
2749                                  'show_in_admin_all_list' => true,
2750                              )
2751                          );
2752                          foreach ( $admin_all_statuses as $admin_all_status ) {
2753                              $type_where .= " OR {$wpdb->posts}.post_status = '$admin_all_status'";
2754                          }
2755                      }
2756  
2757                      // Add private states that are visible to current user.
2758                      if ( is_user_logged_in() && $queried_post_type_object instanceof WP_Post_Type ) {
2759                          $read_private_cap = $queried_post_type_object->cap->read_private_posts;
2760                          $private_statuses = get_post_stati( array( 'private' => true ) );
2761                          foreach ( $private_statuses as $private_status ) {
2762                              $type_where .= current_user_can( $read_private_cap ) ? " \nOR {$wpdb->posts}.post_status = '$private_status'" : " \nOR ({$wpdb->posts}.post_author = $user_id AND {$wpdb->posts}.post_status = '$private_status')";
2763                          }
2764                      }
2765  
2766                      $type_where .= '))';
2767  
2768                      $status_type_clauses[] = $type_where;
2769                  }
2770  
2771                  if ( ! empty( $status_type_clauses ) ) {
2772                      $where .= ' AND (' . implode( ' OR ', $status_type_clauses ) . ')';
2773                  }
2774              } else {
2775                  $where .= ' AND 1=0 ';
2776              }
2777          } else {
2778              $where .= $post_type_where;
2779          }
2780  
2781          /*
2782           * Apply filters on where and join prior to paging so that any
2783           * manipulations to them are reflected in the paging by day queries.
2784           */
2785          if ( ! $query_vars['suppress_filters'] ) {
2786              /**
2787               * Filters the WHERE clause of the query.
2788               *
2789               * @since 1.5.0
2790               *
2791               * @param string   $where The WHERE clause of the query.
2792               * @param WP_Query $query The WP_Query instance (passed by reference).
2793               */
2794              $where = apply_filters_ref_array( 'posts_where', array( $where, &$this ) );
2795  
2796              /**
2797               * Filters the JOIN clause of the query.
2798               *
2799               * @since 1.5.0
2800               *
2801               * @param string   $join  The JOIN clause of the query.
2802               * @param WP_Query $query The WP_Query instance (passed by reference).
2803               */
2804              $join = apply_filters_ref_array( 'posts_join', array( $join, &$this ) );
2805          }
2806  
2807          // Paging.
2808          if ( empty( $query_vars['nopaging'] ) && ! $this->is_singular ) {
2809              $page = absint( $query_vars['paged'] );
2810              if ( ! $page ) {
2811                  $page = 1;
2812              }
2813  
2814              // If 'offset' is provided, it takes precedence over 'paged'.
2815              if ( isset( $query_vars['offset'] ) && is_numeric( $query_vars['offset'] ) ) {
2816                  $query_vars['offset'] = absint( $query_vars['offset'] );
2817                  $pgstrt               = $query_vars['offset'] . ', ';
2818              } else {
2819                  $pgstrt = absint( ( $page - 1 ) * $query_vars['posts_per_page'] ) . ', ';
2820              }
2821              $limits = 'LIMIT ' . $pgstrt . $query_vars['posts_per_page'];
2822          }
2823  
2824          // Comments feeds.
2825          if ( $this->is_comment_feed && ! $this->is_singular ) {
2826              if ( $this->is_archive || $this->is_search ) {
2827                  $cjoin    = "JOIN {$wpdb->posts} ON ( {$wpdb->comments}.comment_post_ID = {$wpdb->posts}.ID ) $join ";
2828                  $cwhere   = "WHERE comment_approved = '1' AND {$wpdb->comments}.comment_type != 'note' $where";
2829                  $cgroupby = "{$wpdb->comments}.comment_id";
2830              } else { // Other non-singular, e.g. front.
2831                  $cjoin    = "JOIN {$wpdb->posts} ON ( {$wpdb->comments}.comment_post_ID = {$wpdb->posts}.ID )";
2832                  $cwhere   = "WHERE ( post_status = 'publish' OR ( post_status = 'inherit' AND post_type = 'attachment' ) ) AND comment_approved = '1' AND {$wpdb->comments}.comment_type != 'note'";
2833                  $cgroupby = '';
2834              }
2835  
2836              if ( ! $query_vars['suppress_filters'] ) {
2837                  /**
2838                   * Filters the JOIN clause of the comments feed query before sending.
2839                   *
2840                   * @since 2.2.0
2841                   *
2842                   * @param string   $cjoin The JOIN clause of the query.
2843                   * @param WP_Query $query The WP_Query instance (passed by reference).
2844                   */
2845                  $cjoin = apply_filters_ref_array( 'comment_feed_join', array( $cjoin, &$this ) );
2846  
2847                  /**
2848                   * Filters the WHERE clause of the comments feed query before sending.
2849                   *
2850                   * @since 2.2.0
2851                   *
2852                   * @param string   $cwhere The WHERE clause of the query.
2853                   * @param WP_Query $query  The WP_Query instance (passed by reference).
2854                   */
2855                  $cwhere = apply_filters_ref_array( 'comment_feed_where', array( $cwhere, &$this ) );
2856  
2857                  /**
2858                   * Filters the GROUP BY clause of the comments feed query before sending.
2859                   *
2860                   * @since 2.2.0
2861                   *
2862                   * @param string   $cgroupby The GROUP BY clause of the query.
2863                   * @param WP_Query $query    The WP_Query instance (passed by reference).
2864                   */
2865                  $cgroupby = apply_filters_ref_array( 'comment_feed_groupby', array( $cgroupby, &$this ) );
2866  
2867                  /**
2868                   * Filters the ORDER BY clause of the comments feed query before sending.
2869                   *
2870                   * @since 2.8.0
2871                   *
2872                   * @param string   $corderby The ORDER BY clause of the query.
2873                   * @param WP_Query $query    The WP_Query instance (passed by reference).
2874                   */
2875                  $corderby = apply_filters_ref_array( 'comment_feed_orderby', array( 'comment_date_gmt DESC', &$this ) );
2876  
2877                  /**
2878                   * Filters the LIMIT clause of the comments feed query before sending.
2879                   *
2880                   * @since 2.8.0
2881                   *
2882                   * @param string   $climits The JOIN clause of the query.
2883                   * @param WP_Query $query   The WP_Query instance (passed by reference).
2884                   */
2885                  $climits = apply_filters_ref_array( 'comment_feed_limits', array( 'LIMIT ' . get_option( 'posts_per_rss' ), &$this ) );
2886              }
2887  
2888              $cgroupby = ( ! empty( $cgroupby ) ) ? 'GROUP BY ' . $cgroupby : '';
2889              $corderby = ( ! empty( $corderby ) ) ? 'ORDER BY ' . $corderby : '';
2890              $climits  = ( ! empty( $climits ) ) ? $climits : '';
2891  
2892              $comments_request = "SELECT $distinct {$wpdb->comments}.comment_ID FROM {$wpdb->comments} $cjoin $cwhere $cgroupby $corderby $climits";
2893  
2894              $key          = md5( $comments_request );
2895              $last_changed = array(
2896                  wp_cache_get_last_changed( 'comment' ),
2897                  wp_cache_get_last_changed( 'posts' ),
2898              );
2899  
2900              $cache_key   = "comment_feed:$key";
2901              $comment_ids = wp_cache_get_salted( $cache_key, 'comment-queries', $last_changed );
2902              if ( false === $comment_ids ) {
2903                  $comment_ids = $wpdb->get_col( $comments_request );
2904                  wp_cache_set_salted( $cache_key, $comment_ids, 'comment-queries', $last_changed );
2905              }
2906              _prime_comment_caches( $comment_ids );
2907  
2908              // Convert to WP_Comment.
2909              /** @var WP_Comment[] */
2910              $this->comments      = array_map( 'get_comment', $comment_ids );
2911              $this->comment_count = count( $this->comments );
2912  
2913              $post_ids = array();
2914  
2915              foreach ( $this->comments as $comment ) {
2916                  $post_ids[] = (int) $comment->comment_post_ID;
2917              }
2918  
2919              $post_ids = implode( ',', $post_ids );
2920              $join     = '';
2921              if ( $post_ids ) {
2922                  $where = "AND {$wpdb->posts}.ID IN ($post_ids) ";
2923              } else {
2924                  $where = 'AND 0';
2925              }
2926          }
2927  
2928          $pieces = array( 'where', 'groupby', 'join', 'orderby', 'distinct', 'fields', 'limits' );
2929  
2930          /*
2931           * Apply post-paging filters on where and join. Only plugins that
2932           * manipulate paging queries should use these hooks.
2933           */
2934          if ( ! $query_vars['suppress_filters'] ) {
2935              /**
2936               * Filters the WHERE clause of the query.
2937               *
2938               * Specifically for manipulating paging queries.
2939               *
2940               * @since 1.5.0
2941               *
2942               * @param string   $where The WHERE clause of the query.
2943               * @param WP_Query $query The WP_Query instance (passed by reference).
2944               */
2945              $where = apply_filters_ref_array( 'posts_where_paged', array( $where, &$this ) );
2946  
2947              /**
2948               * Filters the GROUP BY clause of the query.
2949               *
2950               * @since 2.0.0
2951               *
2952               * @param string   $groupby The GROUP BY clause of the query.
2953               * @param WP_Query $query   The WP_Query instance (passed by reference).
2954               */
2955              $groupby = apply_filters_ref_array( 'posts_groupby', array( $groupby, &$this ) );
2956  
2957              /**
2958               * Filters the JOIN clause of the query.
2959               *
2960               * Specifically for manipulating paging queries.
2961               *
2962               * @since 1.5.0
2963               *
2964               * @param string   $join  The JOIN clause of the query.
2965               * @param WP_Query $query The WP_Query instance (passed by reference).
2966               */
2967              $join = apply_filters_ref_array( 'posts_join_paged', array( $join, &$this ) );
2968  
2969              /**
2970               * Filters the ORDER BY clause of the query.
2971               *
2972               * @since 1.5.1
2973               *
2974               * @param string   $orderby The ORDER BY clause of the query.
2975               * @param WP_Query $query   The WP_Query instance (passed by reference).
2976               */
2977              $orderby = apply_filters_ref_array( 'posts_orderby', array( $orderby, &$this ) );
2978  
2979              /**
2980               * Filters the DISTINCT clause of the query.
2981               *
2982               * @since 2.1.0
2983               *
2984               * @param string   $distinct The DISTINCT clause of the query.
2985               * @param WP_Query $query    The WP_Query instance (passed by reference).
2986               */
2987              $distinct = apply_filters_ref_array( 'posts_distinct', array( $distinct, &$this ) );
2988  
2989              /**
2990               * Filters the LIMIT clause of the query.
2991               *
2992               * @since 2.1.0
2993               *
2994               * @param string   $limits The LIMIT clause of the query.
2995               * @param WP_Query $query  The WP_Query instance (passed by reference).
2996               */
2997              $limits = apply_filters_ref_array( 'post_limits', array( $limits, &$this ) );
2998  
2999              /**
3000               * Filters the SELECT clause of the query.
3001               *
3002               * @since 2.1.0
3003               *
3004               * @param string   $fields The SELECT clause of the query.
3005               * @param WP_Query $query  The WP_Query instance (passed by reference).
3006               */
3007              $fields = apply_filters_ref_array( 'posts_fields', array( $fields, &$this ) );
3008  
3009              /**
3010               * Filters all query clauses at once, for convenience.
3011               *
3012               * Covers the WHERE, GROUP BY, JOIN, ORDER BY, DISTINCT,
3013               * fields (SELECT), and LIMIT clauses.
3014               *
3015               * @since 3.1.0
3016               *
3017               * @param string[] $clauses {
3018               *     Associative array of the clauses for the query.
3019               *
3020               *     @type string $where    The WHERE clause of the query.
3021               *     @type string $groupby  The GROUP BY clause of the query.
3022               *     @type string $join     The JOIN clause of the query.
3023               *     @type string $orderby  The ORDER BY clause of the query.
3024               *     @type string $distinct The DISTINCT clause of the query.
3025               *     @type string $fields   The SELECT clause of the query.
3026               *     @type string $limits   The LIMIT clause of the query.
3027               * }
3028               * @param WP_Query $query   The WP_Query instance (passed by reference).
3029               */
3030              $clauses = (array) apply_filters_ref_array( 'posts_clauses', array( compact( $pieces ), &$this ) );
3031  
3032              $where    = $clauses['where'] ?? '';
3033              $groupby  = $clauses['groupby'] ?? '';
3034              $join     = $clauses['join'] ?? '';
3035              $orderby  = $clauses['orderby'] ?? '';
3036              $distinct = $clauses['distinct'] ?? '';
3037              $fields   = $clauses['fields'] ?? '';
3038              $limits   = $clauses['limits'] ?? '';
3039          }
3040  
3041          /**
3042           * Fires to announce the query's current selection parameters.
3043           *
3044           * For use by caching plugins.
3045           *
3046           * @since 2.3.0
3047           *
3048           * @param string $selection The assembled selection query.
3049           */
3050          do_action( 'posts_selection', $where . $groupby . $orderby . $limits . $join );
3051  
3052          /*
3053           * Filters again for the benefit of caching plugins.
3054           * Regular plugins should use the hooks above.
3055           */
3056          if ( ! $query_vars['suppress_filters'] ) {
3057              /**
3058               * Filters the WHERE clause of the query.
3059               *
3060               * For use by caching plugins.
3061               *
3062               * @since 2.5.0
3063               *
3064               * @param string   $where The WHERE clause of the query.
3065               * @param WP_Query $query The WP_Query instance (passed by reference).
3066               */
3067              $where = apply_filters_ref_array( 'posts_where_request', array( $where, &$this ) );
3068  
3069              /**
3070               * Filters the GROUP BY clause of the query.
3071               *
3072               * For use by caching plugins.
3073               *
3074               * @since 2.5.0
3075               *
3076               * @param string   $groupby The GROUP BY clause of the query.
3077               * @param WP_Query $query   The WP_Query instance (passed by reference).
3078               */
3079              $groupby = apply_filters_ref_array( 'posts_groupby_request', array( $groupby, &$this ) );
3080  
3081              /**
3082               * Filters the JOIN clause of the query.
3083               *
3084               * For use by caching plugins.
3085               *
3086               * @since 2.5.0
3087               *
3088               * @param string   $join  The JOIN clause of the query.
3089               * @param WP_Query $query The WP_Query instance (passed by reference).
3090               */
3091              $join = apply_filters_ref_array( 'posts_join_request', array( $join, &$this ) );
3092  
3093              /**
3094               * Filters the ORDER BY clause of the query.
3095               *
3096               * For use by caching plugins.
3097               *
3098               * @since 2.5.0
3099               *
3100               * @param string   $orderby The ORDER BY clause of the query.
3101               * @param WP_Query $query   The WP_Query instance (passed by reference).
3102               */
3103              $orderby = apply_filters_ref_array( 'posts_orderby_request', array( $orderby, &$this ) );
3104  
3105              /**
3106               * Filters the DISTINCT clause of the query.
3107               *
3108               * For use by caching plugins.
3109               *
3110               * @since 2.5.0
3111               *
3112               * @param string   $distinct The DISTINCT clause of the query.
3113               * @param WP_Query $query    The WP_Query instance (passed by reference).
3114               */
3115              $distinct = apply_filters_ref_array( 'posts_distinct_request', array( $distinct, &$this ) );
3116  
3117              /**
3118               * Filters the SELECT clause of the query.
3119               *
3120               * For use by caching plugins.
3121               *
3122               * @since 2.5.0
3123               *
3124               * @param string   $fields The SELECT clause of the query.
3125               * @param WP_Query $query  The WP_Query instance (passed by reference).
3126               */
3127              $fields = apply_filters_ref_array( 'posts_fields_request', array( $fields, &$this ) );
3128  
3129              /**
3130               * Filters the LIMIT clause of the query.
3131               *
3132               * For use by caching plugins.
3133               *
3134               * @since 2.5.0
3135               *
3136               * @param string   $limits The LIMIT clause of the query.
3137               * @param WP_Query $query  The WP_Query instance (passed by reference).
3138               */
3139              $limits = apply_filters_ref_array( 'post_limits_request', array( $limits, &$this ) );
3140  
3141              /**
3142               * Filters all query clauses at once, for convenience.
3143               *
3144               * For use by caching plugins.
3145               *
3146               * Covers the WHERE, GROUP BY, JOIN, ORDER BY, DISTINCT,
3147               * fields (SELECT), and LIMIT clauses.
3148               *
3149               * @since 3.1.0
3150               *
3151               * @param string[] $clauses {
3152               *     Associative array of the clauses for the query.
3153               *
3154               *     @type string $where    The WHERE clause of the query.
3155               *     @type string $groupby  The GROUP BY clause of the query.
3156               *     @type string $join     The JOIN clause of the query.
3157               *     @type string $orderby  The ORDER BY clause of the query.
3158               *     @type string $distinct The DISTINCT clause of the query.
3159               *     @type string $fields   The SELECT clause of the query.
3160               *     @type string $limits   The LIMIT clause of the query.
3161               * }
3162               * @param WP_Query $query  The WP_Query instance (passed by reference).
3163               */
3164              $clauses = (array) apply_filters_ref_array( 'posts_clauses_request', array( compact( $pieces ), &$this ) );
3165  
3166              $where    = $clauses['where'] ?? '';
3167              $groupby  = $clauses['groupby'] ?? '';
3168              $join     = $clauses['join'] ?? '';
3169              $orderby  = $clauses['orderby'] ?? '';
3170              $distinct = $clauses['distinct'] ?? '';
3171              $fields   = $clauses['fields'] ?? '';
3172              $limits   = $clauses['limits'] ?? '';
3173          }
3174  
3175          if ( ! empty( $groupby ) ) {
3176              $groupby = 'GROUP BY ' . $groupby;
3177          }
3178          if ( ! empty( $orderby ) ) {
3179              $orderby = 'ORDER BY ' . $orderby;
3180          }
3181  
3182          $found_rows = '';
3183          if ( ! $query_vars['no_found_rows'] && ! empty( $limits ) ) {
3184              $found_rows = 'SQL_CALC_FOUND_ROWS';
3185          }
3186  
3187          /*
3188           * Beginning of the string is on a new line to prevent leading whitespace.
3189           *
3190           * The additional indentation of subsequent lines is to ensure the SQL
3191           * queries are identical to those generated when splitting queries. This
3192           * improves caching of the query by ensuring the same cache key is
3193           * generated for the same database queries functionally.
3194           *
3195           * See https://core.trac.wordpress.org/ticket/56841.
3196           * See https://github.com/WordPress/wordpress-develop/pull/6393#issuecomment-2088217429
3197           */
3198          $old_request =
3199              "SELECT $found_rows $distinct $fields
3200                       FROM {$wpdb->posts} $join
3201                       WHERE 1=1 $where
3202                       $groupby
3203                       $orderby
3204                       $limits";
3205  
3206          $this->request = $old_request;
3207  
3208          if ( ! $query_vars['suppress_filters'] ) {
3209              /**
3210               * Filters the completed SQL query before sending.
3211               *
3212               * @since 2.0.0
3213               *
3214               * @param string   $request The complete SQL query.
3215               * @param WP_Query $query   The WP_Query instance (passed by reference).
3216               */
3217              $this->request = apply_filters_ref_array( 'posts_request', array( $this->request, &$this ) );
3218          }
3219  
3220          /**
3221           * Filters the posts array before the query takes place.
3222           *
3223           * Return a non-null value to bypass WordPress' default post queries.
3224           *
3225           * Filtering functions that require pagination information are encouraged to set
3226           * the `found_posts` and `max_num_pages` properties of the WP_Query object,
3227           * passed to the filter by reference. If WP_Query does not perform a database
3228           * query, it will not have enough information to generate these values itself.
3229           *
3230           * @since 4.6.0
3231           *
3232           * @param WP_Post[]|int[]|null $posts Return an array of post data to short-circuit WP's query,
3233           *                                    or null to allow WP to run its normal queries.
3234           * @param WP_Query             $query The WP_Query instance (passed by reference).
3235           */
3236          $this->posts = apply_filters_ref_array( 'posts_pre_query', array( null, &$this ) );
3237  
3238          /*
3239           * Ensure the ID database query is able to be cached.
3240           *
3241           * Random queries are expected to have unpredictable results and
3242           * cannot be cached. Note the space before `RAND` in the string
3243           * search, that to ensure against a collision with another
3244           * function.
3245           *
3246           * If `$fields` has been modified by the `posts_fields`,
3247           * `posts_fields_request`, `post_clauses` or `posts_clauses_request`
3248           * filters, then caching is disabled to prevent caching collisions.
3249           */
3250          $id_query_is_cacheable = ! str_contains( strtoupper( $orderby ), ' RAND(' );
3251  
3252          $cacheable_field_values = array(
3253              "{$wpdb->posts}.*",
3254              "{$wpdb->posts}.ID, {$wpdb->posts}.post_parent",
3255              "{$wpdb->posts}.ID",
3256          );
3257  
3258          if ( ! in_array( $fields, $cacheable_field_values, true ) ) {
3259              $id_query_is_cacheable = false;
3260          }
3261  
3262          $last_changed = (array) wp_cache_get_last_changed( 'posts' );
3263          if ( ! empty( $this->tax_query->queries ) ) {
3264              $last_changed[] = wp_cache_get_last_changed( 'terms' );
3265          }
3266  
3267          if ( $query_vars['cache_results'] && $id_query_is_cacheable ) {
3268              $new_request = str_replace( $fields, "{$wpdb->posts}.*", $this->request );
3269              $cache_key   = $this->generate_cache_key( $query_vars, $new_request );
3270  
3271              $cache_found = false;
3272              if ( null === $this->posts ) {
3273                  $cached_results = wp_cache_get_salted( $cache_key, 'post-queries', $last_changed );
3274  
3275                  if ( $cached_results ) {
3276                      $cache_found = true;
3277                      /** @var int[] */
3278                      $post_ids = array_map( 'intval', $cached_results['posts'] );
3279  
3280                      $this->post_count    = count( $post_ids );
3281                      $this->found_posts   = $cached_results['found_posts'];
3282                      $this->max_num_pages = $cached_results['max_num_pages'];
3283  
3284                      if ( 'ids' === $query_vars['fields'] ) {
3285                          $this->posts = $post_ids;
3286  
3287                          return $this->posts;
3288                      } elseif ( 'id=>parent' === $query_vars['fields'] ) {
3289                          _prime_post_parent_id_caches( $post_ids );
3290  
3291                          $post_parent_cache_keys = array();
3292                          foreach ( $post_ids as $post_id ) {
3293                              $post_parent_cache_keys[] = 'post_parent:' . (string) $post_id;
3294                          }
3295  
3296                          /** @var int[] */
3297                          $post_parents = wp_cache_get_multiple( $post_parent_cache_keys, 'posts' );
3298  
3299                          foreach ( $post_parents as $cache_key => $post_parent ) {
3300                              $obj              = new stdClass();
3301                              $obj->ID          = (int) str_replace( 'post_parent:', '', $cache_key );
3302                              $obj->post_parent = (int) $post_parent;
3303  
3304                              $this->posts[] = $obj;
3305                          }
3306  
3307                          return $post_parents;
3308                      } else {
3309                          _prime_post_caches( $post_ids, $query_vars['update_post_term_cache'], $query_vars['update_post_meta_cache'] );
3310                          /** @var WP_Post[] */
3311                          $this->posts = array_map( 'get_post', $post_ids );
3312                      }
3313                  }
3314              }
3315          }
3316  
3317          if ( 'ids' === $query_vars['fields'] ) {
3318              if ( null === $this->posts ) {
3319                  $this->posts = $wpdb->get_col( $this->request );
3320              }
3321  
3322              /** @var int[] */
3323              $this->posts      = array_map( 'intval', $this->posts );
3324              $this->post_count = count( $this->posts );
3325              $this->set_found_posts( $query_vars, $limits );
3326  
3327              if ( $query_vars['cache_results'] && $id_query_is_cacheable ) {
3328                  $cache_value = array(
3329                      'posts'         => $this->posts,
3330                      'found_posts'   => $this->found_posts,
3331                      'max_num_pages' => $this->max_num_pages,
3332                  );
3333  
3334                  wp_cache_set_salted( $cache_key, $cache_value, 'post-queries', $last_changed );
3335              }
3336  
3337              return $this->posts;
3338          }
3339  
3340          if ( 'id=>parent' === $query_vars['fields'] ) {
3341              if ( null === $this->posts ) {
3342                  $this->posts = $wpdb->get_results( $this->request );
3343              }
3344  
3345              $this->post_count = count( $this->posts );
3346              $this->set_found_posts( $query_vars, $limits );
3347  
3348              /** @var int[] */
3349              $post_parents       = array();
3350              $post_ids           = array();
3351              $post_parents_cache = array();
3352  
3353              foreach ( $this->posts as $key => $post ) {
3354                  $this->posts[ $key ]->ID          = (int) $post->ID;
3355                  $this->posts[ $key ]->post_parent = (int) $post->post_parent;
3356  
3357                  $post_parents[ (int) $post->ID ] = (int) $post->post_parent;
3358                  $post_ids[]                      = (int) $post->ID;
3359  
3360                  $post_parents_cache[ 'post_parent:' . (string) $post->ID ] = (int) $post->post_parent;
3361              }
3362              // Prime post parent caches, so that on second run, there is not another database query.
3363              wp_cache_add_multiple( $post_parents_cache, 'posts' );
3364  
3365              if ( $query_vars['cache_results'] && $id_query_is_cacheable ) {
3366                  $cache_value = array(
3367                      'posts'         => $post_ids,
3368                      'found_posts'   => $this->found_posts,
3369                      'max_num_pages' => $this->max_num_pages,
3370                  );
3371  
3372                  wp_cache_set_salted( $cache_key, $cache_value, 'post-queries', $last_changed );
3373              }
3374  
3375              return $post_parents;
3376          }
3377  
3378          $is_unfiltered_query = $old_request === $this->request && "{$wpdb->posts}.*" === $fields;
3379  
3380          if ( null === $this->posts ) {
3381              $split_the_query = (
3382                  $is_unfiltered_query
3383                  && (
3384                      wp_using_ext_object_cache()
3385                      || ( ! empty( $limits ) && $query_vars['posts_per_page'] < 500 )
3386                  )
3387              );
3388  
3389              /**
3390               * Filters whether to split the query.
3391               *
3392               * Splitting the query will cause it to fetch just the IDs of the found posts
3393               * (and then individually fetch each post by ID), rather than fetching every
3394               * complete row at once. One massive result vs. many small results.
3395               *
3396               * @since 3.4.0
3397               * @since 6.6.0 Added the `$old_request` and `$clauses` parameters.
3398               *
3399               * @param bool     $split_the_query Whether or not to split the query.
3400               * @param WP_Query $query           The WP_Query instance.
3401               * @param string   $old_request     The complete SQL query before filtering.
3402               * @param string[] $clauses {
3403               *     Associative array of the clauses for the query.
3404               *
3405               *     @type string $where    The WHERE clause of the query.
3406               *     @type string $groupby  The GROUP BY clause of the query.
3407               *     @type string $join     The JOIN clause of the query.
3408               *     @type string $orderby  The ORDER BY clause of the query.
3409               *     @type string $distinct The DISTINCT clause of the query.
3410               *     @type string $fields   The SELECT clause of the query.
3411               *     @type string $limits   The LIMIT clause of the query.
3412               * }
3413               */
3414              $split_the_query = apply_filters( 'split_the_query', $split_the_query, $this, $old_request, compact( $pieces ) );
3415  
3416              if ( $split_the_query ) {
3417                  // First get the IDs and then fill in the objects.
3418  
3419                  // Beginning of the string is on a new line to prevent leading whitespace. See https://core.trac.wordpress.org/ticket/56841.
3420                  $this->request =
3421                      "SELECT $found_rows $distinct {$wpdb->posts}.ID
3422                       FROM {$wpdb->posts} $join
3423                       WHERE 1=1 $where
3424                       $groupby
3425                       $orderby
3426                       $limits";
3427  
3428                  /**
3429                   * Filters the Post IDs SQL request before sending.
3430                   *
3431                   * @since 3.4.0
3432                   *
3433                   * @param string   $request The post ID request.
3434                   * @param WP_Query $query   The WP_Query instance.
3435                   */
3436                  $this->request = apply_filters( 'posts_request_ids', $this->request, $this );
3437  
3438                  $post_ids = $wpdb->get_col( $this->request );
3439  
3440                  if ( $post_ids ) {
3441                      $this->posts = $post_ids;
3442                      $this->set_found_posts( $query_vars, $limits );
3443                      _prime_post_caches( $post_ids, $query_vars['update_post_term_cache'], $query_vars['update_post_meta_cache'] );
3444                  } else {
3445                      $this->posts = array();
3446                  }
3447              } else {
3448                  $this->posts = $wpdb->get_results( $this->request );
3449                  $this->set_found_posts( $query_vars, $limits );
3450              }
3451          }
3452  
3453          // Convert to WP_Post objects.
3454          if ( $this->posts ) {
3455              /** @var WP_Post[] */
3456              $this->posts = array_map( 'get_post', $this->posts );
3457          }
3458  
3459          $unfiltered_posts = $this->posts;
3460  
3461          if ( $query_vars['cache_results'] && $id_query_is_cacheable && ! $cache_found ) {
3462              $post_ids = wp_list_pluck( $this->posts, 'ID' );
3463  
3464              $cache_value = array(
3465                  'posts'         => $post_ids,
3466                  'found_posts'   => $this->found_posts,
3467                  'max_num_pages' => $this->max_num_pages,
3468              );
3469  
3470              wp_cache_set_salted( $cache_key, $cache_value, 'post-queries', $last_changed );
3471          }
3472  
3473          if ( ! $query_vars['suppress_filters'] ) {
3474              /**
3475               * Filters the raw post results array, prior to status checks.
3476               *
3477               * @since 2.3.0
3478               *
3479               * @param WP_Post[] $posts Array of post objects.
3480               * @param WP_Query  $query The WP_Query instance (passed by reference).
3481               */
3482              $this->posts = apply_filters_ref_array( 'posts_results', array( $this->posts, &$this ) );
3483          }
3484  
3485          if ( ! empty( $this->posts ) && $this->is_comment_feed && $this->is_singular ) {
3486              /** This filter is documented in wp-includes/class-wp-query.php */
3487              $cjoin = apply_filters_ref_array( 'comment_feed_join', array( '', &$this ) );
3488  
3489              /** This filter is documented in wp-includes/class-wp-query.php */
3490              $cwhere = apply_filters_ref_array( 'comment_feed_where', array( "WHERE comment_post_ID = '{$this->posts[0]->ID}' AND comment_approved = '1' AND {$wpdb->comments}.comment_type != 'note'", &$this ) );
3491  
3492              /** This filter is documented in wp-includes/class-wp-query.php */
3493              $cgroupby = apply_filters_ref_array( 'comment_feed_groupby', array( '', &$this ) );
3494              $cgroupby = ( ! empty( $cgroupby ) ) ? 'GROUP BY ' . $cgroupby : '';
3495  
3496              /** This filter is documented in wp-includes/class-wp-query.php */
3497              $corderby = apply_filters_ref_array( 'comment_feed_orderby', array( 'comment_date_gmt DESC', &$this ) );
3498              $corderby = ( ! empty( $corderby ) ) ? 'ORDER BY ' . $corderby : '';
3499  
3500              /** This filter is documented in wp-includes/class-wp-query.php */
3501              $climits = apply_filters_ref_array( 'comment_feed_limits', array( 'LIMIT ' . get_option( 'posts_per_rss' ), &$this ) );
3502  
3503              $comments_request = "SELECT {$wpdb->comments}.comment_ID FROM {$wpdb->comments} $cjoin $cwhere $cgroupby $corderby $climits";
3504  
3505              $comment_key          = md5( $comments_request );
3506              $comment_last_changed = wp_cache_get_last_changed( 'comment' );
3507  
3508              $comment_cache_key = "comment_feed:$comment_key";
3509              $comment_ids       = wp_cache_get_salted( $comment_cache_key, 'comment-queries', $comment_last_changed );
3510              if ( false === $comment_ids ) {
3511                  $comment_ids = $wpdb->get_col( $comments_request );
3512                  wp_cache_set_salted( $comment_cache_key, $comment_ids, 'comment-queries', $comment_last_changed );
3513              }
3514              _prime_comment_caches( $comment_ids );
3515  
3516              // Convert to WP_Comment.
3517              /** @var WP_Comment[] */
3518              $this->comments      = array_map( 'get_comment', $comment_ids );
3519              $this->comment_count = count( $this->comments );
3520          }
3521  
3522          // Check post status to determine if post should be displayed.
3523          if ( ! empty( $this->posts ) && ( $this->is_single || $this->is_page ) ) {
3524              $status = get_post_status( $this->posts[0] );
3525  
3526              if ( 'attachment' === $this->posts[0]->post_type && 0 === (int) $this->posts[0]->post_parent ) {
3527                  $this->is_page       = false;
3528                  $this->is_single     = true;
3529                  $this->is_attachment = true;
3530              }
3531  
3532              // If the post_status was specifically requested, let it pass through.
3533              if ( ! in_array( $status, $q_status, true ) ) {
3534                  $post_status_obj = get_post_status_object( $status );
3535  
3536                  if ( $post_status_obj && ! $post_status_obj->public ) {
3537                      if ( ! is_user_logged_in() ) {
3538                          // User must be logged in to view unpublished posts.
3539                          $this->posts = array();
3540                      } else {
3541                          if ( $post_status_obj->protected ) {
3542                              // User must have edit permissions on the draft to preview.
3543                              if ( ! current_user_can( $edit_cap, $this->posts[0]->ID ) ) {
3544                                  $this->posts = array();
3545                              } else {
3546                                  $this->is_preview = true;
3547                                  if ( 'future' !== $status ) {
3548                                      $this->posts[0]->post_date = current_time( 'mysql' );
3549                                  }
3550                              }
3551                          } elseif ( $post_status_obj->private ) {
3552                              if ( ! current_user_can( $read_cap, $this->posts[0]->ID ) ) {
3553                                  $this->posts = array();
3554                              }
3555                          } else {
3556                              $this->posts = array();
3557                          }
3558                      }
3559                  } elseif ( ! $post_status_obj ) {
3560                      // Post status is not registered, assume it's not public.
3561                      if ( ! current_user_can( $edit_cap, $this->posts[0]->ID ) ) {
3562                          $this->posts = array();
3563                      }
3564                  }
3565              }
3566  
3567              if ( $this->is_preview && $this->posts && current_user_can( $edit_cap, $this->posts[0]->ID ) ) {
3568                  /**
3569                   * Filters the single post for preview mode.
3570                   *
3571                   * @since 2.7.0
3572                   *
3573                   * @param WP_Post  $post_preview  The Post object.
3574                   * @param WP_Query $query         The WP_Query instance (passed by reference).
3575                   */
3576                  $this->posts[0] = get_post( apply_filters_ref_array( 'the_preview', array( $this->posts[0], &$this ) ) );
3577              }
3578          }
3579  
3580          // Put sticky posts at the top of the posts array.
3581          $sticky_posts = get_option( 'sticky_posts' );
3582          if ( $this->is_home && $page <= 1 && is_array( $sticky_posts ) && ! empty( $sticky_posts ) && ! $query_vars['ignore_sticky_posts'] ) {
3583              $num_posts     = count( $this->posts );
3584              $sticky_offset = 0;
3585              // Loop over posts and relocate stickies to the front.
3586              for ( $i = 0; $i < $num_posts; $i++ ) {
3587                  if ( in_array( $this->posts[ $i ]->ID, $sticky_posts, true ) ) {
3588                      $sticky_post = $this->posts[ $i ];
3589                      // Remove sticky from current position.
3590                      array_splice( $this->posts, $i, 1 );
3591                      // Move to front, after other stickies.
3592                      array_splice( $this->posts, $sticky_offset, 0, array( $sticky_post ) );
3593                      // Increment the sticky offset. The next sticky will be placed at this offset.
3594                      ++$sticky_offset;
3595                      // Remove post from sticky posts array.
3596                      $offset = array_search( $sticky_post->ID, $sticky_posts, true );
3597                      unset( $sticky_posts[ $offset ] );
3598                  }
3599              }
3600  
3601              // If any posts have been excluded specifically, Ignore those that are sticky.
3602              if ( ! empty( $sticky_posts ) && ! empty( $query_vars['post__not_in'] ) ) {
3603                  $sticky_posts = array_diff( $sticky_posts, $query_vars['post__not_in'] );
3604              }
3605  
3606              // Fetch sticky posts that weren't in the query results.
3607              if ( ! empty( $sticky_posts ) ) {
3608                  $stickies = get_posts(
3609                      array(
3610                          'post__in'               => $sticky_posts,
3611                          'post_type'              => $post_type,
3612                          'post_status'            => 'publish',
3613                          'posts_per_page'         => count( $sticky_posts ),
3614                          'suppress_filters'       => $query_vars['suppress_filters'],
3615                          'cache_results'          => $query_vars['cache_results'],
3616                          'update_post_meta_cache' => $query_vars['update_post_meta_cache'],
3617                          'update_post_term_cache' => $query_vars['update_post_term_cache'],
3618                          'lazy_load_term_meta'    => $query_vars['lazy_load_term_meta'],
3619                      )
3620                  );
3621  
3622                  foreach ( $stickies as $sticky_post ) {
3623                      array_splice( $this->posts, $sticky_offset, 0, array( $sticky_post ) );
3624                      ++$sticky_offset;
3625                  }
3626              }
3627          }
3628  
3629          if ( ! $query_vars['suppress_filters'] ) {
3630              /**
3631               * Filters the array of retrieved posts after they've been fetched and
3632               * internally processed.
3633               *
3634               * @since 1.5.0
3635               *
3636               * @param WP_Post[] $posts Array of post objects.
3637               * @param WP_Query  $query The WP_Query instance (passed by reference).
3638               */
3639              $this->posts = apply_filters_ref_array( 'the_posts', array( $this->posts, &$this ) );
3640          }
3641  
3642          /*
3643           * Ensure that any posts added/modified via one of the filters above are
3644           * of the type WP_Post and are filtered.
3645           */
3646          if ( $this->posts ) {
3647              $this->post_count = count( $this->posts );
3648  
3649              /** @var WP_Post[] */
3650              $this->posts = array_map( 'get_post', $this->posts );
3651  
3652              if ( $query_vars['cache_results'] ) {
3653                  if ( $is_unfiltered_query && $unfiltered_posts === $this->posts ) {
3654                      update_post_caches( $this->posts, $post_type, $query_vars['update_post_term_cache'], $query_vars['update_post_meta_cache'] );
3655                  } else {
3656                      $post_ids = wp_list_pluck( $this->posts, 'ID' );
3657                      _prime_post_caches( $post_ids, $query_vars['update_post_term_cache'], $query_vars['update_post_meta_cache'] );
3658                  }
3659              }
3660  
3661              /** @var WP_Post */
3662              $this->post = reset( $this->posts );
3663          } else {
3664              $this->post_count = 0;
3665              $this->posts      = array();
3666          }
3667  
3668          if ( ! empty( $this->posts ) && $query_vars['update_menu_item_cache'] ) {
3669              update_menu_item_cache( $this->posts );
3670          }
3671  
3672          if ( $query_vars['lazy_load_term_meta'] ) {
3673              wp_queue_posts_for_term_meta_lazyload( $this->posts );
3674          }
3675  
3676          return $this->posts;
3677      }
3678  
3679      /**
3680       * Sets up the amount of found posts and the number of pages (if limit clause was used)
3681       * for the current query.
3682       *
3683       * @since 3.5.0
3684       *
3685       * @global wpdb $wpdb WordPress database abstraction object.
3686       *
3687       * @param array  $query_vars Query variables.
3688       * @param string $limits     LIMIT clauses of the query.
3689       */
3690  	private function set_found_posts( $query_vars, $limits ) {
3691          global $wpdb;
3692  
3693          /*
3694           * Bail if posts is an empty array. Continue if posts is an empty string,
3695           * null, or false to accommodate caching plugins that fill posts later.
3696           */
3697          if ( $query_vars['no_found_rows'] || ( is_array( $this->posts ) && ! $this->posts ) ) {
3698              return;
3699          }
3700  
3701          if ( ! empty( $limits ) ) {
3702              /**
3703               * Filters the query to run for retrieving the found posts.
3704               *
3705               * @since 2.1.0
3706               *
3707               * @param string   $found_posts_query The query to run to find the found posts.
3708               * @param WP_Query $query             The WP_Query instance (passed by reference).
3709               */
3710              $found_posts_query = apply_filters_ref_array( 'found_posts_query', array( 'SELECT FOUND_ROWS()', &$this ) );
3711  
3712              $this->found_posts = (int) $wpdb->get_var( $found_posts_query );
3713          } else {
3714              if ( is_array( $this->posts ) ) {
3715                  $this->found_posts = count( $this->posts );
3716              } else {
3717                  if ( null === $this->posts ) {
3718                      $this->found_posts = 0;
3719                  } else {
3720                      $this->found_posts = 1;
3721                  }
3722              }
3723          }
3724  
3725          /**
3726           * Filters the number of found posts for the query.
3727           *
3728           * @since 2.1.0
3729           *
3730           * @param int      $found_posts The number of posts found.
3731           * @param WP_Query $query       The WP_Query instance (passed by reference).
3732           */
3733          $this->found_posts = (int) apply_filters_ref_array( 'found_posts', array( $this->found_posts, &$this ) );
3734  
3735          if ( ! empty( $limits ) ) {
3736              $this->max_num_pages = (int) ceil( $this->found_posts / $query_vars['posts_per_page'] );
3737          }
3738      }
3739  
3740      /**
3741       * Sets up the next post and iterate current post index.
3742       *
3743       * @since 1.5.0
3744       *
3745       * @return WP_Post Next post.
3746       */
3747  	public function next_post() {
3748  
3749          ++$this->current_post;
3750  
3751          /** @var WP_Post */
3752          $this->post = $this->posts[ $this->current_post ];
3753          return $this->post;
3754      }
3755  
3756      /**
3757       * Sets up the current post.
3758       *
3759       * Retrieves the next post, sets up the post, sets the 'in the loop'
3760       * property to true.
3761       *
3762       * @since 1.5.0
3763       *
3764       * @global WP_Post $post Global post object.
3765       */
3766  	public function the_post() {
3767          global $post;
3768  
3769          if ( ! $this->in_the_loop ) {
3770              if ( 'all' === $this->query_vars['fields'] ) {
3771                  // Full post objects queried.
3772                  $post_objects = $this->posts;
3773              } else {
3774                  if ( 'ids' === $this->query_vars['fields'] ) {
3775                      // Post IDs queried.
3776                      $post_ids = $this->posts;
3777                  } else {
3778                      // Only partial objects queried, need to prime the cache for the loop.
3779                      $post_ids = array_reduce(
3780                          $this->posts,
3781                          function ( $carry, $post ) {
3782                              if ( isset( $post->ID ) ) {
3783                                  $carry[] = $post->ID;
3784                              }
3785  
3786                              return $carry;
3787                          },
3788                          array()
3789                      );
3790                  }
3791                  _prime_post_caches( $post_ids, $this->query_vars['update_post_term_cache'], $this->query_vars['update_post_meta_cache'] );
3792                  $post_objects = array_map( 'get_post', $post_ids );
3793              }
3794              update_post_author_caches( $post_objects );
3795          }
3796  
3797          $this->in_the_loop = true;
3798          $this->before_loop = false;
3799  
3800          if ( -1 === $this->current_post ) { // Loop has just started.
3801              /**
3802               * Fires once the loop is started.
3803               *
3804               * @since 2.0.0
3805               *
3806               * @param WP_Query $query The WP_Query instance (passed by reference).
3807               */
3808              do_action_ref_array( 'loop_start', array( &$this ) );
3809          }
3810  
3811          $post = $this->next_post();
3812  
3813          // Ensure a full post object is available.
3814          if ( 'all' !== $this->query_vars['fields'] ) {
3815              if ( 'ids' === $this->query_vars['fields'] ) {
3816                  // Post IDs queried.
3817                  $post = get_post( $post );
3818              } elseif ( isset( $post->ID ) ) {
3819                  /*
3820                   * Partial object queried.
3821                   *
3822                   * The post object was queried with a partial set of
3823                   * fields, populate the entire object for the loop.
3824                   */
3825                  $post = get_post( $post->ID );
3826              }
3827          }
3828  
3829          // Set up the global post object for the loop.
3830          $this->setup_postdata( $post );
3831      }
3832  
3833      /**
3834       * Determines whether there are more posts available in the loop.
3835       *
3836       * Calls the {@see 'loop_end'} action when the loop is complete.
3837       *
3838       * @since 1.5.0
3839       *
3840       * @return bool True if posts are available, false if end of the loop.
3841       */
3842  	public function have_posts() {
3843          if ( $this->current_post + 1 < $this->post_count ) {
3844              return true;
3845          } elseif ( $this->current_post + 1 === $this->post_count && $this->post_count > 0 ) {
3846              /**
3847               * Fires once the loop has ended.
3848               *
3849               * @since 2.0.0
3850               *
3851               * @param WP_Query $query The WP_Query instance (passed by reference).
3852               */
3853              do_action_ref_array( 'loop_end', array( &$this ) );
3854  
3855              // Do some cleaning up after the loop.
3856              $this->rewind_posts();
3857          } elseif ( 0 === $this->post_count ) {
3858              $this->before_loop = false;
3859  
3860              /**
3861               * Fires if no results are found in a post query.
3862               *
3863               * @since 4.9.0
3864               *
3865               * @param WP_Query $query The WP_Query instance.
3866               */
3867              do_action( 'loop_no_results', $this );
3868          }
3869  
3870          $this->in_the_loop = false;
3871          return false;
3872      }
3873  
3874      /**
3875       * Rewinds the posts and resets post index.
3876       *
3877       * @since 1.5.0
3878       */
3879  	public function rewind_posts() {
3880          $this->current_post = -1;
3881          if ( $this->post_count > 0 ) {
3882              $this->post = $this->posts[0];
3883          }
3884      }
3885  
3886      /**
3887       * Iterates current comment index and returns WP_Comment object.
3888       *
3889       * @since 2.2.0
3890       *
3891       * @return WP_Comment Comment object.
3892       */
3893  	public function next_comment() {
3894          ++$this->current_comment;
3895  
3896          /** @var WP_Comment */
3897          $this->comment = $this->comments[ $this->current_comment ];
3898          return $this->comment;
3899      }
3900  
3901      /**
3902       * Sets up the current comment.
3903       *
3904       * @since 2.2.0
3905       *
3906       * @global WP_Comment $comment Global comment object.
3907       */
3908  	public function the_comment() {
3909          global $comment;
3910  
3911          $comment = $this->next_comment();
3912  
3913          if ( 0 === $this->current_comment ) {
3914              /**
3915               * Fires once the comment loop is started.
3916               *
3917               * @since 2.2.0
3918               */
3919              do_action( 'comment_loop_start' );
3920          }
3921      }
3922  
3923      /**
3924       * Determines whether there are more comments available.
3925       *
3926       * Automatically rewinds comments when finished.
3927       *
3928       * @since 2.2.0
3929       *
3930       * @return bool True if comments are available, false if no more comments.
3931       */
3932  	public function have_comments() {
3933          if ( $this->current_comment + 1 < $this->comment_count ) {
3934              return true;
3935          } elseif ( $this->current_comment + 1 === $this->comment_count ) {
3936              $this->rewind_comments();
3937          }
3938  
3939          return false;
3940      }
3941  
3942      /**
3943       * Rewinds the comments, resets the comment index and comment to first.
3944       *
3945       * @since 2.2.0
3946       */
3947  	public function rewind_comments() {
3948          $this->current_comment = -1;
3949          if ( $this->comment_count > 0 ) {
3950              $this->comment = $this->comments[0];
3951          }
3952      }
3953  
3954      /**
3955       * Sets up the WordPress query by parsing query string.
3956       *
3957       * @since 1.5.0
3958       *
3959       * @see WP_Query::parse_query() for all available arguments.
3960       *
3961       * @param string|array $query URL query string or array of query arguments.
3962       * @return WP_Post[]|int[] Array of post objects or post IDs.
3963       *
3964       * @phpstan-return (
3965       *     $query is array{ fields: 'ids', ... } ? int[] : WP_Post[]
3966       * )
3967       */
3968  	public function query( $query ) {
3969          $this->init();
3970          $this->query      = wp_parse_args( $query );
3971          $this->query_vars = $this->query;
3972          return $this->get_posts();
3973      }
3974  
3975      /**
3976       * Retrieves the currently queried object.
3977       *
3978       * If queried object is not set, then the queried object will be set from
3979       * the category, tag, taxonomy, posts page, single post, page, or author
3980       * query variable. After it is set up, it will be returned.
3981       *
3982       * @since 1.5.0
3983       *
3984       * @return WP_Term|WP_Post_Type|WP_Post|WP_User|null The queried object.
3985       */
3986  	public function get_queried_object() {
3987          if ( isset( $this->queried_object ) ) {
3988              return $this->queried_object;
3989          }
3990  
3991          $this->queried_object    = null;
3992          $this->queried_object_id = null;
3993  
3994          if ( $this->is_category || $this->is_tag || $this->is_tax ) {
3995              if ( $this->is_category ) {
3996                  $cat           = $this->get( 'cat' );
3997                  $category_name = $this->get( 'category_name' );
3998  
3999                  if ( $cat ) {
4000                      $term = get_term( $cat, 'category' );
4001                  } elseif ( $category_name ) {
4002                      $term = get_term_by( 'slug', $category_name, 'category' );
4003                  }
4004              } elseif ( $this->is_tag ) {
4005                  $tag_id = $this->get( 'tag_id' );
4006                  $tag    = $this->get( 'tag' );
4007  
4008                  if ( $tag_id ) {
4009                      $term = get_term( $tag_id, 'post_tag' );
4010                  } elseif ( $tag ) {
4011                      $term = get_term_by( 'slug', $tag, 'post_tag' );
4012                  }
4013              } else {
4014                  // For other tax queries, grab the first term from the first clause.
4015                  if ( ! empty( $this->tax_query->queried_terms ) ) {
4016                      $queried_taxonomies = array_keys( $this->tax_query->queried_terms );
4017                      $matched_taxonomy   = reset( $queried_taxonomies );
4018                      $query              = $this->tax_query->queried_terms[ $matched_taxonomy ];
4019  
4020                      if ( ! empty( $query['terms'] ) ) {
4021                          if ( 'term_id' === $query['field'] ) {
4022                              $term = get_term( reset( $query['terms'] ), $matched_taxonomy );
4023                          } else {
4024                              $term = get_term_by( $query['field'], reset( $query['terms'] ), $matched_taxonomy );
4025                          }
4026                      }
4027                  }
4028              }
4029  
4030              if ( ! empty( $term ) && ! is_wp_error( $term ) ) {
4031                  $this->queried_object    = $term;
4032                  $this->queried_object_id = (int) $term->term_id;
4033  
4034                  if ( $this->is_category && 'category' === $this->queried_object->taxonomy ) {
4035                      _make_cat_compat( $this->queried_object );
4036                  }
4037              }
4038          } elseif ( $this->is_post_type_archive ) {
4039              $post_type = $this->get( 'post_type' );
4040              if ( is_array( $post_type ) ) {
4041                  $post_type = reset( $post_type );
4042              }
4043  
4044              $this->queried_object = get_post_type_object( $post_type );
4045          } elseif ( $this->is_posts_page ) {
4046              $posts_page = get_post( get_option( 'page_for_posts' ) );
4047              if ( $posts_page ) {
4048                  $this->queried_object    = $posts_page;
4049                  $this->queried_object_id = (int) $posts_page->ID;
4050              }
4051          } elseif ( $this->is_singular && ! empty( $this->post ) ) {
4052              $this->queried_object    = $this->post;
4053              $this->queried_object_id = (int) $this->post->ID;
4054          } elseif ( $this->is_author ) {
4055              $author      = (int) $this->get( 'author' );
4056              $author_name = $this->get( 'author_name' );
4057  
4058              if ( $author ) {
4059                  $this->queried_object_id = $author;
4060              } elseif ( $author_name ) {
4061                  $user = get_user_by( 'slug', $author_name );
4062                  if ( $user ) {
4063                      $this->queried_object_id = $user->ID;
4064                  }
4065              }
4066  
4067              if ( $this->queried_object_id ) {
4068                  $user = get_userdata( $this->queried_object_id );
4069                  if ( $user ) {
4070                      $this->queried_object = $user;
4071                  }
4072              }
4073          }
4074  
4075          return $this->queried_object;
4076      }
4077  
4078      /**
4079       * Retrieves the ID of the currently queried object.
4080       *
4081       * @since 1.5.0
4082       *
4083       * @return int
4084       */
4085  	public function get_queried_object_id() {
4086          $this->get_queried_object();
4087          return $this->queried_object_id ?? 0;
4088      }
4089  
4090      /**
4091       * Constructor.
4092       *
4093       * Sets up the WordPress query, if parameter is not empty.
4094       *
4095       * @since 1.5.0
4096       *
4097       * @see WP_Query::parse_query() for all available arguments.
4098       *
4099       * @param string|array $query URL query string or array of vars.
4100       */
4101  	public function __construct( $query = '' ) {
4102          if ( ! empty( $query ) ) {
4103              $this->query( $query );
4104          }
4105      }
4106  
4107      /**
4108       * Makes private properties readable for backward compatibility.
4109       *
4110       * @since 4.0.0
4111       *
4112       * @param string $name Property to get.
4113       * @return mixed Property.
4114       */
4115  	public function __get( $name ) {
4116          if ( in_array( $name, $this->compat_fields, true ) ) {
4117              return $this->$name;
4118          }
4119      }
4120  
4121      /**
4122       * Makes private properties checkable for backward compatibility.
4123       *
4124       * @since 4.0.0
4125       *
4126       * @param string $name Property to check if set.
4127       * @return bool Whether the property is set.
4128       */
4129  	public function __isset( $name ) {
4130          if ( in_array( $name, $this->compat_fields, true ) ) {
4131              return isset( $this->$name );
4132          }
4133  
4134          return false;
4135      }
4136  
4137      /**
4138       * Makes private/protected methods readable for backward compatibility.
4139       *
4140       * @since 4.0.0
4141       *
4142       * @param string $name      Method to call.
4143       * @param array  $arguments Arguments to pass when calling.
4144       * @return mixed|false Return value of the callback, false otherwise.
4145       */
4146  	public function __call( $name, $arguments ) {
4147          if ( in_array( $name, $this->compat_methods, true ) ) {
4148              return $this->$name( ...$arguments );
4149          }
4150          return false;
4151      }
4152  
4153      /**
4154       * Determines whether the query is for an existing archive page.
4155       *
4156       * Archive pages include category, tag, author, date, custom post type,
4157       * and custom taxonomy based archives.
4158       *
4159       * @since 3.1.0
4160       *
4161       * @see WP_Query::is_category()
4162       * @see WP_Query::is_tag()
4163       * @see WP_Query::is_author()
4164       * @see WP_Query::is_date()
4165       * @see WP_Query::is_post_type_archive()
4166       * @see WP_Query::is_tax()
4167       *
4168       * @return bool Whether the query is for an existing archive page.
4169       */
4170  	public function is_archive() {
4171          return (bool) $this->is_archive;
4172      }
4173  
4174      /**
4175       * Determines whether the query is for an existing post type archive page.
4176       *
4177       * @since 3.1.0
4178       *
4179       * @param string|string[] $post_types Optional. Post type or array of posts types
4180       *                                    to check against. Default empty.
4181       * @return bool Whether the query is for an existing post type archive page.
4182       */
4183  	public function is_post_type_archive( $post_types = '' ) {
4184          if ( empty( $post_types ) || ! $this->is_post_type_archive ) {
4185              return (bool) $this->is_post_type_archive;
4186          }
4187  
4188          $post_type = $this->get( 'post_type' );
4189          if ( is_array( $post_type ) ) {
4190              $post_type = reset( $post_type );
4191          }
4192          $post_type_object = get_post_type_object( $post_type );
4193  
4194          if ( ! $post_type_object ) {
4195              return false;
4196          }
4197  
4198          return in_array( $post_type_object->name, (array) $post_types, true );
4199      }
4200  
4201      /**
4202       * Determines whether the query is for an existing attachment page.
4203       *
4204       * @since 3.1.0
4205       *
4206       * @param int|string|int[]|string[] $attachment Optional. Attachment ID, title, slug, or array of such
4207       *                                              to check against. Default empty.
4208       * @return bool Whether the query is for an existing attachment page.
4209       */
4210  	public function is_attachment( $attachment = '' ) {
4211          if ( ! $this->is_attachment ) {
4212              return false;
4213          }
4214  
4215          if ( empty( $attachment ) ) {
4216              return true;
4217          }
4218  
4219          $attachment = array_map( 'strval', (array) $attachment );
4220  
4221          $post_obj = $this->get_queried_object();
4222          if ( ! $post_obj ) {
4223              return false;
4224          }
4225  
4226          if ( in_array( (string) $post_obj->ID, $attachment, true ) ) {
4227              return true;
4228          } elseif ( in_array( $post_obj->post_title, $attachment, true ) ) {
4229              return true;
4230          } elseif ( in_array( $post_obj->post_name, $attachment, true ) ) {
4231              return true;
4232          }
4233          return false;
4234      }
4235  
4236      /**
4237       * Determines whether the query is for an existing author archive page.
4238       *
4239       * If the $author parameter is specified, this function will additionally
4240       * check if the query is for one of the authors specified.
4241       *
4242       * @since 3.1.0
4243       *
4244       * @param int|string|int[]|string[] $author Optional. User ID, nickname, nicename, or array of such
4245       *                                          to check against. Default empty.
4246       * @return bool Whether the query is for an existing author archive page.
4247       */
4248  	public function is_author( $author = '' ) {
4249          if ( ! $this->is_author ) {
4250              return false;
4251          }
4252  
4253          if ( empty( $author ) ) {
4254              return true;
4255          }
4256  
4257          $author_obj = $this->get_queried_object();
4258          if ( ! $author_obj ) {
4259              return false;
4260          }
4261  
4262          $author = array_map( 'strval', (array) $author );
4263  
4264          if ( in_array( (string) $author_obj->ID, $author, true ) ) {
4265              return true;
4266          } elseif ( in_array( $author_obj->nickname, $author, true ) ) {
4267              return true;
4268          } elseif ( in_array( $author_obj->user_nicename, $author, true ) ) {
4269              return true;
4270          }
4271  
4272          return false;
4273      }
4274  
4275      /**
4276       * Determines whether the query is for an existing category archive page.
4277       *
4278       * If the $category parameter is specified, this function will additionally
4279       * check if the query is for one of the categories specified.
4280       *
4281       * @since 3.1.0
4282       *
4283       * @param int|string|int[]|string[] $category Optional. Category ID, name, slug, or array of such
4284       *                                            to check against. Default empty.
4285       * @return bool Whether the query is for an existing category archive page.
4286       */
4287  	public function is_category( $category = '' ) {
4288          if ( ! $this->is_category ) {
4289              return false;
4290          }
4291  
4292          if ( empty( $category ) ) {
4293              return true;
4294          }
4295  
4296          $cat_obj = $this->get_queried_object();
4297          if ( ! $cat_obj ) {
4298              return false;
4299          }
4300  
4301          $category = array_map( 'strval', (array) $category );
4302  
4303          if ( in_array( (string) $cat_obj->term_id, $category, true ) ) {
4304              return true;
4305          } elseif ( in_array( $cat_obj->name, $category, true ) ) {
4306              return true;
4307          } elseif ( in_array( $cat_obj->slug, $category, true ) ) {
4308              return true;
4309          }
4310  
4311          return false;
4312      }
4313  
4314      /**
4315       * Determines whether the query is for an existing tag archive page.
4316       *
4317       * If the $tag parameter is specified, this function will additionally
4318       * check if the query is for one of the tags specified.
4319       *
4320       * @since 3.1.0
4321       *
4322       * @param int|string|int[]|string[] $tag Optional. Tag ID, name, slug, or array of such
4323       *                                       to check against. Default empty.
4324       * @return bool Whether the query is for an existing tag archive page.
4325       */
4326  	public function is_tag( $tag = '' ) {
4327          if ( ! $this->is_tag ) {
4328              return false;
4329          }
4330  
4331          if ( empty( $tag ) ) {
4332              return true;
4333          }
4334  
4335          $tag_obj = $this->get_queried_object();
4336          if ( ! $tag_obj ) {
4337              return false;
4338          }
4339  
4340          $tag = array_map( 'strval', (array) $tag );
4341  
4342          if ( in_array( (string) $tag_obj->term_id, $tag, true ) ) {
4343              return true;
4344          } elseif ( in_array( $tag_obj->name, $tag, true ) ) {
4345              return true;
4346          } elseif ( in_array( $tag_obj->slug, $tag, true ) ) {
4347              return true;
4348          }
4349  
4350          return false;
4351      }
4352  
4353      /**
4354       * Determines whether the query is for an existing custom taxonomy archive page.
4355       *
4356       * If the $taxonomy parameter is specified, this function will additionally
4357       * check if the query is for that specific $taxonomy.
4358       *
4359       * If the $term parameter is specified in addition to the $taxonomy parameter,
4360       * this function will additionally check if the query is for one of the terms
4361       * specified.
4362       *
4363       * @since 3.1.0
4364       *
4365       * @global WP_Taxonomy[] $wp_taxonomies Registered taxonomies.
4366       *
4367       * @param string|string[]           $taxonomy Optional. Taxonomy slug or slugs to check against.
4368       *                                            Default empty.
4369       * @param int|string|int[]|string[] $term     Optional. Term ID, name, slug, or array of such
4370       *                                            to check against. Default empty.
4371       * @return bool Whether the query is for an existing custom taxonomy archive page.
4372       *              True for custom taxonomy archive pages, false for built-in taxonomies
4373       *              (category and tag archives).
4374       */
4375  	public function is_tax( $taxonomy = '', $term = '' ) {
4376          global $wp_taxonomies;
4377  
4378          if ( ! $this->is_tax ) {
4379              return false;
4380          }
4381  
4382          if ( empty( $taxonomy ) ) {
4383              return true;
4384          }
4385  
4386          $queried_object = $this->get_queried_object();
4387          $tax_array      = array_intersect( array_keys( $wp_taxonomies ), (array) $taxonomy );
4388          $term_array     = (array) $term;
4389  
4390          // Check that the taxonomy matches.
4391          if ( ! ( isset( $queried_object->taxonomy ) && count( $tax_array ) && in_array( $queried_object->taxonomy, $tax_array, true ) ) ) {
4392              return false;
4393          }
4394  
4395          // Only a taxonomy provided.
4396          if ( empty( $term ) ) {
4397              return true;
4398          }
4399  
4400          return isset( $queried_object->term_id ) &&
4401              count(
4402                  array_intersect(
4403                      array( $queried_object->term_id, $queried_object->name, $queried_object->slug ),
4404                      $term_array
4405                  )
4406              );
4407      }
4408  
4409      /**
4410       * Determines whether the current URL is within the comments popup window.
4411       *
4412       * @since 3.1.0
4413       * @deprecated 4.5.0
4414       *
4415       * @return false Always returns false.
4416       */
4417  	public function is_comments_popup() {
4418          _deprecated_function( __FUNCTION__, '4.5.0' );
4419  
4420          return false;
4421      }
4422  
4423      /**
4424       * Determines whether the query is for an existing date archive.
4425       *
4426       * @since 3.1.0
4427       *
4428       * @return bool Whether the query is for an existing date archive.
4429       */
4430  	public function is_date() {
4431          return (bool) $this->is_date;
4432      }
4433  
4434      /**
4435       * Determines whether the query is for an existing day archive.
4436       *
4437       * @since 3.1.0
4438       *
4439       * @return bool Whether the query is for an existing day archive.
4440       */
4441  	public function is_day() {
4442          return (bool) $this->is_day;
4443      }
4444  
4445      /**
4446       * Determines whether the query is for a feed.
4447       *
4448       * @since 3.1.0
4449       *
4450       * @param string|string[] $feeds Optional. Feed type or array of feed types
4451       *                                         to check against. Default empty.
4452       * @return bool Whether the query is for a feed.
4453       */
4454  	public function is_feed( $feeds = '' ) {
4455          if ( empty( $feeds ) || ! $this->is_feed ) {
4456              return (bool) $this->is_feed;
4457          }
4458  
4459          $query_var = $this->get( 'feed' );
4460          if ( 'feed' === $query_var ) {
4461              $query_var = get_default_feed();
4462          }
4463  
4464          return in_array( $query_var, (array) $feeds, true );
4465      }
4466  
4467      /**
4468       * Determines whether the query is for a comments feed.
4469       *
4470       * @since 3.1.0
4471       *
4472       * @return bool Whether the query is for a comments feed.
4473       */
4474  	public function is_comment_feed() {
4475          return (bool) $this->is_comment_feed;
4476      }
4477  
4478      /**
4479       * Determines whether the query is for the front page of the site.
4480       *
4481       * This is for what is displayed at your site's main URL.
4482       *
4483       * Depends on the site's "Front page displays" Reading Settings 'show_on_front' and 'page_on_front'.
4484       *
4485       * If you set a static page for the front page of your site, this function will return
4486       * true when viewing that page.
4487       *
4488       * Otherwise the same as {@see WP_Query::is_home()}.
4489       *
4490       * @since 3.1.0
4491       *
4492       * @return bool Whether the query is for the front page of the site.
4493       */
4494  	public function is_front_page() {
4495          // Most likely case.
4496          if ( 'posts' === get_option( 'show_on_front' ) && $this->is_home() ) {
4497              return true;
4498          } elseif ( 'page' === get_option( 'show_on_front' ) && get_option( 'page_on_front' )
4499              && $this->is_page( get_option( 'page_on_front' ) )
4500          ) {
4501              return true;
4502          } else {
4503              return false;
4504          }
4505      }
4506  
4507      /**
4508       * Determines whether the query is for the blog homepage.
4509       *
4510       * This is the page which shows the time based blog content of your site.
4511       *
4512       * Depends on the site's "Front page displays" Reading Settings 'show_on_front' and 'page_for_posts'.
4513       *
4514       * If you set a static page for the front page of your site, this function will return
4515       * true only on the page you set as the "Posts page".
4516       *
4517       * @since 3.1.0
4518       *
4519       * @see WP_Query::is_front_page()
4520       *
4521       * @return bool Whether the query is for the blog homepage.
4522       */
4523  	public function is_home() {
4524          return (bool) $this->is_home;
4525      }
4526  
4527      /**
4528       * Determines whether the query is for the Privacy Policy page.
4529       *
4530       * This is the page which shows the Privacy Policy content of your site.
4531       *
4532       * Depends on the site's "Change your Privacy Policy page" Privacy Settings 'wp_page_for_privacy_policy'.
4533       *
4534       * This function will return true only on the page you set as the "Privacy Policy page".
4535       *
4536       * @since 5.2.0
4537       *
4538       * @return bool Whether the query is for the Privacy Policy page.
4539       */
4540  	public function is_privacy_policy() {
4541          if ( get_option( 'wp_page_for_privacy_policy' )
4542              && $this->is_page( get_option( 'wp_page_for_privacy_policy' ) )
4543          ) {
4544              return true;
4545          } else {
4546              return false;
4547          }
4548      }
4549  
4550      /**
4551       * Determines whether the query is for an existing month archive.
4552       *
4553       * @since 3.1.0
4554       *
4555       * @return bool Whether the query is for an existing month archive.
4556       */
4557  	public function is_month() {
4558          return (bool) $this->is_month;
4559      }
4560  
4561      /**
4562       * Determines whether the query is for an existing single page.
4563       *
4564       * If the $page parameter is specified, this function will additionally
4565       * check if the query is for one of the pages specified.
4566       *
4567       * @since 3.1.0
4568       *
4569       * @see WP_Query::is_single()
4570       * @see WP_Query::is_singular()
4571       *
4572       * @param int|string|int[]|string[] $page Optional. Page ID, title, slug, path, or array of such
4573       *                                        to check against. Default empty.
4574       * @return bool Whether the query is for an existing single page.
4575       */
4576  	public function is_page( $page = '' ) {
4577          if ( ! $this->is_page ) {
4578              return false;
4579          }
4580  
4581          if ( empty( $page ) ) {
4582              return true;
4583          }
4584  
4585          $page_obj = $this->get_queried_object();
4586          if ( ! $page_obj ) {
4587              return false;
4588          }
4589  
4590          $page = array_map( 'strval', (array) $page );
4591  
4592          if ( in_array( (string) $page_obj->ID, $page, true ) ) {
4593              return true;
4594          } elseif ( in_array( $page_obj->post_title, $page, true ) ) {
4595              return true;
4596          } elseif ( in_array( $page_obj->post_name, $page, true ) ) {
4597              return true;
4598          } else {
4599              foreach ( $page as $pagepath ) {
4600                  if ( ! strpos( $pagepath, '/' ) ) {
4601                      continue;
4602                  }
4603  
4604                  $pagepath_obj = get_page_by_path( $pagepath );
4605  
4606                  if ( $pagepath_obj && ( $pagepath_obj->ID === $page_obj->ID ) ) {
4607                      return true;
4608                  }
4609              }
4610          }
4611  
4612          return false;
4613      }
4614  
4615      /**
4616       * Determines whether the query is for a paged result and not for the first page.
4617       *
4618       * @since 3.1.0
4619       *
4620       * @return bool Whether the query is for a paged result.
4621       */
4622  	public function is_paged() {
4623          return (bool) $this->is_paged;
4624      }
4625  
4626      /**
4627       * Determines whether the query is for a post or page preview.
4628       *
4629       * @since 3.1.0
4630       *
4631       * @return bool Whether the query is for a post or page preview.
4632       */
4633  	public function is_preview() {
4634          return (bool) $this->is_preview;
4635      }
4636  
4637      /**
4638       * Determines whether the query is for the robots.txt file.
4639       *
4640       * @since 3.1.0
4641       *
4642       * @return bool Whether the query is for the robots.txt file.
4643       */
4644  	public function is_robots() {
4645          return (bool) $this->is_robots;
4646      }
4647  
4648      /**
4649       * Determines whether the query is for the favicon.ico file.
4650       *
4651       * @since 5.4.0
4652       *
4653       * @return bool Whether the query is for the favicon.ico file.
4654       */
4655  	public function is_favicon() {
4656          return (bool) $this->is_favicon;
4657      }
4658  
4659      /**
4660       * Determines whether the query is for a sitemap.
4661       *
4662       * @since 7.1.0
4663       *
4664       * @return bool Whether the query is for a sitemap.
4665       */
4666  	public function is_sitemap(): bool {
4667          return $this->is_sitemap;
4668      }
4669  
4670      /**
4671       * Determines whether the query is for a search.
4672       *
4673       * @since 3.1.0
4674       *
4675       * @return bool Whether the query is for a search.
4676       */
4677  	public function is_search() {
4678          return (bool) $this->is_search;
4679      }
4680  
4681      /**
4682       * Determines whether the query is for an existing single post.
4683       *
4684       * Works for any post type excluding pages.
4685       *
4686       * If the $post parameter is specified, this function will additionally
4687       * check if the query is for one of the Posts specified.
4688       *
4689       * @since 3.1.0
4690       *
4691       * @see WP_Query::is_page()
4692       * @see WP_Query::is_singular()
4693       *
4694       * @param int|string|int[]|string[] $post Optional. Post ID, title, slug, path, or array of such
4695       *                                        to check against. Default empty.
4696       * @return bool Whether the query is for an existing single post.
4697       */
4698  	public function is_single( $post = '' ) {
4699          if ( ! $this->is_single ) {
4700              return false;
4701          }
4702  
4703          if ( empty( $post ) ) {
4704              return true;
4705          }
4706  
4707          $post_obj = $this->get_queried_object();
4708          if ( ! $post_obj ) {
4709              return false;
4710          }
4711  
4712          $post = array_map( 'strval', (array) $post );
4713  
4714          if ( in_array( (string) $post_obj->ID, $post, true ) ) {
4715              return true;
4716          } elseif ( in_array( $post_obj->post_title, $post, true ) ) {
4717              return true;
4718          } elseif ( in_array( $post_obj->post_name, $post, true ) ) {
4719              return true;
4720          } else {
4721              foreach ( $post as $postpath ) {
4722                  if ( ! strpos( $postpath, '/' ) ) {
4723                      continue;
4724                  }
4725  
4726                  $postpath_obj = get_page_by_path( $postpath, OBJECT, $post_obj->post_type );
4727  
4728                  if ( $postpath_obj && ( $postpath_obj->ID === $post_obj->ID ) ) {
4729                      return true;
4730                  }
4731              }
4732          }
4733          return false;
4734      }
4735  
4736      /**
4737       * Determines whether the query is for an existing single post of any post type
4738       * (post, attachment, page, custom post types).
4739       *
4740       * If the $post_types parameter is specified, this function will additionally
4741       * check if the query is for one of the Posts Types specified.
4742       *
4743       * @since 3.1.0
4744       *
4745       * @see WP_Query::is_page()
4746       * @see WP_Query::is_single()
4747       *
4748       * @param string|string[] $post_types Optional. Post type or array of post types
4749       *                                    to check against. Default empty.
4750       * @return bool Whether the query is for an existing single post
4751       *              or any of the given post types.
4752       */
4753  	public function is_singular( $post_types = '' ) {
4754          if ( empty( $post_types ) || ! $this->is_singular ) {
4755              return (bool) $this->is_singular;
4756          }
4757  
4758          $post_obj = $this->get_queried_object();
4759          if ( ! $post_obj ) {
4760              return false;
4761          }
4762  
4763          return in_array( $post_obj->post_type, (array) $post_types, true );
4764      }
4765  
4766      /**
4767       * Determines whether the query is for a specific time.
4768       *
4769       * @since 3.1.0
4770       *
4771       * @return bool Whether the query is for a specific time.
4772       */
4773  	public function is_time() {
4774          return (bool) $this->is_time;
4775      }
4776  
4777      /**
4778       * Determines whether the query is for a trackback endpoint call.
4779       *
4780       * @since 3.1.0
4781       *
4782       * @return bool Whether the query is for a trackback endpoint call.
4783       */
4784  	public function is_trackback() {
4785          return (bool) $this->is_trackback;
4786      }
4787  
4788      /**
4789       * Determines whether the query is for an existing year archive.
4790       *
4791       * @since 3.1.0
4792       *
4793       * @return bool Whether the query is for an existing year archive.
4794       */
4795  	public function is_year() {
4796          return (bool) $this->is_year;
4797      }
4798  
4799      /**
4800       * Determines whether the query is a 404 (returns no results).
4801       *
4802       * @since 3.1.0
4803       *
4804       * @return bool Whether the query is a 404 error.
4805       */
4806  	public function is_404() {
4807          return (bool) $this->is_404;
4808      }
4809  
4810      /**
4811       * Determines whether the query is for an embedded post.
4812       *
4813       * @since 4.4.0
4814       *
4815       * @return bool Whether the query is for an embedded post.
4816       */
4817  	public function is_embed() {
4818          return (bool) $this->is_embed;
4819      }
4820  
4821      /**
4822       * Determines whether the query is the main query.
4823       *
4824       * @since 3.3.0
4825       *
4826       * @global WP_Query $wp_the_query WordPress Query object.
4827       *
4828       * @return bool Whether the query is the main query.
4829       */
4830  	public function is_main_query() {
4831          global $wp_the_query;
4832          return $wp_the_query === $this;
4833      }
4834  
4835      /**
4836       * Sets up global post data.
4837       *
4838       * @since 4.1.0
4839       * @since 4.4.0 Added the ability to pass a post ID to `$post`.
4840       *
4841       * @global int     $id
4842       * @global WP_User $authordata
4843       * @global string  $currentday
4844       * @global string  $currentmonth
4845       * @global int     $page
4846       * @global array   $pages
4847       * @global int     $multipage
4848       * @global int     $more
4849       * @global int     $numpages
4850       *
4851       * @param WP_Post|object|int $post WP_Post instance or Post ID/object.
4852       * @return bool True on success, false on failure.
4853       */
4854  	public function setup_postdata( $post ) {
4855          global $id, $authordata, $currentday, $currentmonth, $page, $pages, $multipage, $more, $numpages;
4856  
4857          if ( ! ( $post instanceof WP_Post ) ) {
4858              $post = get_post( $post );
4859          }
4860  
4861          if ( ! $post ) {
4862              return false;
4863          }
4864  
4865          $elements = $this->generate_postdata( $post );
4866          if ( false === $elements ) {
4867              return false;
4868          }
4869  
4870          $id           = $elements['id'];
4871          $authordata   = $elements['authordata'];
4872          $currentday   = $elements['currentday'];
4873          $currentmonth = $elements['currentmonth'];
4874          $page         = $elements['page'];
4875          $pages        = $elements['pages'];
4876          $multipage    = $elements['multipage'];
4877          $more         = $elements['more'];
4878          $numpages     = $elements['numpages'];
4879  
4880          /**
4881           * Fires once the post data has been set up.
4882           *
4883           * @since 2.8.0
4884           * @since 4.1.0 Introduced `$query` parameter.
4885           *
4886           * @param WP_Post  $post  The Post object (passed by reference).
4887           * @param WP_Query $query The current Query object (passed by reference).
4888           */
4889          do_action_ref_array( 'the_post', array( &$post, &$this ) );
4890  
4891          return true;
4892      }
4893  
4894      /**
4895       * Generates post data.
4896       *
4897       * @since 5.2.0
4898       *
4899       * @param WP_Post|object|int $post WP_Post instance or Post ID/object.
4900       * @return array|false Elements of post or false on failure.
4901       */
4902  	public function generate_postdata( $post ) {
4903  
4904          if ( ! ( $post instanceof WP_Post ) ) {
4905              $post = get_post( $post );
4906          }
4907  
4908          if ( ! $post ) {
4909              return false;
4910          }
4911  
4912          $id = (int) $post->ID;
4913  
4914          $authordata = get_userdata( $post->post_author );
4915  
4916          $currentday   = false;
4917          $currentmonth = false;
4918  
4919          $post_date = $post->post_date;
4920          if ( ! empty( $post_date ) && '0000-00-00 00:00:00' !== $post_date ) {
4921              // Avoid using mysql2date for performance reasons.
4922              $currentmonth = substr( $post_date, 5, 2 );
4923              $day          = substr( $post_date, 8, 2 );
4924              $year         = substr( $post_date, 2, 2 );
4925  
4926              $currentday = sprintf( '%s.%s.%s', $day, $currentmonth, $year );
4927          }
4928  
4929          $numpages  = 1;
4930          $multipage = 0;
4931          $page      = $this->get( 'page' );
4932          if ( ! $page ) {
4933              $page = 1;
4934          }
4935  
4936          /*
4937           * Force full post content when viewing the permalink for the $post,
4938           * or when on an RSS feed. Otherwise respect the 'more' tag.
4939           */
4940          if ( get_queried_object_id() === $post->ID && ( $this->is_page() || $this->is_single() ) ) {
4941              $more = 1;
4942          } elseif ( $this->is_feed() ) {
4943              $more = 1;
4944          } else {
4945              $more = 0;
4946          }
4947  
4948          $content = $post->post_content;
4949          if ( str_contains( $content, '<!--nextpage-->' ) ) {
4950              $content = str_replace( "\n<!--nextpage-->\n", '<!--nextpage-->', $content );
4951              $content = str_replace( "\n<!--nextpage-->", '<!--nextpage-->', $content );
4952              $content = str_replace( "<!--nextpage-->\n", '<!--nextpage-->', $content );
4953  
4954              // Remove the nextpage block delimiters, to avoid invalid block structures in the split content.
4955              $content = str_replace( '<!-- wp:nextpage -->', '', $content );
4956              $content = str_replace( '<!-- /wp:nextpage -->', '', $content );
4957  
4958              // Ignore nextpage at the beginning of the content.
4959              if ( str_starts_with( $content, '<!--nextpage-->' ) ) {
4960                  $content = substr( $content, 15 );
4961              }
4962  
4963              $pages = explode( '<!--nextpage-->', $content );
4964          } else {
4965              $pages = array( $post->post_content );
4966          }
4967  
4968          /**
4969           * Filters the "pages" derived from splitting the post content.
4970           *
4971           * "Pages" are determined by splitting the post content based on the presence
4972           * of `<!-- nextpage -->` tags.
4973           *
4974           * @since 4.4.0
4975           *
4976           * @param string[] $pages Array of "pages" from the post content split by `<!-- nextpage -->` tags.
4977           * @param WP_Post  $post  Current post object.
4978           */
4979          $pages = apply_filters( 'content_pagination', $pages, $post );
4980  
4981          $numpages = count( $pages );
4982  
4983          if ( $numpages > 1 ) {
4984              if ( $page > 1 ) {
4985                  $more = 1;
4986              }
4987              $multipage = 1;
4988          } else {
4989              $multipage = 0;
4990          }
4991  
4992          $elements = compact( 'id', 'authordata', 'currentday', 'currentmonth', 'page', 'pages', 'multipage', 'more', 'numpages' );
4993  
4994          return $elements;
4995      }
4996  
4997      /**
4998       * Generates cache key.
4999       *
5000       * @since 6.1.0
5001       *
5002       * @global wpdb $wpdb WordPress database abstraction object.
5003       *
5004       * @param array  $args Query arguments.
5005       * @param string $sql  SQL statement.
5006       * @return string Cache key.
5007       */
5008  	protected function generate_cache_key( array $args, $sql ) {
5009          global $wpdb;
5010  
5011          unset(
5012              $args['cache_results'],
5013              $args['fields'],
5014              $args['lazy_load_term_meta'],
5015              $args['update_post_meta_cache'],
5016              $args['update_post_term_cache'],
5017              $args['update_menu_item_cache'],
5018              $args['suppress_filters']
5019          );
5020  
5021          if ( empty( $args['post_type'] ) ) {
5022              if ( $this->is_attachment ) {
5023                  $args['post_type'] = 'attachment';
5024              } elseif ( $this->is_page ) {
5025                  $args['post_type'] = 'page';
5026              } else {
5027                  $args['post_type'] = 'post';
5028              }
5029          } elseif ( 'any' === $args['post_type'] ) {
5030              $args['post_type'] = array_values( get_post_types( array( 'exclude_from_search' => false ) ) );
5031          }
5032          $args['post_type'] = (array) $args['post_type'];
5033          // Sort post types to ensure same cache key generation.
5034          sort( $args['post_type'] );
5035  
5036          /*
5037           * Sort arrays that can be used for ordering prior to cache key generation.
5038           *
5039           * These arrays are sorted in the query generator for the purposes of the
5040           * WHERE clause but the arguments are not modified as they can be used for
5041           * the orderby clause.
5042           *
5043           * Their use in the orderby clause will generate a different SQL query so
5044           * they can be sorted for the cache key generation.
5045           */
5046          $sortable_arrays_with_int_values = array(
5047              'post__in',
5048              'post_parent__in',
5049          );
5050          foreach ( $sortable_arrays_with_int_values as $key ) {
5051              if ( isset( $args[ $key ] ) && is_array( $args[ $key ] ) ) {
5052                  $args[ $key ] = array_unique( array_map( 'absint', $args[ $key ] ) );
5053                  sort( $args[ $key ] );
5054              }
5055          }
5056  
5057          // Sort and unique the 'post_name__in' for cache key generation.
5058          if ( isset( $args['post_name__in'] ) && is_array( $args['post_name__in'] ) ) {
5059              $args['post_name__in'] = array_unique( $args['post_name__in'] );
5060              sort( $args['post_name__in'] );
5061          }
5062  
5063          if ( isset( $args['post_status'] ) ) {
5064              $args['post_status'] = (array) $args['post_status'];
5065              // Sort post status to ensure same cache key generation.
5066              sort( $args['post_status'] );
5067          }
5068  
5069          // Add a default orderby value of date to ensure same cache key generation.
5070          if ( ! isset( $args['orderby'] ) ) {
5071              $args['orderby'] = 'date';
5072          }
5073  
5074          $placeholder = $wpdb->placeholder_escape();
5075          array_walk_recursive(
5076              $args,
5077              /*
5078               * Replace wpdb placeholders with the string used in the database
5079               * query to avoid unreachable cache keys. This is necessary because
5080               * the placeholder is randomly generated in each request.
5081               *
5082               * $value is passed by reference to allow it to be modified.
5083               * array_walk_recursive() does not return an array.
5084               */
5085              static function ( &$value ) use ( $wpdb, $placeholder ) {
5086                  if ( is_string( $value ) && str_contains( $value, $placeholder ) ) {
5087                      $value = $wpdb->remove_placeholder_escape( $value );
5088                  }
5089              }
5090          );
5091  
5092          ksort( $args );
5093  
5094          // Replace wpdb placeholder in the SQL statement used by the cache key.
5095          $sql = $wpdb->remove_placeholder_escape( $sql );
5096          $key = md5( serialize( $args ) . $sql );
5097  
5098          $this->query_cache_key = "wp_query:$key";
5099          return $this->query_cache_key;
5100      }
5101  
5102      /**
5103       * After looping through a nested query, this function
5104       * restores the $post global to the current post in this query.
5105       *
5106       * @since 3.7.0
5107       *
5108       * @global WP_Post $post Global post object.
5109       */
5110  	public function reset_postdata() {
5111          if ( ! empty( $this->post ) ) {
5112              $GLOBALS['post'] = $this->post;
5113              $this->setup_postdata( $this->post );
5114          }
5115      }
5116  
5117      /**
5118       * Lazyloads term meta for posts in the loop.
5119       *
5120       * @since 4.4.0
5121       * @deprecated 4.5.0 See wp_queue_posts_for_term_meta_lazyload().
5122       *
5123       * @param mixed $check
5124       * @param int   $term_id
5125       * @return mixed
5126       */
5127  	public function lazyload_term_meta( $check, $term_id ) {
5128          _deprecated_function( __METHOD__, '4.5.0' );
5129          return $check;
5130      }
5131  
5132      /**
5133       * Lazyloads comment meta for comments in the loop.
5134       *
5135       * @since 4.4.0
5136       * @deprecated 4.5.0 See wp_lazyload_comment_meta().
5137       *
5138       * @param mixed $check
5139       * @param int   $comment_id
5140       * @return mixed
5141       */
5142  	public function lazyload_comment_meta( $check, $comment_id ) {
5143          _deprecated_function( __METHOD__, '4.5.0' );
5144          return $check;
5145      }
5146  }


Generated : Wed Jul 22 08:20:19 2026 Cross-referenced by PHPXref