wp/wp-includes/class-wp-comment-query.php
changeset 21 48c4eec2b7e6
parent 19 3d72ae0968f4
equal deleted inserted replaced
20:7b1b88e27a20 21:48c4eec2b7e6
    12  *
    12  *
    13  * @since 3.1.0
    13  * @since 3.1.0
    14  *
    14  *
    15  * @see WP_Comment_Query::__construct() for accepted arguments.
    15  * @see WP_Comment_Query::__construct() for accepted arguments.
    16  */
    16  */
       
    17 #[AllowDynamicProperties]
    17 class WP_Comment_Query {
    18 class WP_Comment_Query {
    18 
    19 
    19 	/**
    20 	/**
    20 	 * SQL for database query.
    21 	 * SQL for database query.
    21 	 *
    22 	 *
   157 	 *                                                      comment objects (false). Default false.
   158 	 *                                                      comment objects (false). Default false.
   158 	 *     @type array           $date_query                Date query clauses to limit comments by. See WP_Date_Query.
   159 	 *     @type array           $date_query                Date query clauses to limit comments by. See WP_Date_Query.
   159 	 *                                                      Default null.
   160 	 *                                                      Default null.
   160 	 *     @type string          $fields                    Comment fields to return. Accepts 'ids' for comment IDs
   161 	 *     @type string          $fields                    Comment fields to return. Accepts 'ids' for comment IDs
   161 	 *                                                      only or empty for all fields. Default empty.
   162 	 *                                                      only or empty for all fields. Default empty.
   162 	 *     @type int             $ID                        Currently unused.
       
   163 	 *     @type array           $include_unapproved        Array of IDs or email addresses of users whose unapproved
   163 	 *     @type array           $include_unapproved        Array of IDs or email addresses of users whose unapproved
   164 	 *                                                      comments will be returned by the query regardless of
   164 	 *                                                      comments will be returned by the query regardless of
   165 	 *                                                      `$status`. Default empty.
   165 	 *                                                      `$status`. Default empty.
   166 	 *     @type int             $karma                     Karma score to retrieve matching comments for.
   166 	 *     @type int             $karma                     Karma score to retrieve matching comments for.
   167 	 *                                                      Default empty.
   167 	 *                                                      Default empty.
   168 	 *     @type string|string[] $meta_key                  Meta key or keys to filter by.
   168 	 *     @type string|string[] $meta_key                  Meta key or keys to filter by.
   169 	 *     @type string|string[] $meta_value                Meta value or values to filter by.
   169 	 *     @type string|string[] $meta_value                Meta value or values to filter by.
   170 	 *     @type string          $meta_compare              MySQL operator used for comparing the meta value.
   170 	 *     @type string          $meta_compare              MySQL operator used for comparing the meta value.
   171 	 *                                                      See WP_Meta_Query::__construct for accepted values and default value.
   171 	 *                                                      See WP_Meta_Query::__construct() for accepted values and default value.
   172 	 *     @type string          $meta_compare_key          MySQL operator used for comparing the meta key.
   172 	 *     @type string          $meta_compare_key          MySQL operator used for comparing the meta key.
   173 	 *                                                      See WP_Meta_Query::__construct for accepted values and default value.
   173 	 *                                                      See WP_Meta_Query::__construct() for accepted values and default value.
   174 	 *     @type string          $meta_type                 MySQL data type that the meta_value column will be CAST to for comparisons.
   174 	 *     @type string          $meta_type                 MySQL data type that the meta_value column will be CAST to for comparisons.
   175 	 *                                                      See WP_Meta_Query::__construct for accepted values and default value.
   175 	 *                                                      See WP_Meta_Query::__construct() for accepted values and default value.
   176 	 *     @type string          $meta_type_key             MySQL data type that the meta_key column will be CAST to for comparisons.
   176 	 *     @type string          $meta_type_key             MySQL data type that the meta_key column will be CAST to for comparisons.
   177 	 *                                                      See WP_Meta_Query::__construct for accepted values and default value.
   177 	 *                                                      See WP_Meta_Query::__construct() for accepted values and default value.
   178 	 *     @type array           $meta_query                An associative array of WP_Meta_Query arguments.
   178 	 *     @type array           $meta_query                An associative array of WP_Meta_Query arguments.
   179 	 *                                                      See WP_Meta_Query::__construct for accepted values.
   179 	 *                                                      See WP_Meta_Query::__construct() for accepted values.
   180 	 *     @type int             $number                    Maximum number of comments to retrieve.
   180 	 *     @type int             $number                    Maximum number of comments to retrieve.
   181 	 *                                                      Default empty (no limit).
   181 	 *                                                      Default empty (no limit).
   182 	 *     @type int             $paged                     When used with `$number`, defines the page of results to return.
   182 	 *     @type int             $paged                     When used with `$number`, defines the page of results to return.
   183 	 *                                                      When used with `$offset`, `$offset` takes precedence. Default 1.
   183 	 *                                                      When used with `$offset`, `$offset` takes precedence. Default 1.
   184 	 *     @type int             $offset                    Number of comments to offset the query. Used to build
   184 	 *     @type int             $offset                    Number of comments to offset the query. Used to build
   221 	 *                                                      children for. Default empty.
   221 	 *                                                      children for. Default empty.
   222 	 *     @type int[]           $post_author__in           Array of author IDs to retrieve comments for.
   222 	 *     @type int[]           $post_author__in           Array of author IDs to retrieve comments for.
   223 	 *                                                      Default empty.
   223 	 *                                                      Default empty.
   224 	 *     @type int[]           $post_author__not_in       Array of author IDs *not* to retrieve comments for.
   224 	 *     @type int[]           $post_author__not_in       Array of author IDs *not* to retrieve comments for.
   225 	 *                                                      Default empty.
   225 	 *                                                      Default empty.
   226 	 *     @type int             $post_ID                   Currently unused.
       
   227 	 *     @type int             $post_id                   Limit results to those affiliated with a given post ID.
   226 	 *     @type int             $post_id                   Limit results to those affiliated with a given post ID.
   228 	 *                                                      Default 0.
   227 	 *                                                      Default 0.
   229 	 *     @type int[]           $post__in                  Array of post IDs to include affiliated comments for.
   228 	 *     @type int[]           $post__in                  Array of post IDs to include affiliated comments for.
   230 	 *                                                      Default empty.
   229 	 *                                                      Default empty.
   231 	 *     @type int[]           $post__not_in              Array of post IDs to exclude affiliated comments for.
   230 	 *     @type int[]           $post__not_in              Array of post IDs to exclude affiliated comments for.
   328 	/**
   327 	/**
   329 	 * Parse arguments passed to the comment query with default query parameters.
   328 	 * Parse arguments passed to the comment query with default query parameters.
   330 	 *
   329 	 *
   331 	 * @since 4.2.0 Extracted from WP_Comment_Query::query().
   330 	 * @since 4.2.0 Extracted from WP_Comment_Query::query().
   332 	 *
   331 	 *
   333 	 * @param string|array $query WP_Comment_Query arguments. See WP_Comment_Query::__construct()
   332 	 * @param string|array $query WP_Comment_Query arguments. See WP_Comment_Query::__construct() for accepted arguments.
   334 	 */
   333 	 */
   335 	public function parse_query( $query = '' ) {
   334 	public function parse_query( $query = '' ) {
   336 		if ( empty( $query ) ) {
   335 		if ( empty( $query ) ) {
   337 			$query = $this->query_vars;
   336 			$query = $this->query_vars;
   338 		}
   337 		}
   451 
   450 
   452 		$key          = md5( serialize( $_args ) );
   451 		$key          = md5( serialize( $_args ) );
   453 		$last_changed = wp_cache_get_last_changed( 'comment' );
   452 		$last_changed = wp_cache_get_last_changed( 'comment' );
   454 
   453 
   455 		$cache_key   = "get_comments:$key:$last_changed";
   454 		$cache_key   = "get_comments:$key:$last_changed";
   456 		$cache_value = wp_cache_get( $cache_key, 'comment' );
   455 		$cache_value = wp_cache_get( $cache_key, 'comment-queries' );
   457 		if ( false === $cache_value ) {
   456 		if ( false === $cache_value ) {
   458 			$comment_ids = $this->get_comment_ids();
   457 			$comment_ids = $this->get_comment_ids();
   459 			if ( $comment_ids ) {
   458 			if ( $comment_ids ) {
   460 				$this->set_found_comments();
   459 				$this->set_found_comments();
   461 			}
   460 			}
   462 
   461 
   463 			$cache_value = array(
   462 			$cache_value = array(
   464 				'comment_ids'    => $comment_ids,
   463 				'comment_ids'    => $comment_ids,
   465 				'found_comments' => $this->found_comments,
   464 				'found_comments' => $this->found_comments,
   466 			);
   465 			);
   467 			wp_cache_add( $cache_key, $cache_value, 'comment' );
   466 			wp_cache_add( $cache_key, $cache_value, 'comment-queries' );
   468 		} else {
   467 		} else {
   469 			$comment_ids          = $cache_value['comment_ids'];
   468 			$comment_ids          = $cache_value['comment_ids'];
   470 			$this->found_comments = $cache_value['found_comments'];
   469 			$this->found_comments = $cache_value['found_comments'];
   471 		}
   470 		}
   472 
   471 
   473 		if ( $this->found_comments && $this->query_vars['number'] ) {
   472 		if ( $this->found_comments && $this->query_vars['number'] ) {
   474 			$this->max_num_pages = ceil( $this->found_comments / $this->query_vars['number'] );
   473 			$this->max_num_pages = (int) ceil( $this->found_comments / $this->query_vars['number'] );
   475 		}
   474 		}
   476 
   475 
   477 		// If querying for a count only, there's nothing more to do.
   476 		// If querying for a count only, there's nothing more to do.
   478 		if ( $this->query_vars['count'] ) {
   477 		if ( $this->query_vars['count'] ) {
   479 			// $comment_ids is actually a count in this case.
   478 			// $comment_ids is actually a count in this case.
   480 			return (int) $comment_ids;
   479 			return (int) $comment_ids;
   481 		}
   480 		}
   482 
   481 
   483 		$comment_ids = array_map( 'intval', $comment_ids );
   482 		$comment_ids = array_map( 'intval', $comment_ids );
   484 
   483 
       
   484 		if ( $this->query_vars['update_comment_meta_cache'] ) {
       
   485 			wp_lazyload_comment_meta( $comment_ids );
       
   486 		}
       
   487 
   485 		if ( 'ids' === $this->query_vars['fields'] ) {
   488 		if ( 'ids' === $this->query_vars['fields'] ) {
   486 			$this->comments = $comment_ids;
   489 			$this->comments = $comment_ids;
   487 			return $this->comments;
   490 			return $this->comments;
   488 		}
   491 		}
   489 
   492 
   490 		_prime_comment_caches( $comment_ids, $this->query_vars['update_comment_meta_cache'] );
   493 		_prime_comment_caches( $comment_ids, false );
   491 
   494 
   492 		// Fetch full comment objects from the primed cache.
   495 		// Fetch full comment objects from the primed cache.
   493 		$_comments = array();
   496 		$_comments = array();
   494 		foreach ( $comment_ids as $comment_id ) {
   497 		foreach ( $comment_ids as $comment_id ) {
   495 			$_comment = get_comment( $comment_id );
   498 			$_comment = get_comment( $comment_id );
   583 
   586 
   584 		// User IDs or emails whose unapproved comments are included, regardless of $status.
   587 		// User IDs or emails whose unapproved comments are included, regardless of $status.
   585 		if ( ! empty( $this->query_vars['include_unapproved'] ) ) {
   588 		if ( ! empty( $this->query_vars['include_unapproved'] ) ) {
   586 			$include_unapproved = wp_parse_list( $this->query_vars['include_unapproved'] );
   589 			$include_unapproved = wp_parse_list( $this->query_vars['include_unapproved'] );
   587 
   590 
   588 			$unapproved_ids    = array();
       
   589 			$unapproved_emails = array();
       
   590 			foreach ( $include_unapproved as $unapproved_identifier ) {
   591 			foreach ( $include_unapproved as $unapproved_identifier ) {
   591 				// Numeric values are assumed to be user IDs.
   592 				// Numeric values are assumed to be user IDs.
   592 				if ( is_numeric( $unapproved_identifier ) ) {
   593 				if ( is_numeric( $unapproved_identifier ) ) {
   593 					$approved_clauses[] = $wpdb->prepare( "( user_id = %d AND comment_approved = '0' )", $unapproved_identifier );
   594 					$approved_clauses[] = $wpdb->prepare( "( user_id = %d AND comment_approved = '0' )", $unapproved_identifier );
   594 				} else {
   595 				} else {
   674 				}
   675 				}
   675 
   676 
   676 				// If no date-related order is available, use the date from the first available clause.
   677 				// If no date-related order is available, use the date from the first available clause.
   677 				if ( ! $comment_id_order ) {
   678 				if ( ! $comment_id_order ) {
   678 					foreach ( $orderby_array as $orderby_clause ) {
   679 					foreach ( $orderby_array as $orderby_clause ) {
   679 						if ( false !== strpos( 'ASC', $orderby_clause ) ) {
   680 						if ( str_contains( 'ASC', $orderby_clause ) ) {
   680 							$comment_id_order = 'ASC';
   681 							$comment_id_order = 'ASC';
   681 						} else {
   682 						} else {
   682 							$comment_id_order = 'DESC';
   683 							$comment_id_order = 'DESC';
   683 						}
   684 						}
   684 
   685 
   909 				$groupby = "{$wpdb->comments}.comment_ID";
   910 				$groupby = "{$wpdb->comments}.comment_ID";
   910 			}
   911 			}
   911 		}
   912 		}
   912 
   913 
   913 		if ( ! empty( $this->query_vars['date_query'] ) && is_array( $this->query_vars['date_query'] ) ) {
   914 		if ( ! empty( $this->query_vars['date_query'] ) && is_array( $this->query_vars['date_query'] ) ) {
   914 			$this->date_query                         = new WP_Date_Query( $this->query_vars['date_query'], 'comment_date' );
   915 			$this->date_query = new WP_Date_Query( $this->query_vars['date_query'], 'comment_date' );
       
   916 
       
   917 			// Strip leading 'AND'.
   915 			$this->sql_clauses['where']['date_query'] = preg_replace( '/^\s*AND\s*/', '', $this->date_query->get_sql() );
   918 			$this->sql_clauses['where']['date_query'] = preg_replace( '/^\s*AND\s*/', '', $this->date_query->get_sql() );
   916 		}
   919 		}
   917 
   920 
   918 		$where = implode( ' AND ', $this->sql_clauses['where'] );
   921 		$where = implode( ' AND ', $this->sql_clauses['where'] );
   919 
   922 
   920 		$clauses = array( 'fields', 'join', 'where', 'orderby', 'limits', 'groupby' );
   923 		$pieces = array( 'fields', 'join', 'where', 'orderby', 'limits', 'groupby' );
   921 
   924 
   922 		/**
   925 		/**
   923 		 * Filters the comment query clauses.
   926 		 * Filters the comment query clauses.
   924 		 *
   927 		 *
   925 		 * @since 3.1.0
   928 		 * @since 3.1.0
   926 		 *
   929 		 *
   927 		 * @param string[]         $clauses An associative array of comment query clauses.
   930 		 * @param string[]         $clauses {
       
   931 		 *     Associative array of the clauses for the query.
       
   932 		 *
       
   933 		 *     @type string $fields   The SELECT clause of the query.
       
   934 		 *     @type string $join     The JOIN clause of the query.
       
   935 		 *     @type string $where    The WHERE clause of the query.
       
   936 		 *     @type string $orderby  The ORDER BY clause of the query.
       
   937 		 *     @type string $limits   The LIMIT clause of the query.
       
   938 		 *     @type string $groupby  The GROUP BY clause of the query.
       
   939 		 * }
   928 		 * @param WP_Comment_Query $query   Current instance of WP_Comment_Query (passed by reference).
   940 		 * @param WP_Comment_Query $query   Current instance of WP_Comment_Query (passed by reference).
   929 		 */
   941 		 */
   930 		$clauses = apply_filters_ref_array( 'comments_clauses', array( compact( $clauses ), &$this ) );
   942 		$clauses = apply_filters_ref_array( 'comments_clauses', array( compact( $pieces ), &$this ) );
   931 
   943 
   932 		$fields  = isset( $clauses['fields'] ) ? $clauses['fields'] : '';
   944 		$fields  = isset( $clauses['fields'] ) ? $clauses['fields'] : '';
   933 		$join    = isset( $clauses['join'] ) ? $clauses['join'] : '';
   945 		$join    = isset( $clauses['join'] ) ? $clauses['join'] : '';
   934 		$where   = isset( $clauses['where'] ) ? $clauses['where'] : '';
   946 		$where   = isset( $clauses['where'] ) ? $clauses['where'] : '';
   935 		$orderby = isset( $clauses['orderby'] ) ? $clauses['orderby'] : '';
   947 		$orderby = isset( $clauses['orderby'] ) ? $clauses['orderby'] : '';
   959 		$this->sql_clauses['from']    = "FROM $wpdb->comments $join";
   971 		$this->sql_clauses['from']    = "FROM $wpdb->comments $join";
   960 		$this->sql_clauses['groupby'] = $groupby;
   972 		$this->sql_clauses['groupby'] = $groupby;
   961 		$this->sql_clauses['orderby'] = $orderby;
   973 		$this->sql_clauses['orderby'] = $orderby;
   962 		$this->sql_clauses['limits']  = $limits;
   974 		$this->sql_clauses['limits']  = $limits;
   963 
   975 
   964 		$this->request = "
   976 		// Beginning of the string is on a new line to prevent leading whitespace. See https://core.trac.wordpress.org/ticket/56841.
   965 			{$this->sql_clauses['select']}
   977 		$this->request =
   966 			{$this->sql_clauses['from']}
   978 			"{$this->sql_clauses['select']}
   967 			{$where}
   979 			 {$this->sql_clauses['from']}
   968 			{$this->sql_clauses['groupby']}
   980 			 {$where}
   969 			{$this->sql_clauses['orderby']}
   981 			 {$this->sql_clauses['groupby']}
   970 			{$this->sql_clauses['limits']}
   982 			 {$this->sql_clauses['orderby']}
   971 		";
   983 			 {$this->sql_clauses['limits']}";
   972 
   984 
   973 		if ( $this->query_vars['count'] ) {
   985 		if ( $this->query_vars['count'] ) {
   974 			return (int) $wpdb->get_var( $this->request );
   986 			return (int) $wpdb->get_var( $this->request );
   975 		} else {
   987 		} else {
   976 			$comment_ids = $wpdb->get_col( $this->request );
   988 			$comment_ids = $wpdb->get_col( $this->request );
  1010 	 * Instead of calling `get_children()` separately on each child comment, we do a single set of queries to fetch
  1022 	 * Instead of calling `get_children()` separately on each child comment, we do a single set of queries to fetch
  1011 	 * the descendant trees for all matched top-level comments.
  1023 	 * the descendant trees for all matched top-level comments.
  1012 	 *
  1024 	 *
  1013 	 * @since 4.4.0
  1025 	 * @since 4.4.0
  1014 	 *
  1026 	 *
  1015 	 * @global wpdb $wpdb WordPress database abstraction object.
       
  1016 	 *
       
  1017 	 * @param WP_Comment[] $comments Array of top-level comments whose descendants should be filled in.
  1027 	 * @param WP_Comment[] $comments Array of top-level comments whose descendants should be filled in.
  1018 	 * @return array
  1028 	 * @return array
  1019 	 */
  1029 	 */
  1020 	protected function fill_descendants( $comments ) {
  1030 	protected function fill_descendants( $comments ) {
  1021 		global $wpdb;
       
  1022 
       
  1023 		$levels = array(
  1031 		$levels = array(
  1024 			0 => wp_list_pluck( $comments, 'comment_ID' ),
  1032 			0 => wp_list_pluck( $comments, 'comment_ID' ),
  1025 		);
  1033 		);
  1026 
  1034 
  1027 		$key          = md5( serialize( wp_array_slice_assoc( $this->query_vars, array_keys( $this->query_var_defaults ) ) ) );
  1035 		$key          = md5( serialize( wp_array_slice_assoc( $this->query_vars, array_keys( $this->query_var_defaults ) ) ) );
  1033 		do {
  1041 		do {
  1034 			// Parent-child relationships may be cached. Only query for those that are not.
  1042 			// Parent-child relationships may be cached. Only query for those that are not.
  1035 			$child_ids           = array();
  1043 			$child_ids           = array();
  1036 			$uncached_parent_ids = array();
  1044 			$uncached_parent_ids = array();
  1037 			$_parent_ids         = $levels[ $level ];
  1045 			$_parent_ids         = $levels[ $level ];
  1038 			foreach ( $_parent_ids as $parent_id ) {
  1046 			if ( $_parent_ids ) {
  1039 				$cache_key        = "get_comment_child_ids:$parent_id:$key:$last_changed";
  1047 				$cache_keys = array();
  1040 				$parent_child_ids = wp_cache_get( $cache_key, 'comment' );
  1048 				foreach ( $_parent_ids as $parent_id ) {
  1041 				if ( false !== $parent_child_ids ) {
  1049 					$cache_keys[ $parent_id ] = "get_comment_child_ids:$parent_id:$key:$last_changed";
  1042 					$child_ids = array_merge( $child_ids, $parent_child_ids );
  1050 				}
  1043 				} else {
  1051 				$cache_data = wp_cache_get_multiple( array_values( $cache_keys ), 'comment-queries' );
  1044 					$uncached_parent_ids[] = $parent_id;
  1052 				foreach ( $_parent_ids as $parent_id ) {
       
  1053 					$parent_child_ids = $cache_data[ $cache_keys[ $parent_id ] ];
       
  1054 					if ( false !== $parent_child_ids ) {
       
  1055 						$child_ids = array_merge( $child_ids, $parent_child_ids );
       
  1056 					} else {
       
  1057 						$uncached_parent_ids[] = $parent_id;
       
  1058 					}
  1045 				}
  1059 				}
  1046 			}
  1060 			}
  1047 
  1061 
  1048 			if ( $uncached_parent_ids ) {
  1062 			if ( $uncached_parent_ids ) {
  1049 				// Fetch this level of comments.
  1063 				// Fetch this level of comments.
  1069 				$data = array();
  1083 				$data = array();
  1070 				foreach ( $parent_map as $parent_id => $children ) {
  1084 				foreach ( $parent_map as $parent_id => $children ) {
  1071 					$cache_key          = "get_comment_child_ids:$parent_id:$key:$last_changed";
  1085 					$cache_key          = "get_comment_child_ids:$parent_id:$key:$last_changed";
  1072 					$data[ $cache_key ] = $children;
  1086 					$data[ $cache_key ] = $children;
  1073 				}
  1087 				}
  1074 				wp_cache_set_multiple( $data, 'comment' );
  1088 				wp_cache_set_multiple( $data, 'comment-queries' );
  1075 			}
  1089 			}
  1076 
  1090 
  1077 			$level++;
  1091 			++$level;
  1078 			$levels[ $level ] = $child_ids;
  1092 			$levels[ $level ] = $child_ids;
  1079 		} while ( $child_ids );
  1093 		} while ( $child_ids );
  1080 
  1094 
  1081 		// Prime comment caches for non-top-level comments.
  1095 		// Prime comment caches for non-top-level comments.
  1082 		$descendant_ids = array();
  1096 		$descendant_ids = array();