116 /** |
116 /** |
117 * Make private/protected methods readable for backward compatibility. |
117 * Make private/protected methods readable for backward compatibility. |
118 * |
118 * |
119 * @since 4.0.0 |
119 * @since 4.0.0 |
120 * |
120 * |
121 * @param string $name Method to call. |
121 * @param string $name Method to call. |
122 * @param array $arguments Arguments to pass when calling. |
122 * @param array $arguments Arguments to pass when calling. |
123 * @return mixed|false Return value of the callback, false otherwise. |
123 * @return mixed|false Return value of the callback, false otherwise. |
124 */ |
124 */ |
125 public function __call( $name, $arguments ) { |
125 public function __call( $name, $arguments ) { |
126 if ( 'get_search_sql' === $name ) { |
126 if ( 'get_search_sql' === $name ) { |
127 return call_user_func_array( array( $this, $name ), $arguments ); |
127 return $this->get_search_sql( ...$arguments ); |
128 } |
128 } |
129 return false; |
129 return false; |
130 } |
130 } |
131 |
131 |
132 /** |
132 /** |
290 'search' => '', |
290 'search' => '', |
291 'count' => false, |
291 'count' => false, |
292 'meta_key' => '', |
292 'meta_key' => '', |
293 'meta_value' => '', |
293 'meta_value' => '', |
294 'meta_query' => '', |
294 'meta_query' => '', |
295 'date_query' => null, // See WP_Date_Query |
295 'date_query' => null, // See WP_Date_Query. |
296 'hierarchical' => false, |
296 'hierarchical' => false, |
297 'cache_domain' => 'core', |
297 'cache_domain' => 'core', |
298 'update_comment_meta_cache' => true, |
298 'update_comment_meta_cache' => true, |
299 'update_comment_post_cache' => false, |
299 'update_comment_post_cache' => false, |
300 ); |
300 ); |
358 public function get_comments() { |
358 public function get_comments() { |
359 global $wpdb; |
359 global $wpdb; |
360 |
360 |
361 $this->parse_query(); |
361 $this->parse_query(); |
362 |
362 |
363 // Parse meta query |
363 // Parse meta query. |
364 $this->meta_query = new WP_Meta_Query(); |
364 $this->meta_query = new WP_Meta_Query(); |
365 $this->meta_query->parse_query_vars( $this->query_vars ); |
365 $this->meta_query->parse_query_vars( $this->query_vars ); |
366 |
366 |
367 /** |
367 /** |
368 * Fires before comments are retrieved. |
368 * Fires before comments are retrieved. |
377 $this->meta_query->parse_query_vars( $this->query_vars ); |
377 $this->meta_query->parse_query_vars( $this->query_vars ); |
378 if ( ! empty( $this->meta_query->queries ) ) { |
378 if ( ! empty( $this->meta_query->queries ) ) { |
379 $this->meta_query_clauses = $this->meta_query->get_sql( 'comment', $wpdb->comments, 'comment_ID', $this ); |
379 $this->meta_query_clauses = $this->meta_query->get_sql( 'comment', $wpdb->comments, 'comment_ID', $this ); |
380 } |
380 } |
381 |
381 |
|
382 $comment_data = null; |
|
383 |
|
384 /** |
|
385 * Filter the comments data before the query takes place. |
|
386 * |
|
387 * Return a non-null value to bypass WordPress's default comment queries. |
|
388 * |
|
389 * The expected return type from this filter depends on the value passed in the request query_vars. |
|
390 * When `$this->query_vars['count']` is set, the filter should return the comment count as an int. |
|
391 * When `'ids' === $this->query_vars['fields']`, the filter should return an array of comment IDs. |
|
392 * Otherwise the filter should return an array of WP_Comment objects. |
|
393 * |
|
394 * @since 5.3.0 |
|
395 * |
|
396 * @param array|int|null $comment_data Return an array of comment data to short-circuit WP's comment query, |
|
397 * the comment count as an integer if `$this->query_vars['count']` is set, |
|
398 * or null to allow WP to run its normal queries. |
|
399 * @param WP_Comment_Query $this The WP_Comment_Query instance, passed by reference. |
|
400 */ |
|
401 $comment_data = apply_filters_ref_array( 'comments_pre_query', array( $comment_data, &$this ) ); |
|
402 |
|
403 if ( null !== $comment_data ) { |
|
404 return $comment_data; |
|
405 } |
|
406 |
382 /* |
407 /* |
383 * Only use the args defined in the query_var_defaults to compute the key, |
408 * Only use the args defined in the query_var_defaults to compute the key, |
384 * but ignore 'fields', which does not affect query results. |
409 * but ignore 'fields', which does not affect query results. |
385 */ |
410 */ |
386 $_args = wp_array_slice_assoc( $this->query_vars, array_keys( $this->query_var_defaults ) ); |
411 $_args = wp_array_slice_assoc( $this->query_vars, array_keys( $this->query_var_defaults ) ); |
417 return intval( $comment_ids ); |
442 return intval( $comment_ids ); |
418 } |
443 } |
419 |
444 |
420 $comment_ids = array_map( 'intval', $comment_ids ); |
445 $comment_ids = array_map( 'intval', $comment_ids ); |
421 |
446 |
422 if ( 'ids' == $this->query_vars['fields'] ) { |
447 if ( 'ids' === $this->query_vars['fields'] ) { |
423 $this->comments = $comment_ids; |
448 $this->comments = $comment_ids; |
424 return $this->comments; |
449 return $this->comments; |
425 } |
450 } |
426 |
451 |
427 _prime_comment_caches( $comment_ids, $this->query_vars['update_comment_meta_cache'] ); |
452 _prime_comment_caches( $comment_ids, $this->query_vars['update_comment_meta_cache'] ); |
428 |
453 |
429 // Fetch full comment objects from the primed cache. |
454 // Fetch full comment objects from the primed cache. |
430 $_comments = array(); |
455 $_comments = array(); |
431 foreach ( $comment_ids as $comment_id ) { |
456 foreach ( $comment_ids as $comment_id ) { |
432 if ( $_comment = get_comment( $comment_id ) ) { |
457 $_comment = get_comment( $comment_id ); |
|
458 if ( $_comment ) { |
433 $_comments[] = $_comment; |
459 $_comments[] = $_comment; |
434 } |
460 } |
435 } |
461 } |
436 |
462 |
437 // Prime comment post caches. |
463 // Prime comment post caches. |
452 * @param WP_Comment[] $_comments An array of comments. |
478 * @param WP_Comment[] $_comments An array of comments. |
453 * @param WP_Comment_Query $this Current instance of WP_Comment_Query (passed by reference). |
479 * @param WP_Comment_Query $this Current instance of WP_Comment_Query (passed by reference). |
454 */ |
480 */ |
455 $_comments = apply_filters_ref_array( 'the_comments', array( $_comments, &$this ) ); |
481 $_comments = apply_filters_ref_array( 'the_comments', array( $_comments, &$this ) ); |
456 |
482 |
457 // Convert to WP_Comment instances |
483 // Convert to WP_Comment instances. |
458 $comments = array_map( 'get_comment', $_comments ); |
484 $comments = array_map( 'get_comment', $_comments ); |
459 |
485 |
460 if ( $this->query_vars['hierarchical'] ) { |
486 if ( $this->query_vars['hierarchical'] ) { |
461 $comments = $this->fill_descendants( $comments ); |
487 $comments = $this->fill_descendants( $comments ); |
462 } |
488 } |
488 if ( empty( $statuses ) ) { |
514 if ( empty( $statuses ) ) { |
489 $statuses = array( 'all' ); |
515 $statuses = array( 'all' ); |
490 } |
516 } |
491 |
517 |
492 // 'any' overrides other statuses. |
518 // 'any' overrides other statuses. |
493 if ( ! in_array( 'any', $statuses ) ) { |
519 if ( ! in_array( 'any', $statuses, true ) ) { |
494 foreach ( $statuses as $status ) { |
520 foreach ( $statuses as $status ) { |
495 switch ( $status ) { |
521 switch ( $status ) { |
496 case 'hold': |
522 case 'hold': |
497 $status_clauses[] = "comment_approved = '0'"; |
523 $status_clauses[] = "comment_approved = '0'"; |
498 break; |
524 break; |
522 $include_unapproved = wp_parse_list( $this->query_vars['include_unapproved'] ); |
548 $include_unapproved = wp_parse_list( $this->query_vars['include_unapproved'] ); |
523 |
549 |
524 $unapproved_ids = array(); |
550 $unapproved_ids = array(); |
525 $unapproved_emails = array(); |
551 $unapproved_emails = array(); |
526 foreach ( $include_unapproved as $unapproved_identifier ) { |
552 foreach ( $include_unapproved as $unapproved_identifier ) { |
527 // Numeric values are assumed to be user ids. |
553 // Numeric values are assumed to be user IDs. |
528 if ( is_numeric( $unapproved_identifier ) ) { |
554 if ( is_numeric( $unapproved_identifier ) ) { |
529 $approved_clauses[] = $wpdb->prepare( "( user_id = %d AND comment_approved = '0' )", $unapproved_identifier ); |
555 $approved_clauses[] = $wpdb->prepare( "( user_id = %d AND comment_approved = '0' )", $unapproved_identifier ); |
530 |
556 } else { |
531 // Otherwise we match against email addresses. |
557 // Otherwise we match against email addresses. |
532 } else { |
558 if ( ! empty( $_GET['unapproved'] ) && ! empty( $_GET['moderation-hash'] ) ) { |
533 $approved_clauses[] = $wpdb->prepare( "( comment_author_email = %s AND comment_approved = '0' )", $unapproved_identifier ); |
559 // Only include requested comment. |
|
560 $approved_clauses[] = $wpdb->prepare( "( comment_author_email = %s AND comment_approved = '0' AND comment_ID = %d )", $unapproved_identifier, (int) $_GET['unapproved'] ); |
|
561 } else { |
|
562 // Include all of the author's unapproved comments. |
|
563 $approved_clauses[] = $wpdb->prepare( "( comment_author_email = %s AND comment_approved = '0' )", $unapproved_identifier ); |
|
564 } |
534 } |
565 } |
535 } |
566 } |
536 } |
567 } |
537 |
568 |
538 // Collapse comment_approved clauses into a single OR-separated clause. |
569 // Collapse comment_approved clauses into a single OR-separated clause. |
542 } else { |
573 } else { |
543 $this->sql_clauses['where']['approved'] = '( ' . implode( ' OR ', $approved_clauses ) . ' )'; |
574 $this->sql_clauses['where']['approved'] = '( ' . implode( ' OR ', $approved_clauses ) . ' )'; |
544 } |
575 } |
545 } |
576 } |
546 |
577 |
547 $order = ( 'ASC' == strtoupper( $this->query_vars['order'] ) ) ? 'ASC' : 'DESC'; |
578 $order = ( 'ASC' === strtoupper( $this->query_vars['order'] ) ) ? 'ASC' : 'DESC'; |
548 |
579 |
549 // Disable ORDER BY with 'none', an empty array, or boolean false. |
580 // Disable ORDER BY with 'none', an empty array, or boolean false. |
550 if ( in_array( $this->query_vars['orderby'], array( 'none', array(), false ), true ) ) { |
581 if ( in_array( $this->query_vars['orderby'], array( 'none', array(), false ), true ) ) { |
551 $orderby = ''; |
582 $orderby = ''; |
552 } elseif ( ! empty( $this->query_vars['orderby'] ) ) { |
583 } elseif ( ! empty( $this->query_vars['orderby'] ) ) { |
706 foreach ( $raw_types as $operator => $_raw_types ) { |
737 foreach ( $raw_types as $operator => $_raw_types ) { |
707 $_raw_types = array_unique( $_raw_types ); |
738 $_raw_types = array_unique( $_raw_types ); |
708 |
739 |
709 foreach ( $_raw_types as $type ) { |
740 foreach ( $_raw_types as $type ) { |
710 switch ( $type ) { |
741 switch ( $type ) { |
711 // An empty translates to 'all', for backward compatibility |
742 // An empty translates to 'all', for backward compatibility. |
712 case '': |
743 case '': |
713 case 'all': |
744 case 'all': |
714 break; |
745 break; |
715 |
746 |
716 case 'comment': |
747 case 'comment': |
717 case 'comments': |
748 case 'comments': |
718 $comment_types[ $operator ][] = "''"; |
749 $comment_types[ $operator ][] = "''"; |
|
750 $comment_types[ $operator ][] = "'comment'"; |
719 break; |
751 break; |
720 |
752 |
721 case 'pings': |
753 case 'pings': |
722 $comment_types[ $operator ][] = "'pingback'"; |
754 $comment_types[ $operator ][] = "'pingback'"; |
723 $comment_types[ $operator ][] = "'trackback'"; |
755 $comment_types[ $operator ][] = "'trackback'"; |
748 $this->sql_clauses['where']['user_id'] = 'user_id IN (' . implode( ',', array_map( 'absint', $this->query_vars['user_id'] ) ) . ')'; |
780 $this->sql_clauses['where']['user_id'] = 'user_id IN (' . implode( ',', array_map( 'absint', $this->query_vars['user_id'] ) ) . ')'; |
749 } elseif ( '' !== $this->query_vars['user_id'] ) { |
781 } elseif ( '' !== $this->query_vars['user_id'] ) { |
750 $this->sql_clauses['where']['user_id'] = $wpdb->prepare( 'user_id = %d', $this->query_vars['user_id'] ); |
782 $this->sql_clauses['where']['user_id'] = $wpdb->prepare( 'user_id = %d', $this->query_vars['user_id'] ); |
751 } |
783 } |
752 |
784 |
753 // Falsy search strings are ignored. |
785 // Falsey search strings are ignored. |
754 if ( strlen( $this->query_vars['search'] ) ) { |
786 if ( strlen( $this->query_vars['search'] ) ) { |
755 $search_sql = $this->get_search_sql( |
787 $search_sql = $this->get_search_sql( |
756 $this->query_vars['search'], |
788 $this->query_vars['search'], |
757 array( 'comment_author', 'comment_author_email', 'comment_author_url', 'comment_author_IP', 'comment_content' ) |
789 array( 'comment_author', 'comment_author_email', 'comment_author_url', 'comment_author_IP', 'comment_content' ) |
758 ); |
790 ); |
768 |
800 |
769 if ( ! empty( $post_fields ) ) { |
801 if ( ! empty( $post_fields ) ) { |
770 $join_posts_table = true; |
802 $join_posts_table = true; |
771 foreach ( $post_fields as $field_name => $field_value ) { |
803 foreach ( $post_fields as $field_name => $field_value ) { |
772 // $field_value may be an array. |
804 // $field_value may be an array. |
773 $esses = array_fill( 0, count( (array) $field_value ), '%s' ); |
805 $esses = array_fill( 0, count( (array) $field_value ), '%s' ); |
|
806 |
|
807 // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare |
774 $this->sql_clauses['where'][ $field_name ] = $wpdb->prepare( " {$wpdb->posts}.{$field_name} IN (" . implode( ',', $esses ) . ')', $field_value ); |
808 $this->sql_clauses['where'][ $field_name ] = $wpdb->prepare( " {$wpdb->posts}.{$field_name} IN (" . implode( ',', $esses ) . ')', $field_value ); |
775 } |
809 } |
776 } |
810 } |
777 |
811 |
778 // 'post_status' and 'post_type' are handled separately, due to the specialized behavior of 'any'. |
812 // 'post_status' and 'post_type' are handled separately, due to the specialized behavior of 'any'. |
789 continue; |
823 continue; |
790 } |
824 } |
791 |
825 |
792 $join_posts_table = true; |
826 $join_posts_table = true; |
793 |
827 |
794 $esses = array_fill( 0, count( $q_values ), '%s' ); |
828 $esses = array_fill( 0, count( $q_values ), '%s' ); |
|
829 |
|
830 // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare |
795 $this->sql_clauses['where'][ $field_name ] = $wpdb->prepare( " {$wpdb->posts}.{$field_name} IN (" . implode( ',', $esses ) . ')', $q_values ); |
831 $this->sql_clauses['where'][ $field_name ] = $wpdb->prepare( " {$wpdb->posts}.{$field_name} IN (" . implode( ',', $esses ) . ')', $q_values ); |
796 } |
832 } |
797 } |
833 } |
798 |
834 |
799 // Comment author IDs for an IN clause. |
835 // Comment author IDs for an IN clause. |
948 // Fetch an entire level of the descendant tree at a time. |
984 // Fetch an entire level of the descendant tree at a time. |
949 $level = 0; |
985 $level = 0; |
950 $exclude_keys = array( 'parent', 'parent__in', 'parent__not_in' ); |
986 $exclude_keys = array( 'parent', 'parent__in', 'parent__not_in' ); |
951 do { |
987 do { |
952 // Parent-child relationships may be cached. Only query for those that are not. |
988 // Parent-child relationships may be cached. Only query for those that are not. |
953 $child_ids = $uncached_parent_ids = array(); |
989 $child_ids = array(); |
954 $_parent_ids = $levels[ $level ]; |
990 $uncached_parent_ids = array(); |
|
991 $_parent_ids = $levels[ $level ]; |
955 foreach ( $_parent_ids as $parent_id ) { |
992 foreach ( $_parent_ids as $parent_id ) { |
956 $cache_key = "get_comment_child_ids:$parent_id:$key:$last_changed"; |
993 $cache_key = "get_comment_child_ids:$parent_id:$key:$last_changed"; |
957 $parent_child_ids = wp_cache_get( $cache_key, 'comment' ); |
994 $parent_child_ids = wp_cache_get( $cache_key, 'comment' ); |
958 if ( false !== $parent_child_ids ) { |
995 if ( false !== $parent_child_ids ) { |
959 $child_ids = array_merge( $child_ids, $parent_child_ids ); |
996 $child_ids = array_merge( $child_ids, $parent_child_ids ); |
1007 $all_comments[] = get_comment( $descendant_id ); |
1044 $all_comments[] = get_comment( $descendant_id ); |
1008 } |
1045 } |
1009 |
1046 |
1010 // If a threaded representation was requested, build the tree. |
1047 // If a threaded representation was requested, build the tree. |
1011 if ( 'threaded' === $this->query_vars['hierarchical'] ) { |
1048 if ( 'threaded' === $this->query_vars['hierarchical'] ) { |
1012 $threaded_comments = $ref = array(); |
1049 $threaded_comments = array(); |
|
1050 $ref = array(); |
1013 foreach ( $all_comments as $k => $c ) { |
1051 foreach ( $all_comments as $k => $c ) { |
1014 $_c = get_comment( $c->comment_ID ); |
1052 $_c = get_comment( $c->comment_ID ); |
1015 |
1053 |
1016 // If the comment isn't in the reference array, it goes in the top level of the thread. |
1054 // If the comment isn't in the reference array, it goes in the top level of the thread. |
1017 if ( ! isset( $ref[ $c->comment_parent ] ) ) { |
1055 if ( ! isset( $ref[ $c->comment_parent ] ) ) { |
1104 if ( $meta_query_clauses ) { |
1142 if ( $meta_query_clauses ) { |
1105 $allowed_keys = array_merge( $allowed_keys, array_keys( $meta_query_clauses ) ); |
1143 $allowed_keys = array_merge( $allowed_keys, array_keys( $meta_query_clauses ) ); |
1106 } |
1144 } |
1107 |
1145 |
1108 $parsed = false; |
1146 $parsed = false; |
1109 if ( $orderby == $this->query_vars['meta_key'] || $orderby == 'meta_value' ) { |
1147 if ( $this->query_vars['meta_key'] === $orderby || 'meta_value' === $orderby ) { |
1110 $parsed = "$wpdb->commentmeta.meta_value"; |
1148 $parsed = "$wpdb->commentmeta.meta_value"; |
1111 } elseif ( $orderby == 'meta_value_num' ) { |
1149 } elseif ( 'meta_value_num' === $orderby ) { |
1112 $parsed = "$wpdb->commentmeta.meta_value+0"; |
1150 $parsed = "$wpdb->commentmeta.meta_value+0"; |
1113 } elseif ( $orderby == 'comment__in' ) { |
1151 } elseif ( 'comment__in' === $orderby ) { |
1114 $comment__in = implode( ',', array_map( 'absint', $this->query_vars['comment__in'] ) ); |
1152 $comment__in = implode( ',', array_map( 'absint', $this->query_vars['comment__in'] ) ); |
1115 $parsed = "FIELD( {$wpdb->comments}.comment_ID, $comment__in )"; |
1153 $parsed = "FIELD( {$wpdb->comments}.comment_ID, $comment__in )"; |
1116 } elseif ( in_array( $orderby, $allowed_keys ) ) { |
1154 } elseif ( in_array( $orderby, $allowed_keys, true ) ) { |
1117 |
1155 |
1118 if ( isset( $meta_query_clauses[ $orderby ] ) ) { |
1156 if ( isset( $meta_query_clauses[ $orderby ] ) ) { |
1119 $meta_clause = $meta_query_clauses[ $orderby ]; |
1157 $meta_clause = $meta_query_clauses[ $orderby ]; |
1120 $parsed = sprintf( 'CAST(%s.meta_value AS %s)', esc_sql( $meta_clause['alias'] ), esc_sql( $meta_clause['cast'] ) ); |
1158 $parsed = sprintf( 'CAST(%s.meta_value AS %s)', esc_sql( $meta_clause['alias'] ), esc_sql( $meta_clause['cast'] ) ); |
1121 } else { |
1159 } else { |