/** * Fetch descendants for located comments. * * Instead of calling `get_children()` separately on each child comment, we do a single set of queries to fetch * the descendant trees for all matched top-level comments. * * @since 4.4.0 * * @param array $comments Array of top-level comments whose descendants should be filled in. * @return array */ protected function fill_descendants($comments) { global $wpdb; $levels = array(0 => wp_list_pluck($comments, 'comment_ID')); /* * The WHERE clause for the descendant query is the same as for the top-level * query, minus the `parent`, `parent__in`, and `parent__not_in` sub-clauses. */ $_where = $this->filtered_where_clause; $exclude_keys = array('parent', 'parent__in', 'parent__not_in'); foreach ($exclude_keys as $exclude_key) { if (isset($this->sql_clauses['where'][$exclude_key])) { $clause = $this->sql_clauses['where'][$exclude_key]; // Strip the clause as well as any adjacent ANDs. $pattern = '|(?:AND)?\\s*' . $clause . '\\s*(?:AND)?|'; $_where_parts = preg_split($pattern, $_where); // Remove empties. $_where_parts = array_filter(array_map('trim', $_where_parts)); // Reassemble with an AND. $_where = implode(' AND ', $_where_parts); } } // Fetch an entire level of the descendant tree at a time. $level = 0; do { $parent_ids = $levels[$level]; if (!$parent_ids) { break; } $where = 'WHERE ' . $_where . ' AND comment_parent IN (' . implode(',', array_map('intval', $parent_ids)) . ')'; $comment_ids = $wpdb->get_col("{$this->sql_clauses['select']} {$this->sql_clauses['from']} {$where} {$this->sql_clauses['groupby']} ORDER BY comment_date_gmt ASC, comment_ID ASC"); $level++; $levels[$level] = $comment_ids; } while ($comment_ids); // Prime comment caches for non-top-level comments. $descendant_ids = array(); for ($i = 1; $i < count($levels); $i++) { $descendant_ids = array_merge($descendant_ids, $levels[$i]); } _prime_comment_caches($descendant_ids, $this->query_vars['update_comment_meta_cache']); // Assemble a flat array of all comments + descendants. $all_comments = $comments; foreach ($descendant_ids as $descendant_id) { $all_comments[] = get_comment($descendant_id); } // If a threaded representation was requested, build the tree. if ('threaded' === $this->query_vars['hierarchical']) { $threaded_comments = $ref = array(); foreach ($all_comments as $k => $c) { $_c = get_comment($c->comment_ID); // If the comment isn't in the reference array, it goes in the top level of the thread. if (!isset($ref[$c->comment_parent])) { $threaded_comments[$_c->comment_ID] = $_c; $ref[$_c->comment_ID] = $threaded_comments[$_c->comment_ID]; // Otherwise, set it as a child of its parent. } else { $ref[$_c->comment_parent]->add_child($_c); $ref[$_c->comment_ID] = $ref[$_c->comment_parent]->get_child($_c->comment_ID); } } // Set the 'populated_children' flag, to ensure additional database queries aren't run. foreach ($ref as $_ref) { $_ref->populated_children(true); } $comments = $threaded_comments; } else { $comments = $all_comments; } return $comments; }
/** * Fetch descendants for located comments. * * Instead of calling `get_children()` separately on each child comment, we do a single set of queries to fetch * the descendant trees for all matched top-level comments. * * @since 4.4.0 * * @param array $comments Array of top-level comments whose descendants should be filled in. * @return array */ protected function fill_descendants($comments) { global $wpdb; $levels = array(0 => wp_list_pluck($comments, 'comment_ID')); $where_clauses = $this->sql_clauses['where']; unset($where_clauses['parent'], $where_clauses['parent__in'], $where_clauses['parent__not_in']); // Fetch an entire level of the descendant tree at a time. $level = 0; do { $parent_ids = $levels[$level]; if (!$parent_ids) { break; } $where = 'WHERE ' . implode(' AND ', $where_clauses) . ' AND comment_parent IN (' . implode(',', array_map('intval', $parent_ids)) . ')'; $comment_ids = $wpdb->get_col("{$this->sql_clauses['select']} {$this->sql_clauses['from']} {$where} {$this->sql_clauses['groupby']} ORDER BY comment_date_gmt ASC, comment_ID ASC"); $level++; $levels[$level] = $comment_ids; } while ($comment_ids); // Prime comment caches for non-top-level comments. $descendant_ids = array(); for ($i = 1; $i < count($levels); $i++) { $descendant_ids = array_merge($descendant_ids, $levels[$i]); } _prime_comment_caches($descendant_ids, $this->query_vars['update_comment_meta_cache']); // Assemble a flat array of all comments + descendants. $all_comments = $comments; foreach ($descendant_ids as $descendant_id) { $all_comments[] = get_comment($descendant_id); } // If a threaded representation was requested, build the tree. if ('threaded' === $this->query_vars['hierarchical']) { $threaded_comments = $ref = array(); foreach ($all_comments as $k => $c) { $_c = get_comment($c->comment_ID); // If the comment isn't in the reference array, it goes in the top level of the thread. if (!isset($ref[$c->comment_parent])) { $threaded_comments[$_c->comment_ID] = $_c; $ref[$_c->comment_ID] = $threaded_comments[$_c->comment_ID]; // Otherwise, set it as a child of its parent. } else { $ref[$_c->comment_parent]->add_child($_c); $ref[$_c->comment_ID] = $ref[$_c->comment_parent]->get_child($_c->comment_ID); } } // Set the 'populated_children' flag, to ensure additional database queries aren't run. foreach ($ref as $_ref) { $_ref->populated_children(true); } $comments = $threaded_comments; } else { $comments = $all_comments; } return $comments; }
/** * Fetch descendants for located comments. * * Instead of calling `get_children()` separately on each child comment, we do a single set of queries to fetch * the descendant trees for all matched top-level comments. * * @since 4.4.0 * * @param array $comments Array of top-level comments whose descendants should be filled in. * @return array */ protected function fill_descendants($comments) { global $wpdb; $levels = array(0 => wp_list_pluck($comments, 'comment_ID')); /* * The WHERE clause for the descendant query is the same as for the top-level * query, minus the `parent`, `parent__in`, and `parent__not_in` sub-clauses. */ $_where = $this->filtered_where_clause; $exclude_keys = array('parent', 'parent__in', 'parent__not_in'); foreach ($exclude_keys as $exclude_key) { if (isset($this->sql_clauses['where'][$exclude_key])) { $clause = $this->sql_clauses['where'][$exclude_key]; // Strip the clause as well as any adjacent ANDs. $pattern = '|(?:AND)?\\s*' . $clause . '\\s*(?:AND)?|'; $_where_parts = preg_split($pattern, $_where); // Remove empties. $_where_parts = array_filter(array_map('trim', $_where_parts)); // Reassemble with an AND. $_where = implode(' AND ', $_where_parts); } } $key = md5(serialize(wp_array_slice_assoc($this->query_vars, array_keys($this->query_var_defaults)))); $last_changed = wp_cache_get_last_changed('comment'); // Fetch an entire level of the descendant tree at a time. $level = 0; do { // Parent-child relationships may be cached. Only query for those that are not. $child_ids = $uncached_parent_ids = array(); $_parent_ids = $levels[$level]; foreach ($_parent_ids as $parent_id) { $cache_key = "get_comment_child_ids:{$parent_id}:{$key}:{$last_changed}"; $parent_child_ids = wp_cache_get($cache_key, 'comment'); if (false !== $parent_child_ids) { $child_ids = array_merge($child_ids, $parent_child_ids); } else { $uncached_parent_ids[] = $parent_id; } } if ($uncached_parent_ids) { // Fetch this level of comments. $parent_query_args = $this->query_vars; foreach ($exclude_keys as $exclude_key) { $parent_query_args[$exclude_key] = ''; } $parent_query_args['parent__in'] = $uncached_parent_ids; $parent_query_args['no_found_rows'] = true; $parent_query_args['hierarchical'] = false; $parent_query_args['offset'] = 0; $parent_query_args['number'] = 0; $level_comments = get_comments($parent_query_args); // Cache parent-child relationships. $parent_map = array_fill_keys($uncached_parent_ids, array()); foreach ($level_comments as $level_comment) { $parent_map[$level_comment->comment_parent][] = $level_comment->comment_ID; $child_ids[] = $level_comment->comment_ID; } foreach ($parent_map as $parent_id => $children) { $cache_key = "get_comment_child_ids:{$parent_id}:{$key}:{$last_changed}"; wp_cache_set($cache_key, $children, 'comment'); } } $level++; $levels[$level] = $child_ids; } while ($child_ids); // Prime comment caches for non-top-level comments. $descendant_ids = array(); for ($i = 1, $c = count($levels); $i < $c; $i++) { $descendant_ids = array_merge($descendant_ids, $levels[$i]); } _prime_comment_caches($descendant_ids, $this->query_vars['update_comment_meta_cache']); // Assemble a flat array of all comments + descendants. $all_comments = $comments; foreach ($descendant_ids as $descendant_id) { $all_comments[] = get_comment($descendant_id); } // If a threaded representation was requested, build the tree. if ('threaded' === $this->query_vars['hierarchical']) { $threaded_comments = $ref = array(); foreach ($all_comments as $k => $c) { $_c = get_comment($c->comment_ID); // If the comment isn't in the reference array, it goes in the top level of the thread. if (!isset($ref[$c->comment_parent])) { $threaded_comments[$_c->comment_ID] = $_c; $ref[$_c->comment_ID] = $threaded_comments[$_c->comment_ID]; // Otherwise, set it as a child of its parent. } else { $ref[$_c->comment_parent]->add_child($_c); $ref[$_c->comment_ID] = $ref[$_c->comment_parent]->get_child($_c->comment_ID); } } // Set the 'populated_children' flag, to ensure additional database queries aren't run. foreach ($ref as $_ref) { $_ref->populated_children(true); } $comments = $threaded_comments; } else { $comments = $all_comments; } return $comments; }
/** * Get a list of comments matching the query vars. * * @since 4.2.0 * @access public * * @global wpdb $wpdb WordPress database abstraction object. * * @return int|array The list of comments. */ public function get_comments() { global $wpdb; $this->parse_query(); // Parse meta query $this->meta_query = new WP_Meta_Query(); $this->meta_query->parse_query_vars($this->query_vars); /** * Fires before comments are retrieved. * * @since 3.1.0 * * @param WP_Comment_Query &$this Current instance of WP_Comment_Query, passed by reference. */ do_action_ref_array('pre_get_comments', array(&$this)); // Reparse query vars, in case they were modified in a 'pre_get_comments' callback. $this->meta_query->parse_query_vars($this->query_vars); if (!empty($this->meta_query->queries)) { $this->meta_query_clauses = $this->meta_query->get_sql('comment', $wpdb->comments, 'comment_ID', $this); } // $args can include anything. Only use the args defined in the query_var_defaults to compute the key. $key = md5(serialize(wp_array_slice_assoc($this->query_vars, array_keys($this->query_var_defaults)))); $last_changed = wp_cache_get('last_changed', 'comment'); if (!$last_changed) { $last_changed = microtime(); wp_cache_set('last_changed', $last_changed, 'comment'); } $cache_key = "get_comment_ids:{$key}:{$last_changed}"; $comment_ids = wp_cache_get($cache_key, 'comment'); if (false === $comment_ids) { $comment_ids = $this->get_comment_ids(); wp_cache_add($cache_key, $comment_ids, 'comment'); } // If querying for a count only, there's nothing more to do. if ($this->query_vars['count']) { // $comment_ids is actually a count in this case. return intval($comment_ids); } $comment_ids = array_map('intval', $comment_ids); if ('ids' == $this->query_vars['fields']) { $this->comments = $comment_ids; return $this->comments; } _prime_comment_caches($comment_ids, $this->query_vars['update_comment_meta_cache']); // Fetch full comment objects from the primed cache. $_comments = array(); foreach ($comment_ids as $comment_id) { if ($_comment = wp_cache_get($comment_id, 'comment')) { $_comments[] = $_comment; } } /** * Filter the comment query results. * * @since 3.1.0 * * @param array $results An array of comments. * @param WP_Comment_Query &$this Current instance of WP_Comment_Query, passed by reference. */ $_comments = apply_filters_ref_array('the_comments', array($_comments, &$this)); // Convert to WP_Comment instances $comments = array_map('get_comment', $_comments); $this->comments = $comments; return $this->comments; }