[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

title

Body

[close]

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

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


Generated: Wed Oct 23 08:20:01 2019 Cross-referenced by PHPXref 0.7