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


Generated: Sun Jan 24 01:00:03 2021 Cross-referenced by PHPXref 0.7.1