[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

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


Generated : Thu Jul 9 08:20:02 2020 Cross-referenced by PHPXref