[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

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


Generated: Mon Jun 17 08:20:02 2019 Cross-referenced by PHPXref 0.7