[ Index ]

PHP Cross Reference of WordPress

title

Body

[close]

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

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


Generated: Tue Jun 2 01:00:03 2020 Cross-referenced by PHPXref 0.7.1