[ Index ]

PHP Cross Reference of WordPress

title

Body

[close]

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

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