[ Index ]

PHP Cross Reference of WordPress

title

Body

[close]

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

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