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


Generated : Mon May 4 08:20:14 2026 Cross-referenced by PHPXref