[ Index ]

WordPress 5.4.1

[ Index ]     [ Classes ]     [ Functions ]     [ Variables ]     [ Constants ]     [ Statistics ]    

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      /**
1713       * Retrieve query variable.
1714       *
1715       * @since 1.5.0
1716       * @since 3.9.0 The `$default` argument was introduced.
1717       *
1718       * @param string $query_var Query variable key.
1719       * @param mixed  $default   Optional. Value to return if the query variable is not set. Default empty.
1720       * @return mixed Contents of the query variable.
1721       */
1722  	public function get( $query_var, $default = '' ) {
1723          if ( isset( $this->query_vars[ $query_var ] ) ) {
1724              return $this->query_vars[ $query_var ];
1725          }
1726  
1727          return $default;
1728      }
1729  
1730      /**
1731       * Set query variable.
1732       *
1733       * @since 1.5.0
1734       *
1735       * @param string $query_var Query variable key.
1736       * @param mixed  $value     Query variable value.
1737       */
1738  	public function set( $query_var, $value ) {
1739          $this->query_vars[ $query_var ] = $value;
1740      }
1741  
1742      /**
1743       * Retrieves an array of posts based on query variables.
1744       *
1745       * There are a few filters and actions that can be used to modify the post
1746       * database query.
1747       *
1748       * @since 1.5.0
1749       *
1750       * @return WP_Post[]|int[] Array of post objects or post IDs.
1751       */
1752  	public function get_posts() {
1753          global $wpdb;
1754  
1755          $this->parse_query();
1756  
1757          /**
1758           * Fires after the query variable object is created, but before the actual query is run.
1759           *
1760           * Note: If using conditional tags, use the method versions within the passed instance
1761           * (e.g. $this->is_main_query() instead of is_main_query()). This is because the functions
1762           * like is_main_query() test against the global $wp_query instance, not the passed one.
1763           *
1764           * @since 2.0.0
1765           *
1766           * @param WP_Query $this The WP_Query instance (passed by reference).
1767           */
1768          do_action_ref_array( 'pre_get_posts', array( &$this ) );
1769  
1770          // Shorthand.
1771          $q = &$this->query_vars;
1772  
1773          // Fill again in case 'pre_get_posts' unset some vars.
1774          $q = $this->fill_query_vars( $q );
1775  
1776          // Parse meta query.
1777          $this->meta_query = new WP_Meta_Query();
1778          $this->meta_query->parse_query_vars( $q );
1779  
1780          // Set a flag if a 'pre_get_posts' hook changed the query vars.
1781          $hash = md5( serialize( $this->query_vars ) );
1782          if ( $hash != $this->query_vars_hash ) {
1783              $this->query_vars_changed = true;
1784              $this->query_vars_hash    = $hash;
1785          }
1786          unset( $hash );
1787  
1788          // First let's clear some variables.
1789          $distinct         = '';
1790          $whichauthor      = '';
1791          $whichmimetype    = '';
1792          $where            = '';
1793          $limits           = '';
1794          $join             = '';
1795          $search           = '';
1796          $groupby          = '';
1797          $post_status_join = false;
1798          $page             = 1;
1799  
1800          if ( isset( $q['caller_get_posts'] ) ) {
1801              _deprecated_argument(
1802                  'WP_Query',
1803                  '3.1.0',
1804                  sprintf(
1805                      /* translators: 1: caller_get_posts, 2: ignore_sticky_posts */
1806                      __( '%1$s is deprecated. Use %2$s instead.' ),
1807                      '<code>caller_get_posts</code>',
1808                      '<code>ignore_sticky_posts</code>'
1809                  )
1810              );
1811  
1812              if ( ! isset( $q['ignore_sticky_posts'] ) ) {
1813                  $q['ignore_sticky_posts'] = $q['caller_get_posts'];
1814              }
1815          }
1816  
1817          if ( ! isset( $q['ignore_sticky_posts'] ) ) {
1818              $q['ignore_sticky_posts'] = false;
1819          }
1820  
1821          if ( ! isset( $q['suppress_filters'] ) ) {
1822              $q['suppress_filters'] = false;
1823          }
1824  
1825          if ( ! isset( $q['cache_results'] ) ) {
1826              if ( wp_using_ext_object_cache() ) {
1827                  $q['cache_results'] = false;
1828              } else {
1829                  $q['cache_results'] = true;
1830              }
1831          }
1832  
1833          if ( ! isset( $q['update_post_term_cache'] ) ) {
1834              $q['update_post_term_cache'] = true;
1835          }
1836  
1837          if ( ! isset( $q['lazy_load_term_meta'] ) ) {
1838              $q['lazy_load_term_meta'] = $q['update_post_term_cache'];
1839          }
1840  
1841          if ( ! isset( $q['update_post_meta_cache'] ) ) {
1842              $q['update_post_meta_cache'] = true;
1843          }
1844  
1845          if ( ! isset( $q['post_type'] ) ) {
1846              if ( $this->is_search ) {
1847                  $q['post_type'] = 'any';
1848              } else {
1849                  $q['post_type'] = '';
1850              }
1851          }
1852          $post_type = $q['post_type'];
1853          if ( empty( $q['posts_per_page'] ) ) {
1854              $q['posts_per_page'] = get_option( 'posts_per_page' );
1855          }
1856          if ( isset( $q['showposts'] ) && $q['showposts'] ) {
1857              $q['showposts']      = (int) $q['showposts'];
1858              $q['posts_per_page'] = $q['showposts'];
1859          }
1860          if ( ( isset( $q['posts_per_archive_page'] ) && 0 != $q['posts_per_archive_page'] ) && ( $this->is_archive || $this->is_search ) ) {
1861              $q['posts_per_page'] = $q['posts_per_archive_page'];
1862          }
1863          if ( ! isset( $q['nopaging'] ) ) {
1864              if ( -1 == $q['posts_per_page'] ) {
1865                  $q['nopaging'] = true;
1866              } else {
1867                  $q['nopaging'] = false;
1868              }
1869          }
1870  
1871          if ( $this->is_feed ) {
1872              // This overrides 'posts_per_page'.
1873              if ( ! empty( $q['posts_per_rss'] ) ) {
1874                  $q['posts_per_page'] = $q['posts_per_rss'];
1875              } else {
1876                  $q['posts_per_page'] = get_option( 'posts_per_rss' );
1877              }
1878              $q['nopaging'] = false;
1879          }
1880          $q['posts_per_page'] = (int) $q['posts_per_page'];
1881          if ( $q['posts_per_page'] < -1 ) {
1882              $q['posts_per_page'] = abs( $q['posts_per_page'] );
1883          } elseif ( 0 == $q['posts_per_page'] ) {
1884              $q['posts_per_page'] = 1;
1885          }
1886  
1887          if ( ! isset( $q['comments_per_page'] ) || 0 == $q['comments_per_page'] ) {
1888              $q['comments_per_page'] = get_option( 'comments_per_page' );
1889          }
1890  
1891          if ( $this->is_home && ( empty( $this->query ) || 'true' === $q['preview'] ) && ( 'page' === get_option( 'show_on_front' ) ) && get_option( 'page_on_front' ) ) {
1892              $this->is_page = true;
1893              $this->is_home = false;
1894              $q['page_id']  = get_option( 'page_on_front' );
1895          }
1896  
1897          if ( isset( $q['page'] ) ) {
1898              $q['page'] = trim( $q['page'], '/' );
1899              $q['page'] = absint( $q['page'] );
1900          }
1901  
1902          // If true, forcibly turns off SQL_CALC_FOUND_ROWS even when limits are present.
1903          if ( isset( $q['no_found_rows'] ) ) {
1904              $q['no_found_rows'] = (bool) $q['no_found_rows'];
1905          } else {
1906              $q['no_found_rows'] = false;
1907          }
1908  
1909          switch ( $q['fields'] ) {
1910              case 'ids':
1911                  $fields = "{$wpdb->posts}.ID";
1912                  break;
1913              case 'id=>parent':
1914                  $fields = "{$wpdb->posts}.ID, {$wpdb->posts}.post_parent";
1915                  break;
1916              default:
1917                  $fields = "{$wpdb->posts}.*";
1918          }
1919  
1920          if ( '' !== $q['menu_order'] ) {
1921              $where .= " AND {$wpdb->posts}.menu_order = " . $q['menu_order'];
1922          }
1923          // The "m" parameter is meant for months but accepts datetimes of varying specificity.
1924          if ( $q['m'] ) {
1925              $where .= " AND YEAR({$wpdb->posts}.post_date)=" . substr( $q['m'], 0, 4 );
1926              if ( strlen( $q['m'] ) > 5 ) {
1927                  $where .= " AND MONTH({$wpdb->posts}.post_date)=" . substr( $q['m'], 4, 2 );
1928              }
1929              if ( strlen( $q['m'] ) > 7 ) {
1930                  $where .= " AND DAYOFMONTH({$wpdb->posts}.post_date)=" . substr( $q['m'], 6, 2 );
1931              }
1932              if ( strlen( $q['m'] ) > 9 ) {
1933                  $where .= " AND HOUR({$wpdb->posts}.post_date)=" . substr( $q['m'], 8, 2 );
1934              }
1935              if ( strlen( $q['m'] ) > 11 ) {
1936                  $where .= " AND MINUTE({$wpdb->posts}.post_date)=" . substr( $q['m'], 10, 2 );
1937              }
1938              if ( strlen( $q['m'] ) > 13 ) {
1939                  $where .= " AND SECOND({$wpdb->posts}.post_date)=" . substr( $q['m'], 12, 2 );
1940              }
1941          }
1942  
1943          // Handle the other individual date parameters.
1944          $date_parameters = array();
1945  
1946          if ( '' !== $q['hour'] ) {
1947              $date_parameters['hour'] = $q['hour'];
1948          }
1949  
1950          if ( '' !== $q['minute'] ) {
1951              $date_parameters['minute'] = $q['minute'];
1952          }
1953  
1954          if ( '' !== $q['second'] ) {
1955              $date_parameters['second'] = $q['second'];
1956          }
1957  
1958          if ( $q['year'] ) {
1959              $date_parameters['year'] = $q['year'];
1960          }
1961  
1962          if ( $q['monthnum'] ) {
1963              $date_parameters['monthnum'] = $q['monthnum'];
1964          }
1965  
1966          if ( $q['w'] ) {
1967              $date_parameters['week'] = $q['w'];
1968          }
1969  
1970          if ( $q['day'] ) {
1971              $date_parameters['day'] = $q['day'];
1972          }
1973  
1974          if ( $date_parameters ) {
1975              $date_query = new WP_Date_Query( array( $date_parameters ) );
1976              $where     .= $date_query->get_sql();
1977          }
1978          unset( $date_parameters, $date_query );
1979  
1980          // Handle complex date queries.
1981          if ( ! empty( $q['date_query'] ) ) {
1982              $this->date_query = new WP_Date_Query( $q['date_query'] );
1983              $where           .= $this->date_query->get_sql();
1984          }
1985  
1986          // If we've got a post_type AND it's not "any" post_type.
1987          if ( ! empty( $q['post_type'] ) && 'any' != $q['post_type'] ) {
1988              foreach ( (array) $q['post_type'] as $_post_type ) {
1989                  $ptype_obj = get_post_type_object( $_post_type );
1990                  if ( ! $ptype_obj || ! $ptype_obj->query_var || empty( $q[ $ptype_obj->query_var ] ) ) {
1991                      continue;
1992                  }
1993  
1994                  if ( ! $ptype_obj->hierarchical ) {
1995                      // Non-hierarchical post types can directly use 'name'.
1996                      $q['name'] = $q[ $ptype_obj->query_var ];
1997                  } else {
1998                      // Hierarchical post types will operate through 'pagename'.
1999                      $q['pagename'] = $q[ $ptype_obj->query_var ];
2000                      $q['name']     = '';
2001                  }
2002  
2003                  // Only one request for a slug is possible, this is why name & pagename are overwritten above.
2004                  break;
2005              } // End foreach.
2006              unset( $ptype_obj );
2007          }
2008  
2009          if ( '' !== $q['title'] ) {
2010              $where .= $wpdb->prepare( " AND {$wpdb->posts}.post_title = %s", stripslashes( $q['title'] ) );
2011          }
2012  
2013          // Parameters related to 'post_name'.
2014          if ( '' != $q['name'] ) {
2015              $q['name'] = sanitize_title_for_query( $q['name'] );
2016              $where    .= " AND {$wpdb->posts}.post_name = '" . $q['name'] . "'";
2017          } elseif ( '' != $q['pagename'] ) {
2018              if ( isset( $this->queried_object_id ) ) {
2019                  $reqpage = $this->queried_object_id;
2020              } else {
2021                  if ( 'page' != $q['post_type'] ) {
2022                      foreach ( (array) $q['post_type'] as $_post_type ) {
2023                          $ptype_obj = get_post_type_object( $_post_type );
2024                          if ( ! $ptype_obj || ! $ptype_obj->hierarchical ) {
2025                              continue;
2026                          }
2027  
2028                          $reqpage = get_page_by_path( $q['pagename'], OBJECT, $_post_type );
2029                          if ( $reqpage ) {
2030                              break;
2031                          }
2032                      }
2033                      unset( $ptype_obj );
2034                  } else {
2035                      $reqpage = get_page_by_path( $q['pagename'] );
2036                  }
2037                  if ( ! empty( $reqpage ) ) {
2038                      $reqpage = $reqpage->ID;
2039                  } else {
2040                      $reqpage = 0;
2041                  }
2042              }
2043  
2044              $page_for_posts = get_option( 'page_for_posts' );
2045              if ( ( 'page' != get_option( 'show_on_front' ) ) || empty( $page_for_posts ) || ( $reqpage != $page_for_posts ) ) {
2046                  $q['pagename'] = sanitize_title_for_query( wp_basename( $q['pagename'] ) );
2047                  $q['name']     = $q['pagename'];
2048                  $where        .= " AND ({$wpdb->posts}.ID = '$reqpage')";
2049                  $reqpage_obj   = get_post( $reqpage );
2050                  if ( is_object( $reqpage_obj ) && 'attachment' == $reqpage_obj->post_type ) {
2051                      $this->is_attachment = true;
2052                      $post_type           = 'attachment';
2053                      $q['post_type']      = 'attachment';
2054                      $this->is_page       = true;
2055                      $q['attachment_id']  = $reqpage;
2056                  }
2057              }
2058          } elseif ( '' != $q['attachment'] ) {
2059              $q['attachment'] = sanitize_title_for_query( wp_basename( $q['attachment'] ) );
2060              $q['name']       = $q['attachment'];
2061              $where          .= " AND {$wpdb->posts}.post_name = '" . $q['attachment'] . "'";
2062          } elseif ( is_array( $q['post_name__in'] ) && ! empty( $q['post_name__in'] ) ) {
2063              $q['post_name__in'] = array_map( 'sanitize_title_for_query', $q['post_name__in'] );
2064              $post_name__in      = "'" . implode( "','", $q['post_name__in'] ) . "'";
2065              $where             .= " AND {$wpdb->posts}.post_name IN ($post_name__in)";
2066          }
2067  
2068          // If an attachment is requested by number, let it supersede any post number.
2069          if ( $q['attachment_id'] ) {
2070              $q['p'] = absint( $q['attachment_id'] );
2071          }
2072  
2073          // If a post number is specified, load that post.
2074          if ( $q['p'] ) {
2075              $where .= " AND {$wpdb->posts}.ID = " . $q['p'];
2076          } elseif ( $q['post__in'] ) {
2077              $post__in = implode( ',', array_map( 'absint', $q['post__in'] ) );
2078              $where   .= " AND {$wpdb->posts}.ID IN ($post__in)";
2079          } elseif ( $q['post__not_in'] ) {
2080              $post__not_in = implode( ',', array_map( 'absint', $q['post__not_in'] ) );
2081              $where       .= " AND {$wpdb->posts}.ID NOT IN ($post__not_in)";
2082          }
2083  
2084          if ( is_numeric( $q['post_parent'] ) ) {
2085              $where .= $wpdb->prepare( " AND {$wpdb->posts}.post_parent = %d ", $q['post_parent'] );
2086          } elseif ( $q['post_parent__in'] ) {
2087              $post_parent__in = implode( ',', array_map( 'absint', $q['post_parent__in'] ) );
2088              $where          .= " AND {$wpdb->posts}.post_parent IN ($post_parent__in)";
2089          } elseif ( $q['post_parent__not_in'] ) {
2090              $post_parent__not_in = implode( ',', array_map( 'absint', $q['post_parent__not_in'] ) );
2091              $where              .= " AND {$wpdb->posts}.post_parent NOT IN ($post_parent__not_in)";
2092          }
2093  
2094          if ( $q['page_id'] ) {
2095              if ( ( 'page' !== get_option( 'show_on_front' ) ) || ( get_option( 'page_for_posts' ) != $q['page_id'] ) ) {
2096                  $q['p'] = $q['page_id'];
2097                  $where  = " AND {$wpdb->posts}.ID = " . $q['page_id'];
2098              }
2099          }
2100  
2101          // If a search pattern is specified, load the posts that match.
2102          if ( strlen( $q['s'] ) ) {
2103              $search = $this->parse_search( $q );
2104          }
2105  
2106          if ( ! $q['suppress_filters'] ) {
2107              /**
2108               * Filters the search SQL that is used in the WHERE clause of WP_Query.
2109               *
2110               * @since 3.0.0
2111               *
2112               * @param string   $search Search SQL for WHERE clause.
2113               * @param WP_Query $this   The current WP_Query object.
2114               */
2115              $search = apply_filters_ref_array( 'posts_search', array( $search, &$this ) );
2116          }
2117  
2118          // Taxonomies.
2119          if ( ! $this->is_singular ) {
2120              $this->parse_tax_query( $q );
2121  
2122              $clauses = $this->tax_query->get_sql( $wpdb->posts, 'ID' );
2123  
2124              $join  .= $clauses['join'];
2125              $where .= $clauses['where'];
2126          }
2127  
2128          if ( $this->is_tax ) {
2129              if ( empty( $post_type ) ) {
2130                  // Do a fully inclusive search for currently registered post types of queried taxonomies.
2131                  $post_type  = array();
2132                  $taxonomies = array_keys( $this->tax_query->queried_terms );
2133                  foreach ( get_post_types( array( 'exclude_from_search' => false ) ) as $pt ) {
2134                      $object_taxonomies = 'attachment' === $pt ? get_taxonomies_for_attachments() : get_object_taxonomies( $pt );
2135                      if ( array_intersect( $taxonomies, $object_taxonomies ) ) {
2136                          $post_type[] = $pt;
2137                      }
2138                  }
2139                  if ( ! $post_type ) {
2140                      $post_type = 'any';
2141                  } elseif ( count( $post_type ) == 1 ) {
2142                      $post_type = $post_type[0];
2143                  }
2144  
2145                  $post_status_join = true;
2146              } elseif ( in_array( 'attachment', (array) $post_type ) ) {
2147                  $post_status_join = true;
2148              }
2149          }
2150  
2151          /*
2152           * Ensure that 'taxonomy', 'term', 'term_id', 'cat', and
2153           * 'category_name' vars are set for backward compatibility.
2154           */
2155          if ( ! empty( $this->tax_query->queried_terms ) ) {
2156  
2157              /*
2158               * Set 'taxonomy', 'term', and 'term_id' to the
2159               * first taxonomy other than 'post_tag' or 'category'.
2160               */
2161              if ( ! isset( $q['taxonomy'] ) ) {
2162                  foreach ( $this->tax_query->queried_terms as $queried_taxonomy => $queried_items ) {
2163                      if ( empty( $queried_items['terms'][0] ) ) {
2164                          continue;
2165                      }
2166  
2167                      if ( ! in_array( $queried_taxonomy, array( 'category', 'post_tag' ) ) ) {
2168                          $q['taxonomy'] = $queried_taxonomy;
2169  
2170                          if ( 'slug' === $queried_items['field'] ) {
2171                              $q['term'] = $queried_items['terms'][0];
2172                          } else {
2173                              $q['term_id'] = $queried_items['terms'][0];
2174                          }
2175  
2176                          // Take the first one we find.
2177                          break;
2178                      }
2179                  }
2180              }
2181  
2182              // 'cat', 'category_name', 'tag_id'.
2183              foreach ( $this->tax_query->queried_terms as $queried_taxonomy => $queried_items ) {
2184                  if ( empty( $queried_items['terms'][0] ) ) {
2185                      continue;
2186                  }
2187  
2188                  if ( 'category' === $queried_taxonomy ) {
2189                      $the_cat = get_term_by( $queried_items['field'], $queried_items['terms'][0], 'category' );
2190                      if ( $the_cat ) {
2191                          $this->set( 'cat', $the_cat->term_id );
2192                          $this->set( 'category_name', $the_cat->slug );
2193                      }
2194                      unset( $the_cat );
2195                  }
2196  
2197                  if ( 'post_tag' === $queried_taxonomy ) {
2198                      $the_tag = get_term_by( $queried_items['field'], $queried_items['terms'][0], 'post_tag' );
2199                      if ( $the_tag ) {
2200                          $this->set( 'tag_id', $the_tag->term_id );
2201                      }
2202                      unset( $the_tag );
2203                  }
2204              }
2205          }
2206  
2207          if ( ! empty( $this->tax_query->queries ) || ! empty( $this->meta_query->queries ) ) {
2208              $groupby = "{$wpdb->posts}.ID";
2209          }
2210  
2211          // Author/user stuff.
2212  
2213          if ( ! empty( $q['author'] ) && '0' != $q['author'] ) {
2214              $q['author'] = addslashes_gpc( '' . urldecode( $q['author'] ) );
2215              $authors     = array_unique( array_map( 'intval', preg_split( '/[,\s]+/', $q['author'] ) ) );
2216              foreach ( $authors as $author ) {
2217                  $key         = $author > 0 ? 'author__in' : 'author__not_in';
2218                  $q[ $key ][] = abs( $author );
2219              }
2220              $q['author'] = implode( ',', $authors );
2221          }
2222  
2223          if ( ! empty( $q['author__not_in'] ) ) {
2224              $author__not_in = implode( ',', array_map( 'absint', array_unique( (array) $q['author__not_in'] ) ) );
2225              $where         .= " AND {$wpdb->posts}.post_author NOT IN ($author__not_in) ";
2226          } elseif ( ! empty( $q['author__in'] ) ) {
2227              $author__in = implode( ',', array_map( 'absint', array_unique( (array) $q['author__in'] ) ) );
2228              $where     .= " AND {$wpdb->posts}.post_author IN ($author__in) ";
2229          }
2230  
2231          // Author stuff for nice URLs.
2232  
2233          if ( '' != $q['author_name'] ) {
2234              if ( strpos( $q['author_name'], '/' ) !== false ) {
2235                  $q['author_name'] = explode( '/', $q['author_name'] );
2236                  if ( $q['author_name'][ count( $q['author_name'] ) - 1 ] ) {
2237                      $q['author_name'] = $q['author_name'][ count( $q['author_name'] ) - 1 ]; // No trailing slash.
2238                  } else {
2239                      $q['author_name'] = $q['author_name'][ count( $q['author_name'] ) - 2 ]; // There was a trailing slash.
2240                  }
2241              }
2242              $q['author_name'] = sanitize_title_for_query( $q['author_name'] );
2243              $q['author']      = get_user_by( 'slug', $q['author_name'] );
2244              if ( $q['author'] ) {
2245                  $q['author'] = $q['author']->ID;
2246              }
2247              $whichauthor .= " AND ({$wpdb->posts}.post_author = " . absint( $q['author'] ) . ')';
2248          }
2249  
2250          // Matching by comment count.
2251          if ( isset( $q['comment_count'] ) ) {
2252              // Numeric comment count is converted to array format.
2253              if ( is_numeric( $q['comment_count'] ) ) {
2254                  $q['comment_count'] = array(
2255                      'value' => intval( $q['comment_count'] ),
2256                  );
2257              }
2258  
2259              if ( isset( $q['comment_count']['value'] ) ) {
2260                  $q['comment_count'] = array_merge(
2261                      array(
2262                          'compare' => '=',
2263                      ),
2264                      $q['comment_count']
2265                  );
2266  
2267                  // Fallback for invalid compare operators is '='.
2268                  $compare_operators = array( '=', '!=', '>', '>=', '<', '<=' );
2269                  if ( ! in_array( $q['comment_count']['compare'], $compare_operators, true ) ) {
2270                      $q['comment_count']['compare'] = '=';
2271                  }
2272  
2273                  $where .= $wpdb->prepare( " AND {$wpdb->posts}.comment_count {$q['comment_count']['compare']} %d", $q['comment_count']['value'] );
2274              }
2275          }
2276  
2277          // MIME-Type stuff for attachment browsing.
2278  
2279          if ( isset( $q['post_mime_type'] ) && '' != $q['post_mime_type'] ) {
2280              $whichmimetype = wp_post_mime_type_where( $q['post_mime_type'], $wpdb->posts );
2281          }
2282          $where .= $search . $whichauthor . $whichmimetype;
2283  
2284          if ( ! empty( $this->meta_query->queries ) ) {
2285              $clauses = $this->meta_query->get_sql( 'post', $wpdb->posts, 'ID', $this );
2286              $join   .= $clauses['join'];
2287              $where  .= $clauses['where'];
2288          }
2289  
2290          $rand = ( isset( $q['orderby'] ) && 'rand' === $q['orderby'] );
2291          if ( ! isset( $q['order'] ) ) {
2292              $q['order'] = $rand ? '' : 'DESC';
2293          } else {
2294              $q['order'] = $rand ? '' : $this->parse_order( $q['order'] );
2295          }
2296  
2297          // These values of orderby should ignore the 'order' parameter.
2298          $force_asc = array( 'post__in', 'post_name__in', 'post_parent__in' );
2299          if ( isset( $q['orderby'] ) && in_array( $q['orderby'], $force_asc, true ) ) {
2300              $q['order'] = '';
2301          }
2302  
2303          // Order by.
2304          if ( empty( $q['orderby'] ) ) {
2305              /*
2306               * Boolean false or empty array blanks out ORDER BY,
2307               * while leaving the value unset or otherwise empty sets the default.
2308               */
2309              if ( isset( $q['orderby'] ) && ( is_array( $q['orderby'] ) || false === $q['orderby'] ) ) {
2310                  $orderby = '';
2311              } else {
2312                  $orderby = "{$wpdb->posts}.post_date " . $q['order'];
2313              }
2314          } elseif ( 'none' == $q['orderby'] ) {
2315              $orderby = '';
2316          } else {
2317              $orderby_array = array();
2318              if ( is_array( $q['orderby'] ) ) {
2319                  foreach ( $q['orderby'] as $_orderby => $order ) {
2320                      $orderby = addslashes_gpc( urldecode( $_orderby ) );
2321                      $parsed  = $this->parse_orderby( $orderby );
2322  
2323                      if ( ! $parsed ) {
2324                          continue;
2325                      }
2326  
2327                      $orderby_array[] = $parsed . ' ' . $this->parse_order( $order );
2328                  }
2329                  $orderby = implode( ', ', $orderby_array );
2330  
2331              } else {
2332                  $q['orderby'] = urldecode( $q['orderby'] );
2333                  $q['orderby'] = addslashes_gpc( $q['orderby'] );
2334  
2335                  foreach ( explode( ' ', $q['orderby'] ) as $i => $orderby ) {
2336                      $parsed = $this->parse_orderby( $orderby );
2337                      // Only allow certain values for safety.
2338                      if ( ! $parsed ) {
2339                          continue;
2340                      }
2341  
2342                      $orderby_array[] = $parsed;
2343                  }
2344                  $orderby = implode( ' ' . $q['order'] . ', ', $orderby_array );
2345  
2346                  if ( empty( $orderby ) ) {
2347                      $orderby = "{$wpdb->posts}.post_date " . $q['order'];
2348                  } elseif ( ! empty( $q['order'] ) ) {
2349                      $orderby .= " {$q['order']}";
2350                  }
2351              }
2352          }
2353  
2354          // Order search results by relevance only when another "orderby" is not specified in the query.
2355          if ( ! empty( $q['s'] ) ) {
2356              $search_orderby = '';
2357              if ( ! empty( $q['search_orderby_title'] ) && ( empty( $q['orderby'] ) && ! $this->is_feed ) || ( isset( $q['orderby'] ) && 'relevance' === $q['orderby'] ) ) {
2358                  $search_orderby = $this->parse_search_order( $q );
2359              }
2360  
2361              if ( ! $q['suppress_filters'] ) {
2362                  /**
2363                   * Filters the ORDER BY used when ordering search results.
2364                   *
2365                   * @since 3.7.0
2366                   *
2367                   * @param string   $search_orderby The ORDER BY clause.
2368                   * @param WP_Query $this           The current WP_Query instance.
2369                   */
2370                  $search_orderby = apply_filters( 'posts_search_orderby', $search_orderby, $this );
2371              }
2372  
2373              if ( $search_orderby ) {
2374                  $orderby = $orderby ? $search_orderby . ', ' . $orderby : $search_orderby;
2375              }
2376          }
2377  
2378          if ( is_array( $post_type ) && count( $post_type ) > 1 ) {
2379              $post_type_cap = 'multiple_post_type';
2380          } else {
2381              if ( is_array( $post_type ) ) {
2382                  $post_type = reset( $post_type );
2383              }
2384              $post_type_object = get_post_type_object( $post_type );
2385              if ( empty( $post_type_object ) ) {
2386                  $post_type_cap = $post_type;
2387              }
2388          }
2389  
2390          if ( isset( $q['post_password'] ) ) {
2391              $where .= $wpdb->prepare( " AND {$wpdb->posts}.post_password = %s", $q['post_password'] );
2392              if ( empty( $q['perm'] ) ) {
2393                  $q['perm'] = 'readable';
2394              }
2395          } elseif ( isset( $q['has_password'] ) ) {
2396              $where .= sprintf( " AND {$wpdb->posts}.post_password %s ''", $q['has_password'] ? '!=' : '=' );
2397          }
2398  
2399          if ( ! empty( $q['comment_status'] ) ) {
2400              $where .= $wpdb->prepare( " AND {$wpdb->posts}.comment_status = %s ", $q['comment_status'] );
2401          }
2402  
2403          if ( ! empty( $q['ping_status'] ) ) {
2404              $where .= $wpdb->prepare( " AND {$wpdb->posts}.ping_status = %s ", $q['ping_status'] );
2405          }
2406  
2407          if ( 'any' == $post_type ) {
2408              $in_search_post_types = get_post_types( array( 'exclude_from_search' => false ) );
2409              if ( empty( $in_search_post_types ) ) {
2410                  $where .= ' AND 1=0 ';
2411              } else {
2412                  $where .= " AND {$wpdb->posts}.post_type IN ('" . join( "', '", array_map( 'esc_sql', $in_search_post_types ) ) . "')";
2413              }
2414          } elseif ( ! empty( $post_type ) && is_array( $post_type ) ) {
2415              $where .= " AND {$wpdb->posts}.post_type IN ('" . join( "', '", esc_sql( $post_type ) ) . "')";
2416          } elseif ( ! empty( $post_type ) ) {
2417              $where           .= $wpdb->prepare( " AND {$wpdb->posts}.post_type = %s", $post_type );
2418              $post_type_object = get_post_type_object( $post_type );
2419          } elseif ( $this->is_attachment ) {
2420              $where           .= " AND {$wpdb->posts}.post_type = 'attachment'";
2421              $post_type_object = get_post_type_object( 'attachment' );
2422          } elseif ( $this->is_page ) {
2423              $where           .= " AND {$wpdb->posts}.post_type = 'page'";
2424              $post_type_object = get_post_type_object( 'page' );
2425          } else {
2426              $where           .= " AND {$wpdb->posts}.post_type = 'post'";
2427              $post_type_object = get_post_type_object( 'post' );
2428          }
2429  
2430          $edit_cap = 'edit_post';
2431          $read_cap = 'read_post';
2432  
2433          if ( ! empty( $post_type_object ) ) {
2434              $edit_others_cap  = $post_type_object->cap->edit_others_posts;
2435              $read_private_cap = $post_type_object->cap->read_private_posts;
2436          } else {
2437              $edit_others_cap  = 'edit_others_' . $post_type_cap . 's';
2438              $read_private_cap = 'read_private_' . $post_type_cap . 's';
2439          }
2440  
2441          $user_id = get_current_user_id();
2442  
2443          $q_status = array();
2444          if ( ! empty( $q['post_status'] ) ) {
2445              $statuswheres = array();
2446              $q_status     = $q['post_status'];
2447              if ( ! is_array( $q_status ) ) {
2448                  $q_status = explode( ',', $q_status );
2449              }
2450              $r_status = array();
2451              $p_status = array();
2452              $e_status = array();
2453              if ( in_array( 'any', $q_status ) ) {
2454                  foreach ( get_post_stati( array( 'exclude_from_search' => true ) ) as $status ) {
2455                      if ( ! in_array( $status, $q_status ) ) {
2456                          $e_status[] = "{$wpdb->posts}.post_status <> '$status'";
2457                      }
2458                  }
2459              } else {
2460                  foreach ( get_post_stati() as $status ) {
2461                      if ( in_array( $status, $q_status ) ) {
2462                          if ( 'private' == $status ) {
2463                              $p_status[] = "{$wpdb->posts}.post_status = '$status'";
2464                          } else {
2465                              $r_status[] = "{$wpdb->posts}.post_status = '$status'";
2466                          }
2467                      }
2468                  }
2469              }
2470  
2471              if ( empty( $q['perm'] ) || 'readable' != $q['perm'] ) {
2472                  $r_status = array_merge( $r_status, $p_status );
2473                  unset( $p_status );
2474              }
2475  
2476              if ( ! empty( $e_status ) ) {
2477                  $statuswheres[] = '(' . join( ' AND ', $e_status ) . ')';
2478              }
2479              if ( ! empty( $r_status ) ) {
2480                  if ( ! empty( $q['perm'] ) && 'editable' == $q['perm'] && ! current_user_can( $edit_others_cap ) ) {
2481                      $statuswheres[] = "({$wpdb->posts}.post_author = $user_id " . 'AND (' . join( ' OR ', $r_status ) . '))';
2482                  } else {
2483                      $statuswheres[] = '(' . join( ' OR ', $r_status ) . ')';
2484                  }
2485              }
2486              if ( ! empty( $p_status ) ) {
2487                  if ( ! empty( $q['perm'] ) && 'readable' == $q['perm'] && ! current_user_can( $read_private_cap ) ) {
2488                      $statuswheres[] = "({$wpdb->posts}.post_author = $user_id " . 'AND (' . join( ' OR ', $p_status ) . '))';
2489                  } else {
2490                      $statuswheres[] = '(' . join( ' OR ', $p_status ) . ')';
2491                  }
2492              }
2493              if ( $post_status_join ) {
2494                  $join .= " LEFT JOIN {$wpdb->posts} AS p2 ON ({$wpdb->posts}.post_parent = p2.ID) ";
2495                  foreach ( $statuswheres as $index => $statuswhere ) {
2496                      $statuswheres[ $index ] = "($statuswhere OR ({$wpdb->posts}.post_status = 'inherit' AND " . str_replace( $wpdb->posts, 'p2', $statuswhere ) . '))';
2497                  }
2498              }
2499              $where_status = implode( ' OR ', $statuswheres );
2500              if ( ! empty( $where_status ) ) {
2501                  $where .= " AND ($where_status)";
2502              }
2503          } elseif ( ! $this->is_singular ) {
2504              $where .= " AND ({$wpdb->posts}.post_status = 'publish'";
2505  
2506              // Add public states.
2507              $public_states = get_post_stati( array( 'public' => true ) );
2508              foreach ( (array) $public_states as $state ) {
2509                  if ( 'publish' == $state ) { // Publish is hard-coded above.
2510                      continue;
2511                  }
2512                  $where .= " OR {$wpdb->posts}.post_status = '$state'";
2513              }
2514  
2515              if ( $this->is_admin ) {
2516                  // Add protected states that should show in the admin all list.
2517                  $admin_all_states = get_post_stati(
2518                      array(
2519                          'protected'              => true,
2520                          'show_in_admin_all_list' => true,
2521                      )
2522                  );
2523                  foreach ( (array) $admin_all_states as $state ) {
2524                      $where .= " OR {$wpdb->posts}.post_status = '$state'";
2525                  }
2526              }
2527  
2528              if ( is_user_logged_in() ) {
2529                  // Add private states that are limited to viewing by the author of a post or someone who has caps to read private states.
2530                  $private_states = get_post_stati( array( 'private' => true ) );
2531                  foreach ( (array) $private_states as $state ) {
2532                      $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'";
2533                  }
2534              }
2535  
2536              $where .= ')';
2537          }
2538  
2539          /*
2540           * Apply filters on where and join prior to paging so that any
2541           * manipulations to them are reflected in the paging by day queries.
2542           */
2543          if ( ! $q['suppress_filters'] ) {
2544              /**
2545               * Filters the WHERE clause of the query.
2546               *
2547               * @since 1.5.0
2548               *
2549               * @param string   $where The WHERE clause of the query.
2550               * @param WP_Query $this The WP_Query instance (passed by reference).
2551               */
2552              $where = apply_filters_ref_array( 'posts_where', array( $where, &$this ) );
2553  
2554              /**
2555               * Filters the JOIN clause of the query.
2556               *
2557               * @since 1.5.0
2558               *
2559               * @param string   $join  The JOIN clause of the query.
2560               * @param WP_Query $this The WP_Query instance (passed by reference).
2561               */
2562              $join = apply_filters_ref_array( 'posts_join', array( $join, &$this ) );
2563          }
2564  
2565          // Paging.
2566          if ( empty( $q['nopaging'] ) && ! $this->is_singular ) {
2567              $page = absint( $q['paged'] );
2568              if ( ! $page ) {
2569                  $page = 1;
2570              }
2571  
2572              // If 'offset' is provided, it takes precedence over 'paged'.
2573              if ( isset( $q['offset'] ) && is_numeric( $q['offset'] ) ) {
2574                  $q['offset'] = absint( $q['offset'] );
2575                  $pgstrt      = $q['offset'] . ', ';
2576              } else {
2577                  $pgstrt = absint( ( $page - 1 ) * $q['posts_per_page'] ) . ', ';
2578              }
2579              $limits = 'LIMIT ' . $pgstrt . $q['posts_per_page'];
2580          }
2581  
2582          // Comments feeds.
2583          if ( $this->is_comment_feed && ! $this->is_singular ) {
2584              if ( $this->is_archive || $this->is_search ) {
2585                  $cjoin    = "JOIN {$wpdb->posts} ON ({$wpdb->comments}.comment_post_ID = {$wpdb->posts}.ID) $join ";
2586                  $cwhere   = "WHERE comment_approved = '1' $where";
2587                  $cgroupby = "{$wpdb->comments}.comment_id";
2588              } else { // Other non-singular, e.g. front.
2589                  $cjoin    = "JOIN {$wpdb->posts} ON ( {$wpdb->comments}.comment_post_ID = {$wpdb->posts}.ID )";
2590                  $cwhere   = "WHERE ( post_status = 'publish' OR ( post_status = 'inherit' AND post_type = 'attachment' ) ) AND comment_approved = '1'";
2591                  $cgroupby = '';
2592              }
2593  
2594              if ( ! $q['suppress_filters'] ) {
2595                  /**
2596                   * Filters the JOIN clause of the comments feed query before sending.
2597                   *
2598                   * @since 2.2.0
2599                   *
2600                   * @param string   $cjoin The JOIN clause of the query.
2601                   * @param WP_Query $this The WP_Query instance (passed by reference).
2602                   */
2603                  $cjoin = apply_filters_ref_array( 'comment_feed_join', array( $cjoin, &$this ) );
2604  
2605                  /**
2606                   * Filters the WHERE clause of the comments feed query before sending.
2607                   *
2608                   * @since 2.2.0
2609                   *
2610                   * @param string   $cwhere The WHERE clause of the query.
2611                   * @param WP_Query $this   The WP_Query instance (passed by reference).
2612                   */
2613                  $cwhere = apply_filters_ref_array( 'comment_feed_where', array( $cwhere, &$this ) );
2614  
2615                  /**
2616                   * Filters the GROUP BY clause of the comments feed query before sending.
2617                   *
2618                   * @since 2.2.0
2619                   *
2620                   * @param string   $cgroupby The GROUP BY clause of the query.
2621                   * @param WP_Query $this     The WP_Query instance (passed by reference).
2622                   */
2623                  $cgroupby = apply_filters_ref_array( 'comment_feed_groupby', array( $cgroupby, &$this ) );
2624  
2625                  /**
2626                   * Filters the ORDER BY clause of the comments feed query before sending.
2627                   *
2628                   * @since 2.8.0
2629                   *
2630                   * @param string   $corderby The ORDER BY clause of the query.
2631                   * @param WP_Query $this     The WP_Query instance (passed by reference).
2632                   */
2633                  $corderby = apply_filters_ref_array( 'comment_feed_orderby', array( 'comment_date_gmt DESC', &$this ) );
2634  
2635                  /**
2636                   * Filters the LIMIT clause of the comments feed query before sending.
2637                   *
2638                   * @since 2.8.0
2639                   *
2640                   * @param string   $climits The JOIN clause of the query.
2641                   * @param WP_Query $this    The WP_Query instance (passed by reference).
2642                   */
2643                  $climits = apply_filters_ref_array( 'comment_feed_limits', array( 'LIMIT ' . get_option( 'posts_per_rss' ), &$this ) );
2644              }
2645  
2646              $cgroupby = ( ! empty( $cgroupby ) ) ? 'GROUP BY ' . $cgroupby : '';
2647              $corderby = ( ! empty( $corderby ) ) ? 'ORDER BY ' . $corderby : '';
2648              $climits  = ( ! empty( $climits ) ) ? $climits : '';
2649  
2650              $comments = (array) $wpdb->get_results( "SELECT $distinct {$wpdb->comments}.* FROM {$wpdb->comments} $cjoin $cwhere $cgroupby $corderby $climits" );
2651              // Convert to WP_Comment.
2652              $this->comments      = array_map( 'get_comment', $comments );
2653              $this->comment_count = count( $this->comments );
2654  
2655              $post_ids = array();
2656  
2657              foreach ( $this->comments as $comment ) {
2658                  $post_ids[] = (int) $comment->comment_post_ID;
2659              }
2660  
2661              $post_ids = join( ',', $post_ids );
2662              $join     = '';
2663              if ( $post_ids ) {
2664                  $where = "AND {$wpdb->posts}.ID IN ($post_ids) ";
2665              } else {
2666                  $where = 'AND 0';
2667              }
2668          }
2669  
2670          $pieces = array( 'where', 'groupby', 'join', 'orderby', 'distinct', 'fields', 'limits' );
2671  
2672          /*
2673           * Apply post-paging filters on where and join. Only plugins that
2674           * manipulate paging queries should use these hooks.
2675           */
2676          if ( ! $q['suppress_filters'] ) {
2677              /**
2678               * Filters the WHERE clause of the query.
2679               *
2680               * Specifically for manipulating paging queries.
2681               *
2682               * @since 1.5.0
2683               *
2684               * @param string   $where The WHERE clause of the query.
2685               * @param WP_Query $this The WP_Query instance (passed by reference).
2686               */
2687              $where = apply_filters_ref_array( 'posts_where_paged', array( $where, &$this ) );
2688  
2689              /**
2690               * Filters the GROUP BY clause of the query.
2691               *
2692               * @since 2.0.0
2693               *
2694               * @param string   $groupby The GROUP BY clause of the query.
2695               * @param WP_Query $this    The WP_Query instance (passed by reference).
2696               */
2697              $groupby = apply_filters_ref_array( 'posts_groupby', array( $groupby, &$this ) );
2698  
2699              /**
2700               * Filters the JOIN clause of the query.
2701               *
2702               * Specifically for manipulating paging queries.
2703               *
2704               * @since 1.5.0
2705               *
2706               * @param string   $join  The JOIN clause of the query.
2707               * @param WP_Query $this The WP_Query instance (passed by reference).
2708               */
2709              $join = apply_filters_ref_array( 'posts_join_paged', array( $join, &$this ) );
2710  
2711              /**
2712               * Filters the ORDER BY clause of the query.
2713               *
2714               * @since 1.5.1
2715               *
2716               * @param string   $orderby The ORDER BY clause of the query.
2717               * @param WP_Query $this    The WP_Query instance (passed by reference).
2718               */
2719              $orderby = apply_filters_ref_array( 'posts_orderby', array( $orderby, &$this ) );
2720  
2721              /**
2722               * Filters the DISTINCT clause of the query.
2723               *
2724               * @since 2.1.0
2725               *
2726               * @param string   $distinct The DISTINCT clause of the query.
2727               * @param WP_Query $this     The WP_Query instance (passed by reference).
2728               */
2729              $distinct = apply_filters_ref_array( 'posts_distinct', array( $distinct, &$this ) );
2730  
2731              /**
2732               * Filters the LIMIT clause of the query.
2733               *
2734               * @since 2.1.0
2735               *
2736               * @param string   $limits The LIMIT clause of the query.
2737               * @param WP_Query $this   The WP_Query instance (passed by reference).
2738               */
2739              $limits = apply_filters_ref_array( 'post_limits', array( $limits, &$this ) );
2740  
2741              /**
2742               * Filters the SELECT clause of the query.
2743               *
2744               * @since 2.1.0
2745               *
2746               * @param string   $fields The SELECT clause of the query.
2747               * @param WP_Query $this   The WP_Query instance (passed by reference).
2748               */
2749              $fields = apply_filters_ref_array( 'posts_fields', array( $fields, &$this ) );
2750  
2751              /**
2752               * Filters all query clauses at once, for convenience.
2753               *
2754               * Covers the WHERE, GROUP BY, JOIN, ORDER BY, DISTINCT,
2755               * fields (SELECT), and LIMITS clauses.
2756               *
2757               * @since 3.1.0
2758               *
2759               * @param string[] $clauses Associative array of the clauses for the query.
2760               * @param WP_Query $this    The WP_Query instance (passed by reference).
2761               */
2762              $clauses = (array) apply_filters_ref_array( 'posts_clauses', array( compact( $pieces ), &$this ) );
2763  
2764              $where    = isset( $clauses['where'] ) ? $clauses['where'] : '';
2765              $groupby  = isset( $clauses['groupby'] ) ? $clauses['groupby'] : '';
2766              $join     = isset( $clauses['join'] ) ? $clauses['join'] : '';
2767              $orderby  = isset( $clauses['orderby'] ) ? $clauses['orderby'] : '';
2768              $distinct = isset( $clauses['distinct'] ) ? $clauses['distinct'] : '';
2769              $fields   = isset( $clauses['fields'] ) ? $clauses['fields'] : '';
2770              $limits   = isset( $clauses['limits'] ) ? $clauses['limits'] : '';
2771          }
2772  
2773          /**
2774           * Fires to announce the query's current selection parameters.
2775           *
2776           * For use by caching plugins.
2777           *
2778           * @since 2.3.0
2779           *
2780           * @param string $selection The assembled selection query.
2781           */
2782          do_action( 'posts_selection', $where . $groupby . $orderby . $limits . $join );
2783  
2784          /*
2785           * Filters again for the benefit of caching plugins.
2786           * Regular plugins should use the hooks above.
2787           */
2788          if ( ! $q['suppress_filters'] ) {
2789              /**
2790               * Filters the WHERE clause of the query.
2791               *
2792               * For use by caching plugins.
2793               *
2794               * @since 2.5.0
2795               *
2796               * @param string   $where The WHERE clause of the query.
2797               * @param WP_Query $this The WP_Query instance (passed by reference).
2798               */
2799              $where = apply_filters_ref_array( 'posts_where_request', array( $where, &$this ) );
2800  
2801              /**
2802               * Filters the GROUP BY clause of the query.
2803               *
2804               * For use by caching plugins.
2805               *
2806               * @since 2.5.0
2807               *
2808               * @param string   $groupby The GROUP BY clause of the query.
2809               * @param WP_Query $this    The WP_Query instance (passed by reference).
2810               */
2811              $groupby = apply_filters_ref_array( 'posts_groupby_request', array( $groupby, &$this ) );
2812  
2813              /**
2814               * Filters the JOIN clause of the query.
2815               *
2816               * For use by caching plugins.
2817               *
2818               * @since 2.5.0
2819               *
2820               * @param string   $join  The JOIN clause of the query.
2821               * @param WP_Query $this The WP_Query instance (passed by reference).
2822               */
2823              $join = apply_filters_ref_array( 'posts_join_request', array( $join, &$this ) );
2824  
2825              /**
2826               * Filters the ORDER BY clause of the query.
2827               *
2828               * For use by caching plugins.
2829               *
2830               * @since 2.5.0
2831               *
2832               * @param string   $orderby The ORDER BY clause of the query.
2833               * @param WP_Query $this    The WP_Query instance (passed by reference).
2834               */
2835              $orderby = apply_filters_ref_array( 'posts_orderby_request', array( $orderby, &$this ) );
2836  
2837              /**
2838               * Filters the DISTINCT clause of the query.
2839               *
2840               * For use by caching plugins.
2841               *
2842               * @since 2.5.0
2843               *
2844               * @param string   $distinct The DISTINCT clause of the query.
2845               * @param WP_Query $this     The WP_Query instance (passed by reference).
2846               */
2847              $distinct = apply_filters_ref_array( 'posts_distinct_request', array( $distinct, &$this ) );
2848  
2849              /**
2850               * Filters the SELECT clause of the query.
2851               *
2852               * For use by caching plugins.
2853               *
2854               * @since 2.5.0
2855               *
2856               * @param string   $fields The SELECT clause of the query.
2857               * @param WP_Query $this   The WP_Query instance (passed by reference).
2858               */
2859              $fields = apply_filters_ref_array( 'posts_fields_request', array( $fields, &$this ) );
2860  
2861              /**
2862               * Filters the LIMIT clause of the query.
2863               *
2864               * For use by caching plugins.
2865               *
2866               * @since 2.5.0
2867               *
2868               * @param string   $limits The LIMIT clause of the query.
2869               * @param WP_Query $this   The WP_Query instance (passed by reference).
2870               */
2871              $limits = apply_filters_ref_array( 'post_limits_request', array( $limits, &$this ) );
2872  
2873              /**
2874               * Filters all query clauses at once, for convenience.
2875               *
2876               * For use by caching plugins.
2877               *
2878               * Covers the WHERE, GROUP BY, JOIN, ORDER BY, DISTINCT,
2879               * fields (SELECT), and LIMITS clauses.
2880               *
2881               * @since 3.1.0
2882               *
2883               * @param string[] $pieces Associative array of the pieces of the query.
2884               * @param WP_Query $this   The WP_Query instance (passed by reference).
2885               */
2886              $clauses = (array) apply_filters_ref_array( 'posts_clauses_request', array( compact( $pieces ), &$this ) );
2887  
2888              $where    = isset( $clauses['where'] ) ? $clauses['where'] : '';
2889              $groupby  = isset( $clauses['groupby'] ) ? $clauses['groupby'] : '';
2890              $join     = isset( $clauses['join'] ) ? $clauses['join'] : '';
2891              $orderby  = isset( $clauses['orderby'] ) ? $clauses['orderby'] : '';
2892              $distinct = isset( $clauses['distinct'] ) ? $clauses['distinct'] : '';
2893              $fields   = isset( $clauses['fields'] ) ? $clauses['fields'] : '';
2894              $limits   = isset( $clauses['limits'] ) ? $clauses['limits'] : '';
2895          }
2896  
2897          if ( ! empty( $groupby ) ) {
2898              $groupby = 'GROUP BY ' . $groupby;
2899          }
2900          if ( ! empty( $orderby ) ) {
2901              $orderby = 'ORDER BY ' . $orderby;
2902          }
2903  
2904          $found_rows = '';
2905          if ( ! $q['no_found_rows'] && ! empty( $limits ) ) {
2906              $found_rows = 'SQL_CALC_FOUND_ROWS';
2907          }
2908  
2909          $old_request   = "SELECT $found_rows $distinct $fields FROM {$wpdb->posts} $join WHERE 1=1 $where $groupby $orderby $limits";
2910          $this->request = $old_request;
2911  
2912          if ( ! $q['suppress_filters'] ) {
2913              /**
2914               * Filters the completed SQL query before sending.
2915               *
2916               * @since 2.0.0
2917               *
2918               * @param string   $request The complete SQL query.
2919               * @param WP_Query $this    The WP_Query instance (passed by reference).
2920               */
2921              $this->request = apply_filters_ref_array( 'posts_request', array( $this->request, &$this ) );
2922          }
2923  
2924          /**
2925           * Filters the posts array before the query takes place.
2926           *
2927           * Return a non-null value to bypass WordPress's default post queries.
2928           *
2929           * Filtering functions that require pagination information are encouraged to set
2930           * the `found_posts` and `max_num_pages` properties of the WP_Query object,
2931           * passed to the filter by reference. If WP_Query does not perform a database
2932           * query, it will not have enough information to generate these values itself.
2933           *
2934           * @since 4.6.0
2935           *
2936           * @param array|null $posts Return an array of post data to short-circuit WP's query,
2937           *                          or null to allow WP to run its normal queries.
2938           * @param WP_Query   $this  The WP_Query instance (passed by reference).
2939           */
2940          $this->posts = apply_filters_ref_array( 'posts_pre_query', array( null, &$this ) );
2941  
2942          if ( 'ids' == $q['fields'] ) {
2943              if ( null === $this->posts ) {
2944                  $this->posts = $wpdb->get_col( $this->request );
2945              }
2946  
2947              $this->posts      = array_map( 'intval', $this->posts );
2948              $this->post_count = count( $this->posts );
2949              $this->set_found_posts( $q, $limits );
2950  
2951              return $this->posts;
2952          }
2953  
2954          if ( 'id=>parent' == $q['fields'] ) {
2955              if ( null === $this->posts ) {
2956                  $this->posts = $wpdb->get_results( $this->request );
2957              }
2958  
2959              $this->post_count = count( $this->posts );
2960              $this->set_found_posts( $q, $limits );
2961  
2962              $r = array();
2963              foreach ( $this->posts as $key => $post ) {
2964                  $this->posts[ $key ]->ID          = (int) $post->ID;
2965                  $this->posts[ $key ]->post_parent = (int) $post->post_parent;
2966  
2967                  $r[ (int) $post->ID ] = (int) $post->post_parent;
2968              }
2969  
2970              return $r;
2971          }
2972  
2973          if ( null === $this->posts ) {
2974              $split_the_query = ( $old_request == $this->request && "{$wpdb->posts}.*" == $fields && ! empty( $limits ) && $q['posts_per_page'] < 500 );
2975  
2976              /**
2977               * Filters whether to split the query.
2978               *
2979               * Splitting the query will cause it to fetch just the IDs of the found posts
2980               * (and then individually fetch each post by ID), rather than fetching every
2981               * complete row at once. One massive result vs. many small results.
2982               *
2983               * @since 3.4.0
2984               *
2985               * @param bool     $split_the_query Whether or not to split the query.
2986               * @param WP_Query $this            The WP_Query instance.
2987               */
2988              $split_the_query = apply_filters( 'split_the_query', $split_the_query, $this );
2989  
2990              if ( $split_the_query ) {
2991                  // First get the IDs and then fill in the objects.
2992  
2993                  $this->request = "SELECT $found_rows $distinct {$wpdb->posts}.ID FROM {$wpdb->posts} $join WHERE 1=1 $where $groupby $orderby $limits";
2994  
2995                  /**
2996                   * Filters the Post IDs SQL request before sending.
2997                   *
2998                   * @since 3.4.0
2999                   *
3000                   * @param string   $request The post ID request.
3001                   * @param WP_Query $this    The WP_Query instance.
3002                   */
3003                  $this->request = apply_filters( 'posts_request_ids', $this->request, $this );
3004  
3005                  $ids = $wpdb->get_col( $this->request );
3006  
3007                  if ( $ids ) {
3008                      $this->posts = $ids;
3009                      $this->set_found_posts( $q, $limits );
3010                      _prime_post_caches( $ids, $q['update_post_term_cache'], $q['update_post_meta_cache'] );
3011                  } else {
3012                      $this->posts = array();
3013                  }
3014              } else {
3015                  $this->posts = $wpdb->get_results( $this->request );
3016                  $this->set_found_posts( $q, $limits );
3017              }
3018          }
3019  
3020          // Convert to WP_Post objects.
3021          if ( $this->posts ) {
3022              $this->posts = array_map( 'get_post', $this->posts );
3023          }
3024  
3025          if ( ! $q['suppress_filters'] ) {
3026              /**
3027               * Filters the raw post results array, prior to status checks.
3028               *
3029               * @since 2.3.0
3030               *
3031               * @param WP_Post[] $posts Array of post objects.
3032               * @param WP_Query  $this  The WP_Query instance (passed by reference).
3033               */
3034              $this->posts = apply_filters_ref_array( 'posts_results', array( $this->posts, &$this ) );
3035          }
3036  
3037          if ( ! empty( $this->posts ) && $this->is_comment_feed && $this->is_singular ) {
3038              /** This filter is documented in wp-includes/query.php */
3039              $cjoin = apply_filters_ref_array( 'comment_feed_join', array( '', &$this ) );
3040  
3041              /** This filter is documented in wp-includes/query.php */
3042              $cwhere = apply_filters_ref_array( 'comment_feed_where', array( "WHERE comment_post_ID = '{$this->posts[0]->ID}' AND comment_approved = '1'", &$this ) );
3043  
3044              /** This filter is documented in wp-includes/query.php */
3045              $cgroupby = apply_filters_ref_array( 'comment_feed_groupby', array( '', &$this ) );
3046              $cgroupby = ( ! empty( $cgroupby ) ) ? 'GROUP BY ' . $cgroupby : '';
3047  
3048              /** This filter is documented in wp-includes/query.php */
3049              $corderby = apply_filters_ref_array( 'comment_feed_orderby', array( 'comment_date_gmt DESC', &$this ) );
3050              $corderby = ( ! empty( $corderby ) ) ? 'ORDER BY ' . $corderby : '';
3051  
3052              /** This filter is documented in wp-includes/query.php */
3053              $climits = apply_filters_ref_array( 'comment_feed_limits', array( 'LIMIT ' . get_option( 'posts_per_rss' ), &$this ) );
3054  
3055              $comments_request = "SELECT {$wpdb->comments}.* FROM {$wpdb->comments} $cjoin $cwhere $cgroupby $corderby $climits";
3056              $comments         = $wpdb->get_results( $comments_request );
3057              // Convert to WP_Comment.
3058              $this->comments      = array_map( 'get_comment', $comments );
3059              $this->comment_count = count( $this->comments );
3060          }
3061  
3062          // Check post status to determine if post should be displayed.
3063          if ( ! empty( $this->posts ) && ( $this->is_single || $this->is_page ) ) {
3064              $status = get_post_status( $this->posts[0] );
3065  
3066              if ( 'attachment' === $this->posts[0]->post_type && 0 === (int) $this->posts[0]->post_parent ) {
3067                  $this->is_page       = false;
3068                  $this->is_single     = true;
3069                  $this->is_attachment = true;
3070              }
3071  
3072              // If the post_status was specifically requested, let it pass through.
3073              if ( ! in_array( $status, $q_status ) ) {
3074                  $post_status_obj = get_post_status_object( $status );
3075  
3076                  if ( $post_status_obj && ! $post_status_obj->public ) {
3077                      if ( ! is_user_logged_in() ) {
3078                          // User must be logged in to view unpublished posts.
3079                          $this->posts = array();
3080                      } else {
3081                          if ( $post_status_obj->protected ) {
3082                              // User must have edit permissions on the draft to preview.
3083                              if ( ! current_user_can( $edit_cap, $this->posts[0]->ID ) ) {
3084                                  $this->posts = array();
3085                              } else {
3086                                  $this->is_preview = true;
3087                                  if ( 'future' != $status ) {
3088                                      $this->posts[0]->post_date = current_time( 'mysql' );
3089                                  }
3090                              }
3091                          } elseif ( $post_status_obj->private ) {
3092                              if ( ! current_user_can( $read_cap, $this->posts[0]->ID ) ) {
3093                                  $this->posts = array();
3094                              }
3095                          } else {
3096                              $this->posts = array();
3097                          }
3098                      }
3099                  } elseif ( ! $post_status_obj ) {
3100                      // Post status is not registered, assume it's not public.
3101                      if ( ! current_user_can( $edit_cap, $this->posts[0]->ID ) ) {
3102                          $this->posts = array();
3103                      }
3104                  }
3105              }
3106  
3107              if ( $this->is_preview && $this->posts && current_user_can( $edit_cap, $this->posts[0]->ID ) ) {
3108                  /**
3109                   * Filters the single post for preview mode.
3110                   *
3111                   * @since 2.7.0
3112                   *
3113                   * @param WP_Post  $post_preview  The Post object.
3114                   * @param WP_Query $this          The WP_Query instance (passed by reference).
3115                   */
3116                  $this->posts[0] = get_post( apply_filters_ref_array( 'the_preview', array( $this->posts[0], &$this ) ) );
3117              }
3118          }
3119  
3120          // Put sticky posts at the top of the posts array.
3121          $sticky_posts = get_option( 'sticky_posts' );
3122          if ( $this->is_home && $page <= 1 && is_array( $sticky_posts ) && ! empty( $sticky_posts ) && ! $q['ignore_sticky_posts'] ) {
3123              $num_posts     = count( $this->posts );
3124              $sticky_offset = 0;
3125              // Loop over posts and relocate stickies to the front.
3126              for ( $i = 0; $i < $num_posts; $i++ ) {
3127                  if ( in_array( $this->posts[ $i ]->ID, $sticky_posts ) ) {
3128                      $sticky_post = $this->posts[ $i ];
3129                      // Remove sticky from current position.
3130                      array_splice( $this->posts, $i, 1 );
3131                      // Move to front, after other stickies.
3132                      array_splice( $this->posts, $sticky_offset, 0, array( $sticky_post ) );
3133                      // Increment the sticky offset. The next sticky will be placed at this offset.
3134                      $sticky_offset++;
3135                      // Remove post from sticky posts array.
3136                      $offset = array_search( $sticky_post->ID, $sticky_posts );
3137                      unset( $sticky_posts[ $offset ] );
3138                  }
3139              }
3140  
3141              // If any posts have been excluded specifically, Ignore those that are sticky.
3142              if ( ! empty( $sticky_posts ) && ! empty( $q['post__not_in'] ) ) {
3143                  $sticky_posts = array_diff( $sticky_posts, $q['post__not_in'] );
3144              }
3145  
3146              // Fetch sticky posts that weren't in the query results.
3147              if ( ! empty( $sticky_posts ) ) {
3148                  $stickies = get_posts(
3149                      array(
3150                          'post__in'    => $sticky_posts,
3151                          'post_type'   => $post_type,
3152                          'post_status' => 'publish',
3153                          'nopaging'    => true,
3154                      )
3155                  );
3156  
3157                  foreach ( $stickies as $sticky_post ) {
3158                      array_splice( $this->posts, $sticky_offset, 0, array( $sticky_post ) );
3159                      $sticky_offset++;
3160                  }
3161              }
3162          }
3163  
3164          // If comments have been fetched as part of the query, make sure comment meta lazy-loading is set up.
3165          if ( ! empty( $this->comments ) ) {
3166              wp_queue_comments_for_comment_meta_lazyload( $this->comments );
3167          }
3168  
3169          if ( ! $q['suppress_filters'] ) {
3170              /**
3171               * Filters the array of retrieved posts after they've been fetched and
3172               * internally processed.
3173               *
3174               * @since 1.5.0
3175               *
3176               * @param WP_Post[] $posts Array of post objects.
3177               * @param WP_Query  $this The WP_Query instance (passed by reference).
3178               */
3179              $this->posts = apply_filters_ref_array( 'the_posts', array( $this->posts, &$this ) );
3180          }
3181  
3182          // Ensure that any posts added/modified via one of the filters above are
3183          // of the type WP_Post and are filtered.
3184          if ( $this->posts ) {
3185              $this->post_count = count( $this->posts );
3186  
3187              $this->posts = array_map( 'get_post', $this->posts );
3188  
3189              if ( $q['cache_results'] ) {
3190                  update_post_caches( $this->posts, $post_type, $q['update_post_term_cache'], $q['update_post_meta_cache'] );
3191              }
3192  
3193              $this->post = reset( $this->posts );
3194          } else {
3195              $this->post_count = 0;
3196              $this->posts      = array();
3197          }
3198  
3199          if ( $q['lazy_load_term_meta'] ) {
3200              wp_queue_posts_for_term_meta_lazyload( $this->posts );
3201          }
3202  
3203          return $this->posts;
3204      }
3205  
3206      /**
3207       * Set up the amount of found posts and the number of pages (if limit clause was used)
3208       * for the current query.
3209       *
3210       * @since 3.5.0
3211       *
3212       * @param array  $q      Query variables.
3213       * @param string $limits LIMIT clauses of the query.
3214       */
3215  	private function set_found_posts( $q, $limits ) {
3216          global $wpdb;
3217          // Bail if posts is an empty array. Continue if posts is an empty string,
3218          // null, or false to accommodate caching plugins that fill posts later.
3219          if ( $q['no_found_rows'] || ( is_array( $this->posts ) && ! $this->posts ) ) {
3220              return;
3221          }
3222  
3223          if ( ! empty( $limits ) ) {
3224              /**
3225               * Filters the query to run for retrieving the found posts.
3226               *
3227               * @since 2.1.0
3228               *
3229               * @param string   $found_posts The query to run to find the found posts.
3230               * @param WP_Query $this        The WP_Query instance (passed by reference).
3231               */
3232              $this->found_posts = $wpdb->get_var( apply_filters_ref_array( 'found_posts_query', array( 'SELECT FOUND_ROWS()', &$this ) ) );
3233          } else {
3234              if ( is_array( $this->posts ) ) {
3235                  $this->found_posts = count( $this->posts );
3236              } else {
3237                  if ( null === $this->posts ) {
3238                      $this->found_posts = 0;
3239                  } else {
3240                      $this->found_posts = 1;
3241                  }
3242              }
3243          }
3244  
3245          /**
3246           * Filters the number of found posts for the query.
3247           *
3248           * @since 2.1.0
3249           *
3250           * @param int      $found_posts The number of posts found.
3251           * @param WP_Query $this        The WP_Query instance (passed by reference).
3252           */
3253          $this->found_posts = apply_filters_ref_array( 'found_posts', array( $this->found_posts, &$this ) );
3254  
3255          if ( ! empty( $limits ) ) {
3256              $this->max_num_pages = ceil( $this->found_posts / $q['posts_per_page'] );
3257          }
3258      }
3259  
3260      /**
3261       * Set up the next post and iterate current post index.
3262       *
3263       * @since 1.5.0
3264       *
3265       * @return WP_Post Next post.
3266       */
3267  	public function next_post() {
3268  
3269          $this->current_post++;
3270  
3271          $this->post = $this->posts[ $this->current_post ];
3272          return $this->post;
3273      }
3274  
3275      /**
3276       * Sets up the current post.
3277       *
3278       * Retrieves the next post, sets up the post, sets the 'in the loop'
3279       * property to true.
3280       *
3281       * @since 1.5.0
3282       *
3283       * @global WP_Post $post Global post object.
3284       */
3285  	public function the_post() {
3286          global $post;
3287          $this->in_the_loop = true;
3288  
3289          if ( -1 == $this->current_post ) { // Loop has just started.
3290              /**
3291               * Fires once the loop is started.
3292               *
3293               * @since 2.0.0
3294               *
3295               * @param WP_Query $this The WP_Query instance (passed by reference).
3296               */
3297              do_action_ref_array( 'loop_start', array( &$this ) );
3298          }
3299  
3300          $post = $this->next_post();
3301          $this->setup_postdata( $post );
3302      }
3303  
3304      /**
3305       * Determines whether there are more posts available in the loop.
3306       *
3307       * Calls the {@see 'loop_end'} action when the loop is complete.
3308       *
3309       * @since 1.5.0
3310       *
3311       * @return bool True if posts are available, false if end of loop.
3312       */
3313  	public function have_posts() {
3314          if ( $this->current_post + 1 < $this->post_count ) {
3315              return true;
3316          } elseif ( $this->current_post + 1 == $this->post_count && $this->post_count > 0 ) {
3317              /**
3318               * Fires once the loop has ended.
3319               *
3320               * @since 2.0.0
3321               *
3322               * @param WP_Query $this The WP_Query instance (passed by reference).
3323               */
3324              do_action_ref_array( 'loop_end', array( &$this ) );
3325              // Do some cleaning up after the loop.
3326              $this->rewind_posts();
3327          } elseif ( 0 === $this->post_count ) {
3328              /**
3329               * Fires if no results are found in a post query.
3330               *
3331               * @since 4.9.0
3332               *
3333               * @param WP_Query $this The WP_Query instance.
3334               */
3335              do_action( 'loop_no_results', $this );
3336          }
3337  
3338          $this->in_the_loop = false;
3339          return false;
3340      }
3341  
3342      /**
3343       * Rewind the posts and reset post index.
3344       *
3345       * @since 1.5.0
3346       */
3347  	public function rewind_posts() {
3348          $this->current_post = -1;
3349          if ( $this->post_count > 0 ) {
3350              $this->post = $this->posts[0];
3351          }
3352      }
3353  
3354      /**
3355       * Iterate current comment index and return WP_Comment object.
3356       *
3357       * @since 2.2.0
3358       *
3359       * @return WP_Comment Comment object.
3360       */
3361  	public function next_comment() {
3362          $this->current_comment++;
3363  
3364          $this->comment = $this->comments[ $this->current_comment ];
3365          return $this->comment;
3366      }
3367  
3368      /**
3369       * Sets up the current comment.
3370       *
3371       * @since 2.2.0
3372       * @global WP_Comment $comment Global comment object.
3373       */
3374  	public function the_comment() {
3375          global $comment;
3376  
3377          $comment = $this->next_comment();
3378  
3379          if ( 0 == $this->current_comment ) {
3380              /**
3381               * Fires once the comment loop is started.
3382               *
3383               * @since 2.2.0
3384               */
3385              do_action( 'comment_loop_start' );
3386          }
3387      }
3388  
3389      /**
3390       * Whether there are more comments available.
3391       *
3392       * Automatically rewinds comments when finished.
3393       *
3394       * @since 2.2.0
3395       *
3396       * @return bool True, if more comments. False, if no more posts.
3397       */
3398  	public function have_comments() {
3399          if ( $this->current_comment + 1 < $this->comment_count ) {
3400              return true;
3401          } elseif ( $this->current_comment + 1 == $this->comment_count ) {
3402              $this->rewind_comments();
3403          }
3404  
3405          return false;
3406      }
3407  
3408      /**
3409       * Rewind the comments, resets the comment index and comment to first.
3410       *
3411       * @since 2.2.0
3412       */
3413  	public function rewind_comments() {
3414          $this->current_comment = -1;
3415          if ( $this->comment_count > 0 ) {
3416              $this->comment = $this->comments[0];
3417          }
3418      }
3419  
3420      /**
3421       * Sets up the WordPress query by parsing query string.
3422       *
3423       * @since 1.5.0
3424       *
3425       * @param string|array $query URL query string or array of query arguments.
3426       * @return WP_Post[]|int[] Array of post objects or post IDs.
3427       */
3428  	public function query( $query ) {
3429          $this->init();
3430          $this->query      = wp_parse_args( $query );
3431          $this->query_vars = $this->query;
3432          return $this->get_posts();
3433      }
3434  
3435      /**
3436       * Retrieve queried object.
3437       *
3438       * If queried object is not set, then the queried object will be set from
3439       * the category, tag, taxonomy, posts page, single post, page, or author
3440       * query variable. After it is set up, it will be returned.
3441       *
3442       * @since 1.5.0
3443       *
3444       * @return object
3445       */
3446  	public function get_queried_object() {
3447          if ( isset( $this->queried_object ) ) {
3448              return $this->queried_object;
3449          }
3450  
3451          $this->queried_object    = null;
3452          $this->queried_object_id = null;
3453  
3454          if ( $this->is_category || $this->is_tag || $this->is_tax ) {
3455              if ( $this->is_category ) {
3456                  if ( $this->get( 'cat' ) ) {
3457                      $term = get_term( $this->get( 'cat' ), 'category' );
3458                  } elseif ( $this->get( 'category_name' ) ) {
3459                      $term = get_term_by( 'slug', $this->get( 'category_name' ), 'category' );
3460                  }
3461              } elseif ( $this->is_tag ) {
3462                  if ( $this->get( 'tag_id' ) ) {
3463                      $term = get_term( $this->get( 'tag_id' ), 'post_tag' );
3464                  } elseif ( $this->get( 'tag' ) ) {
3465                      $term = get_term_by( 'slug', $this->get( 'tag' ), 'post_tag' );
3466                  }
3467              } else {
3468                  // For other tax queries, grab the first term from the first clause.
3469                  if ( ! empty( $this->tax_query->queried_terms ) ) {
3470                      $queried_taxonomies = array_keys( $this->tax_query->queried_terms );
3471                      $matched_taxonomy   = reset( $queried_taxonomies );
3472                      $query              = $this->tax_query->queried_terms[ $matched_taxonomy ];
3473  
3474                      if ( ! empty( $query['terms'] ) ) {
3475                          if ( 'term_id' == $query['field'] ) {
3476                              $term = get_term( reset( $query['terms'] ), $matched_taxonomy );
3477                          } else {
3478                              $term = get_term_by( $query['field'], reset( $query['terms'] ), $matched_taxonomy );
3479                          }
3480                      }
3481                  }
3482              }
3483  
3484              if ( ! empty( $term ) && ! is_wp_error( $term ) ) {
3485                  $this->queried_object    = $term;
3486                  $this->queried_object_id = (int) $term->term_id;
3487  
3488                  if ( $this->is_category && 'category' === $this->queried_object->taxonomy ) {
3489                      _make_cat_compat( $this->queried_object );
3490                  }
3491              }
3492          } elseif ( $this->is_post_type_archive ) {
3493              $post_type = $this->get( 'post_type' );
3494              if ( is_array( $post_type ) ) {
3495                  $post_type = reset( $post_type );
3496              }
3497              $this->queried_object = get_post_type_object( $post_type );
3498          } elseif ( $this->is_posts_page ) {
3499              $page_for_posts          = get_option( 'page_for_posts' );
3500              $this->queried_object    = get_post( $page_for_posts );
3501              $this->queried_object_id = (int) $this->queried_object->ID;
3502          } elseif ( $this->is_singular && ! empty( $this->post ) ) {
3503              $this->queried_object    = $this->post;
3504              $this->queried_object_id = (int) $this->post->ID;
3505          } elseif ( $this->is_author ) {
3506              $this->queried_object_id = (int) $this->get( 'author' );
3507              $this->queried_object    = get_userdata( $this->queried_object_id );
3508          }
3509  
3510          return $this->queried_object;
3511      }
3512  
3513      /**
3514       * Retrieve ID of the current queried object.
3515       *
3516       * @since 1.5.0
3517       *
3518       * @return int
3519       */
3520  	public function get_queried_object_id() {
3521          $this->get_queried_object();
3522  
3523          if ( isset( $this->queried_object_id ) ) {
3524              return $this->queried_object_id;
3525          }
3526  
3527          return 0;
3528      }
3529  
3530      /**
3531       * Constructor.
3532       *
3533       * Sets up the WordPress query, if parameter is not empty.
3534       *
3535       * @since 1.5.0
3536       *
3537       * @param string|array $query URL query string or array of vars.
3538       */
3539  	public function __construct( $query = '' ) {
3540          if ( ! empty( $query ) ) {
3541              $this->query( $query );
3542          }
3543      }
3544  
3545      /**
3546       * Make private properties readable for backward compatibility.
3547       *
3548       * @since 4.0.0
3549       *
3550       * @param string $name Property to get.
3551       * @return mixed Property.
3552       */
3553  	public function __get( $name ) {
3554          if ( in_array( $name, $this->compat_fields ) ) {
3555              return $this->$name;
3556          }
3557      }
3558  
3559      /**
3560       * Make private properties checkable for backward compatibility.
3561       *
3562       * @since 4.0.0
3563       *
3564       * @param string $name Property to check if set.
3565       * @return bool Whether the property is set.
3566       */
3567  	public function __isset( $name ) {
3568          if ( in_array( $name, $this->compat_fields ) ) {
3569              return isset( $this->$name );
3570          }
3571      }
3572  
3573      /**
3574       * Make private/protected methods readable for backward compatibility.
3575       *
3576       * @since 4.0.0
3577       *
3578       * @param string   $name      Method to call.
3579       * @param array    $arguments Arguments to pass when calling.
3580       * @return mixed|false Return value of the callback, false otherwise.
3581       */
3582  	public function __call( $name, $arguments ) {
3583          if ( in_array( $name, $this->compat_methods ) ) {
3584              return $this->$name( ...$arguments );
3585          }
3586          return false;
3587      }
3588  
3589      /**
3590       * Is the query for an existing archive page?
3591       *
3592       * Month, Year, Category, Author, Post Type archive...
3593       *
3594       * @since 3.1.0
3595       *
3596       * @return bool
3597       */
3598  	public function is_archive() {
3599          return (bool) $this->is_archive;
3600      }
3601  
3602      /**
3603       * Is the query for an existing post type archive page?
3604       *
3605       * @since 3.1.0
3606       *
3607       * @param string|string[] $post_types Optional. Post type or array of posts types
3608       *                                    to check against. Default empty.
3609       * @return bool
3610       */
3611  	public function is_post_type_archive( $post_types = '' ) {
3612          if ( empty( $post_types ) || ! $this->is_post_type_archive ) {
3613              return (bool) $this->is_post_type_archive;
3614          }
3615  
3616          $post_type = $this->get( 'post_type' );
3617          if ( is_array( $post_type ) ) {
3618              $post_type = reset( $post_type );
3619          }
3620          $post_type_object = get_post_type_object( $post_type );
3621  
3622          return in_array( $post_type_object->name, (array) $post_types );
3623      }
3624  
3625      /**
3626       * Is the query for an existing attachment page?
3627       *
3628       * @since 3.1.0
3629       *
3630       * @param int|string|int[]|string[] $attachment Optional. Attachment ID, title, slug, or array of such
3631       *                                              to check against. Default empty.
3632       * @return bool
3633       */
3634  	public function is_attachment( $attachment = '' ) {
3635          if ( ! $this->is_attachment ) {
3636              return false;
3637          }
3638  
3639          if ( empty( $attachment ) ) {
3640              return true;
3641          }
3642  
3643          $attachment = array_map( 'strval', (array) $attachment );
3644  
3645          $post_obj = $this->get_queried_object();
3646  
3647          if ( in_array( (string) $post_obj->ID, $attachment ) ) {
3648              return true;
3649          } elseif ( in_array( $post_obj->post_title, $attachment ) ) {
3650              return true;
3651          } elseif ( in_array( $post_obj->post_name, $attachment ) ) {
3652              return true;
3653          }
3654          return false;
3655      }
3656  
3657      /**
3658       * Is the query for an existing author archive page?
3659       *
3660       * If the $author parameter is specified, this function will additionally
3661       * check if the query is for one of the authors specified.
3662       *
3663       * @since 3.1.0
3664       *
3665       * @param int|string|int[]|string[] $author Optional. User ID, nickname, nicename, or array of such
3666       *                                          to check against. Default empty.
3667       * @return bool
3668       */
3669  	public function is_author( $author = '' ) {
3670          if ( ! $this->is_author ) {
3671              return false;
3672          }
3673  
3674          if ( empty( $author ) ) {
3675              return true;
3676          }
3677  
3678          $author_obj = $this->get_queried_object();
3679  
3680          $author = array_map( 'strval', (array) $author );
3681  
3682          if ( in_array( (string) $author_obj->ID, $author ) ) {
3683              return true;
3684          } elseif ( in_array( $author_obj->nickname, $author ) ) {
3685              return true;
3686          } elseif ( in_array( $author_obj->user_nicename, $author ) ) {
3687              return true;
3688          }
3689  
3690          return false;
3691      }
3692  
3693      /**
3694       * Is the query for an existing category archive page?
3695       *
3696       * If the $category parameter is specified, this function will additionally
3697       * check if the query is for one of the categories specified.
3698       *
3699       * @since 3.1.0
3700       *
3701       * @param int|string|int[]|string[] $category Optional. Category ID, name, slug, or array of such
3702       *                                            to check against. Default empty.
3703       * @return bool
3704       */
3705  	public function is_category( $category = '' ) {
3706          if ( ! $this->is_category ) {
3707              return false;
3708          }
3709  
3710          if ( empty( $category ) ) {
3711              return true;
3712          }
3713  
3714          $cat_obj = $this->get_queried_object();
3715  
3716          $category = array_map( 'strval', (array) $category );
3717  
3718          if ( in_array( (string) $cat_obj->term_id, $category ) ) {
3719              return true;
3720          } elseif ( in_array( $cat_obj->name, $category ) ) {
3721              return true;
3722          } elseif ( in_array( $cat_obj->slug, $category ) ) {
3723              return true;
3724          }
3725  
3726          return false;
3727      }
3728  
3729      /**
3730       * Is the query for an existing tag archive page?
3731       *
3732       * If the $tag parameter is specified, this function will additionally
3733       * check if the query is for one of the tags specified.
3734       *
3735       * @since 3.1.0
3736       *
3737       * @param int|string|int[]|string[] $tag Optional. Tag ID, name, slug, or array of such
3738       *                                       to check against. Default empty.
3739       * @return bool
3740       */
3741  	public function is_tag( $tag = '' ) {
3742          if ( ! $this->is_tag ) {
3743              return false;
3744          }
3745  
3746          if ( empty( $tag ) ) {
3747              return true;
3748          }
3749  
3750          $tag_obj = $this->get_queried_object();
3751  
3752          $tag = array_map( 'strval', (array) $tag );
3753  
3754          if ( in_array( (string) $tag_obj->term_id, $tag ) ) {
3755              return true;
3756          } elseif ( in_array( $tag_obj->name, $tag ) ) {
3757              return true;
3758          } elseif ( in_array( $tag_obj->slug, $tag ) ) {
3759              return true;
3760          }
3761  
3762          return false;
3763      }
3764  
3765      /**
3766       * Is the query for an existing custom taxonomy archive page?
3767       *
3768       * If the $taxonomy parameter is specified, this function will additionally
3769       * check if the query is for that specific $taxonomy.
3770       *
3771       * If the $term parameter is specified in addition to the $taxonomy parameter,
3772       * this function will additionally check if the query is for one of the terms
3773       * specified.
3774       *
3775       * @since 3.1.0
3776       *
3777       * @global array $wp_taxonomies
3778       *
3779       * @param string|string[]           $taxonomy Optional. Taxonomy slug or slugs to check against.
3780       *                                            Default empty.
3781       * @param int|string|int[]|string[] $term     Optional. Term ID, name, slug, or array of such
3782       *                                            to check against. Default empty.
3783       * @return bool True for custom taxonomy archive pages, false for built-in taxonomies
3784       *              (category and tag archives).
3785       */
3786  	public function is_tax( $taxonomy = '', $term = '' ) {
3787          global $wp_taxonomies;
3788  
3789          if ( ! $this->is_tax ) {
3790              return false;
3791          }
3792  
3793          if ( empty( $taxonomy ) ) {
3794              return true;
3795          }
3796  
3797          $queried_object = $this->get_queried_object();
3798          $tax_array      = array_intersect( array_keys( $wp_taxonomies ), (array) $taxonomy );
3799          $term_array     = (array) $term;
3800  
3801          // Check that the taxonomy matches.
3802          if ( ! ( isset( $queried_object->taxonomy ) && count( $tax_array ) && in_array( $queried_object->taxonomy, $tax_array ) ) ) {
3803              return false;
3804          }
3805  
3806          // Only a taxonomy provided.
3807          if ( empty( $term ) ) {
3808              return true;
3809          }
3810  
3811          return isset( $queried_object->term_id ) &&
3812              count(
3813                  array_intersect(
3814                      array( $queried_object->term_id, $queried_object->name, $queried_object->slug ),
3815                      $term_array
3816                  )
3817              );
3818      }
3819  
3820      /**
3821       * Whether the current URL is within the comments popup window.
3822       *
3823       * @since 3.1.0
3824       * @deprecated 4.5.0
3825       *
3826       * @return bool
3827       */
3828  	public function is_comments_popup() {
3829          _deprecated_function( __FUNCTION__, '4.5.0' );
3830  
3831          return false;
3832      }
3833  
3834      /**
3835       * Is the query for an existing date archive?
3836       *
3837       * @since 3.1.0
3838       *
3839       * @return bool
3840       */
3841  	public function is_date() {
3842          return (bool) $this->is_date;
3843      }
3844  
3845      /**
3846       * Is the query for an existing day archive?
3847       *
3848       * @since 3.1.0
3849       *
3850       * @return bool
3851       */
3852  	public function is_day() {
3853          return (bool) $this->is_day;
3854      }
3855  
3856      /**
3857       * Is the query for a feed?
3858       *
3859       * @since 3.1.0
3860       *
3861       * @param string|string[] $feeds Optional. Feed type or array of feed types
3862       *                                         to check against. Default empty.
3863       * @return bool
3864       */
3865  	public function is_feed( $feeds = '' ) {
3866          if ( empty( $feeds ) || ! $this->is_feed ) {
3867              return (bool) $this->is_feed;
3868          }
3869          $qv = $this->get( 'feed' );
3870          if ( 'feed' == $qv ) {
3871              $qv = get_default_feed();
3872          }
3873          return in_array( $qv, (array) $feeds );
3874      }
3875  
3876      /**
3877       * Is the query for a comments feed?
3878       *
3879       * @since 3.1.0
3880       *
3881       * @return bool
3882       */
3883  	public function is_comment_feed() {
3884          return (bool) $this->is_comment_feed;
3885      }
3886  
3887      /**
3888       * Is the query for the front page of the site?
3889       *
3890       * This is for what is displayed at your site's main URL.
3891       *
3892       * Depends on the site's "Front page displays" Reading Settings 'show_on_front' and 'page_on_front'.
3893       *
3894       * If you set a static page for the front page of your site, this function will return
3895       * true when viewing that page.
3896       *
3897       * Otherwise the same as @see WP_Query::is_home()
3898       *
3899       * @since 3.1.0
3900       *
3901       * @return bool True, if front of site.
3902       */
3903  	public function is_front_page() {
3904          // Most likely case.
3905          if ( 'posts' == get_option( 'show_on_front' ) && $this->is_home() ) {
3906              return true;
3907          } elseif ( 'page' == get_option( 'show_on_front' ) && get_option( 'page_on_front' ) && $this->is_page( get_option( 'page_on_front' ) ) ) {
3908              return true;
3909          } else {
3910              return false;
3911          }
3912      }
3913  
3914      /**
3915       * Is the query for the blog homepage?
3916       *
3917       * This is the page which shows the time based blog content of your site.
3918       *
3919       * Depends on the site's "Front page displays" Reading Settings 'show_on_front' and 'page_for_posts'.
3920       *
3921       * If you set a static page for the front page of your site, this function will return
3922       * true only on the page you set as the "Posts page".
3923       *
3924       * @see WP_Query::is_front_page()
3925       *
3926       * @since 3.1.0
3927       *
3928       * @return bool True if blog view homepage.
3929       */
3930  	public function is_home() {
3931          return (bool) $this->is_home;
3932      }
3933  
3934      /**
3935       * Is the query for the Privacy Policy page?
3936       *
3937       * This is the page which shows the Privacy Policy content of your site.
3938       *
3939       * Depends on the site's "Change your Privacy Policy page" Privacy Settings 'wp_page_for_privacy_policy'.
3940       *
3941       * This function will return true only on the page you set as the "Privacy Policy page".
3942       *
3943       * @since 5.2.0
3944       *
3945       * @return bool True, if Privacy Policy page.
3946       */
3947  	public function is_privacy_policy() {
3948          if ( get_option( 'wp_page_for_privacy_policy' ) && $this->is_page( get_option( 'wp_page_for_privacy_policy' ) ) ) {
3949              return true;
3950          } else {
3951              return false;
3952          }
3953      }
3954  
3955      /**
3956       * Is the query for an existing month archive?
3957       *
3958       * @since 3.1.0
3959       *
3960       * @return bool
3961       */
3962  	public function is_month() {
3963          return (bool) $this->is_month;
3964      }
3965  
3966      /**
3967       * Is the query for an existing single page?
3968       *
3969       * If the $page parameter is specified, this function will additionally
3970       * check if the query is for one of the pages specified.
3971       *
3972       * @see WP_Query::is_single()
3973       * @see WP_Query::is_singular()
3974       *
3975       * @since 3.1.0
3976       *
3977       * @param int|string|int[]|string[] $page Optional. Page ID, title, slug, path, or array of such
3978       *                                        to check against. Default empty.
3979       * @return bool Whether the query is for an existing single page.
3980       */
3981  	public function is_page( $page = '' ) {
3982          if ( ! $this->is_page ) {
3983              return false;
3984          }
3985  
3986          if ( empty( $page ) ) {
3987              return true;
3988          }
3989  
3990          $page_obj = $this->get_queried_object();
3991  
3992          $page = array_map( 'strval', (array) $page );
3993  
3994          if ( in_array( (string) $page_obj->ID, $page ) ) {
3995              return true;
3996          } elseif ( in_array( $page_obj->post_title, $page ) ) {
3997              return true;
3998          } elseif ( in_array( $page_obj->post_name, $page ) ) {
3999              return true;
4000          } else {
4001              foreach ( $page as $pagepath ) {
4002                  if ( ! strpos( $pagepath, '/' ) ) {
4003                      continue;
4004                  }
4005                  $pagepath_obj = get_page_by_path( $pagepath );
4006  
4007                  if ( $pagepath_obj && ( $pagepath_obj->ID == $page_obj->ID ) ) {
4008                      return true;
4009                  }
4010              }
4011          }
4012  
4013          return false;
4014      }
4015  
4016      /**
4017       * Is the query for paged result and not for the first page?
4018       *
4019       * @since 3.1.0
4020       *
4021       * @return bool
4022       */
4023  	public function is_paged() {
4024          return (bool) $this->is_paged;
4025      }
4026  
4027      /**
4028       * Is the query for a post or page preview?
4029       *
4030       * @since 3.1.0
4031       *
4032       * @return bool
4033       */
4034  	public function is_preview() {
4035          return (bool) $this->is_preview;
4036      }
4037  
4038      /**
4039       * Is the query for the robots.txt file?
4040       *
4041       * @since 3.1.0
4042       *
4043       * @return bool
4044       */
4045  	public function is_robots() {
4046          return (bool) $this->is_robots;
4047      }
4048  
4049      /**
4050       * Is the query for the favicon.ico file?
4051       *
4052       * @since 5.4.0
4053       *
4054       * @return bool
4055       */
4056  	public function is_favicon() {
4057          return (bool) $this->is_favicon;
4058      }
4059  
4060      /**
4061       * Is the query for a search?
4062       *
4063       * @since 3.1.0
4064       *
4065       * @return bool
4066       */
4067  	public function is_search() {
4068          return (bool) $this->is_search;
4069      }
4070  
4071      /**
4072       * Is the query for an existing single post?
4073       *
4074       * Works for any post type excluding pages.
4075       *
4076       * If the $post parameter is specified, this function will additionally
4077       * check if the query is for one of the Posts specified.
4078       *
4079       * @see WP_Query::is_page()
4080       * @see WP_Query::is_singular()
4081       *
4082       * @since 3.1.0
4083       *
4084       * @param int|string|int[]|string[] $post Optional. Post ID, title, slug, path, or array of such
4085       *                                        to check against. Default empty.
4086       * @return bool Whether the query is for an existing single post.
4087       */
4088  	public function is_single( $post = '' ) {
4089          if ( ! $this->is_single ) {
4090              return false;
4091          }
4092  
4093          if ( empty( $post ) ) {
4094              return true;
4095          }
4096  
4097          $post_obj = $this->get_queried_object();
4098  
4099          $post = array_map( 'strval', (array) $post );
4100  
4101          if ( in_array( (string) $post_obj->ID, $post ) ) {
4102              return true;
4103          } elseif ( in_array( $post_obj->post_title, $post ) ) {
4104              return true;
4105          } elseif ( in_array( $post_obj->post_name, $post ) ) {
4106              return true;
4107          } else {
4108              foreach ( $post as $postpath ) {
4109                  if ( ! strpos( $postpath, '/' ) ) {
4110                      continue;
4111                  }
4112                  $postpath_obj = get_page_by_path( $postpath, OBJECT, $post_obj->post_type );
4113  
4114                  if ( $postpath_obj && ( $postpath_obj->ID == $post_obj->ID ) ) {
4115                      return true;
4116                  }
4117              }
4118          }
4119          return false;
4120      }
4121  
4122      /**
4123       * Is the query for an existing single post of any post type (post, attachment, page,
4124       * custom post types)?
4125       *
4126       * If the $post_types parameter is specified, this function will additionally
4127       * check if the query is for one of the Posts Types specified.
4128       *
4129       * @see WP_Query::is_page()
4130       * @see WP_Query::is_single()
4131       *
4132       * @since 3.1.0
4133       *
4134       * @param string|string[] $post_types Optional. Post type or array of post types
4135       *                                    to check against. Default empty.
4136       * @return bool Whether the query is for an existing single post
4137       *              or any of the given post types.
4138       */
4139  	public function is_singular( $post_types = '' ) {
4140          if ( empty( $post_types ) || ! $this->is_singular ) {
4141              return (bool) $this->is_singular;
4142          }
4143  
4144          $post_obj = $this->get_queried_object();
4145  
4146          return in_array( $post_obj->post_type, (array) $post_types );
4147      }
4148  
4149      /**
4150       * Is the query for a specific time?
4151       *
4152       * @since 3.1.0
4153       *
4154       * @return bool
4155       */
4156  	public function is_time() {
4157          return (bool) $this->is_time;
4158      }
4159  
4160      /**
4161       * Is the query for a trackback endpoint call?
4162       *
4163       * @since 3.1.0
4164       *
4165       * @return bool
4166       */
4167  	public function is_trackback() {
4168          return (bool) $this->is_trackback;
4169      }
4170  
4171      /**
4172       * Is the query for an existing year archive?
4173       *
4174       * @since 3.1.0
4175       *
4176       * @return bool
4177       */
4178  	public function is_year() {
4179          return (bool) $this->is_year;
4180      }
4181  
4182      /**
4183       * Is the query a 404 (returns no results)?
4184       *
4185       * @since 3.1.0
4186       *
4187       * @return bool
4188       */
4189  	public function is_404() {
4190          return (bool) $this->is_404;
4191      }
4192  
4193      /**
4194       * Is the query for an embedded post?
4195       *
4196       * @since 4.4.0
4197       *
4198       * @return bool
4199       */
4200  	public function is_embed() {
4201          return (bool) $this->is_embed;
4202      }
4203  
4204      /**
4205       * Is the query the main query?
4206       *
4207       * @since 3.3.0
4208       *
4209       * @global WP_Query $wp_query WordPress Query object.
4210       *
4211       * @return bool
4212       */
4213  	public function is_main_query() {
4214          global $wp_the_query;
4215          return $wp_the_query === $this;
4216      }
4217  
4218      /**
4219       * Set up global post data.
4220       *
4221       * @since 4.1.0
4222       * @since 4.4.0 Added the ability to pass a post ID to `$post`.
4223       *
4224       * @global int     $id
4225       * @global WP_User $authordata
4226       * @global string  $currentday
4227       * @global string  $currentmonth
4228       * @global int     $page
4229       * @global array   $pages
4230       * @global int     $multipage
4231       * @global int     $more
4232       * @global int     $numpages
4233       *
4234       * @param WP_Post|object|int $post WP_Post instance or Post ID/object.
4235       * @return true True when finished.
4236       */
4237  	public function setup_postdata( $post ) {
4238          global $id, $authordata, $currentday, $currentmonth, $page, $pages, $multipage, $more, $numpages;
4239  
4240          if ( ! ( $post instanceof WP_Post ) ) {
4241              $post = get_post( $post );
4242          }
4243  
4244          if ( ! $post ) {
4245              return;
4246          }
4247  
4248          $elements = $this->generate_postdata( $post );
4249          if ( false === $elements ) {
4250              return;
4251          }
4252  
4253          $id           = $elements['id'];
4254          $authordata   = $elements['authordata'];
4255          $currentday   = $elements['currentday'];
4256          $currentmonth = $elements['currentmonth'];
4257          $page         = $elements['page'];
4258          $pages        = $elements['pages'];
4259          $multipage    = $elements['multipage'];
4260          $more         = $elements['more'];
4261          $numpages     = $elements['numpages'];
4262  
4263          /**
4264           * Fires once the post data has been setup.
4265           *
4266           * @since 2.8.0
4267           * @since 4.1.0 Introduced `$this` parameter.
4268           *
4269           * @param WP_Post  $post The Post object (passed by reference).
4270           * @param WP_Query $this The current Query object (passed by reference).
4271           */
4272          do_action_ref_array( 'the_post', array( &$post, &$this ) );
4273  
4274          return true;
4275      }
4276  
4277      /**
4278       * Generate post data.
4279       *
4280       * @since 5.2.0
4281       *
4282       * @param WP_Post|object|int $post WP_Post instance or Post ID/object.
4283       * @return array|bool $elements Elements of post or false on failure.
4284       */
4285  	public function generate_postdata( $post ) {
4286  
4287          if ( ! ( $post instanceof WP_Post ) ) {
4288              $post = get_post( $post );
4289          }
4290  
4291          if ( ! $post ) {
4292              return false;
4293          }
4294  
4295          $id = (int) $post->ID;
4296  
4297          $authordata = get_userdata( $post->post_author );
4298  
4299          $currentday   = mysql2date( 'd.m.y', $post->post_date, false );
4300          $currentmonth = mysql2date( 'm', $post->post_date, false );
4301          $numpages     = 1;
4302          $multipage    = 0;
4303          $page         = $this->get( 'page' );
4304          if ( ! $page ) {
4305              $page = 1;
4306          }
4307  
4308          /*
4309           * Force full post content when viewing the permalink for the $post,
4310           * or when on an RSS feed. Otherwise respect the 'more' tag.
4311           */
4312          if ( get_queried_object_id() === $post->ID && ( $this->is_page() || $this->is_single() ) ) {
4313              $more = 1;
4314          } elseif ( $this->is_feed() ) {
4315              $more = 1;
4316          } else {
4317              $more = 0;
4318          }
4319  
4320          $content = $post->post_content;
4321          if ( false !== strpos( $content, '<!--nextpage-->' ) ) {
4322              $content = str_replace( "\n<!--nextpage-->\n", '<!--nextpage-->', $content );
4323              $content = str_replace( "\n<!--nextpage-->", '<!--nextpage-->', $content );
4324              $content = str_replace( "<!--nextpage-->\n", '<!--nextpage-->', $content );
4325  
4326              // Remove the nextpage block delimiters, to avoid invalid block structures in the split content.
4327              $content = str_replace( '<!-- wp:nextpage -->', '', $content );
4328              $content = str_replace( '<!-- /wp:nextpage -->', '', $content );
4329  
4330              // Ignore nextpage at the beginning of the content.
4331              if ( 0 === strpos( $content, '<!--nextpage-->' ) ) {
4332                  $content = substr( $content, 15 );
4333              }
4334  
4335              $pages = explode( '<!--nextpage-->', $content );
4336          } else {
4337              $pages = array( $post->post_content );
4338          }
4339  
4340          /**
4341           * Filters the "pages" derived from splitting the post content.
4342           *
4343           * "Pages" are determined by splitting the post content based on the presence
4344           * of `<!-- nextpage -->` tags.
4345           *
4346           * @since 4.4.0
4347           *
4348           * @param string[] $pages Array of "pages" from the post content split by `<!-- nextpage -->` tags.
4349           * @param WP_Post  $post  Current post object.
4350           */
4351          $pages = apply_filters( 'content_pagination', $pages, $post );
4352  
4353          $numpages = count( $pages );
4354  
4355          if ( $numpages > 1 ) {
4356              if ( $page > 1 ) {
4357                  $more = 1;
4358              }
4359              $multipage = 1;
4360          } else {
4361              $multipage = 0;
4362          }
4363  
4364          $elements = compact( 'id', 'authordata', 'currentday', 'currentmonth', 'page', 'pages', 'multipage', 'more', 'numpages' );
4365  
4366          return $elements;
4367      }
4368      /**
4369       * After looping through a nested query, this function
4370       * restores the $post global to the current post in this query.
4371       *
4372       * @since 3.7.0
4373       *
4374       * @global WP_Post $post Global post object.
4375       */
4376  	public function reset_postdata() {
4377          if ( ! empty( $this->post ) ) {
4378              $GLOBALS['post'] = $this->post;
4379              $this->setup_postdata( $this->post );
4380          }
4381      }
4382  
4383      /**
4384       * Lazyload term meta for posts in the loop.
4385       *
4386       * @since 4.4.0
4387       * @deprecated 4.5.0 See wp_queue_posts_for_term_meta_lazyload().
4388       *
4389       * @param mixed $check
4390       * @param int   $term_id
4391       * @return mixed
4392       */
4393  	public function lazyload_term_meta( $check, $term_id ) {
4394          _deprecated_function( __METHOD__, '4.5.0' );
4395          return $check;
4396      }
4397  
4398      /**
4399       * Lazyload comment meta for comments in the loop.
4400       *
4401       * @since 4.4.0
4402       * @deprecated 4.5.0 See wp_queue_comments_for_comment_meta_lazyload().
4403       *
4404       * @param mixed $check
4405       * @param int   $comment_id
4406       * @return mixed
4407       */
4408  	public function lazyload_comment_meta( $check, $comment_id ) {
4409          _deprecated_function( __METHOD__, '4.5.0' );
4410          return $check;
4411      }
4412  }


Generated: Tue May 19 15:51:04 2020 Cross-referenced by PHPXref 0.7.1