[ Index ]

PHP Cross Reference of WordPress

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