[ Index ]

PHP Cross Reference of WordPress

title

Body

[close]

/wp-includes/ -> class-wp-query.php (source)

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


Generated: Wed Aug 12 01:00:03 2020 Cross-referenced by PHPXref 0.7.1