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