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


Generated: Wed Jan 29 01:00:05 2020 Cross-referenced by PHPXref 0.7.1