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


Generated : Wed Sep 16 08:20:31 2026 Cross-referenced by PHPXref