Make WordPress Core

Ticket #8071: 8071.6.diff

File 8071.6.diff, 28.0 KB (added by boonebgorges, 9 years ago)
  • src/wp-includes/class-wp-comment-query.php

    diff --git src/wp-includes/class-wp-comment-query.php src/wp-includes/class-wp-comment-query.php
    index 33d9888..1371853 100644
    class WP_Comment_Query { 
    4444        protected $meta_query_clauses;
    4545
    4646        /**
     47         * SQL query clauses.
     48         *
     49         * @since 4.4.0
     50         * @access protected
     51         * @var array
     52         */
     53        protected $sql_clauses = array(
     54                'select'  => '',
     55                'from'    => '',
     56                'where'   => array(),
     57                'groupby' => '',
     58                'orderby' => '',
     59                'limits'  => '',
     60        );
     61
     62        /**
    4763         * Date query container
    4864         *
    4965         * @since 3.7.0
    class WP_Comment_Query { 
    8096        public $comments;
    8197
    8298        /**
     99         * The amount of found comments for the current query.
     100         *
     101         * @since 4.4.0
     102         * @access public
     103         * @var int
     104         */
     105        public $found_comments = 0;
     106
     107        /**
     108         * The number of pages.
     109         *
     110         * @since 4.4.0
     111         * @access public
     112         * @var int
     113         */
     114        public $max_num_pages = 0;
     115
     116        /**
    83117         * Make private/protected methods readable for backwards compatibility.
    84118         *
    85119         * @since 4.0.0
    class WP_Comment_Query { 
    103137         *
    104138         * @since 4.2.0
    105139         * @since 4.4.0 `$parent__in` and `$parent__not_in` were added.
    106          * @since 4.4.0 Order by `comment__in` was added. `$update_comment_meta_cache` was added.
     140         * @since 4.4.0 Order by `comment__in` was added. `$update_comment_meta_cache`, `$no_found_rows`, and
     141         *              `$hierarchical` were added.
    107142         * @access public
    108143         *
    109144         * @param string|array $query {
    class WP_Comment_Query { 
    132167         *     @type int          $number              Maximum number of comments to retrieve. Default null (no limit).
    133168         *     @type int          $offset              Number of comments to offset the query. Used to build LIMIT clause.
    134169         *                                             Default 0.
     170         *     @type bool         $no_found_rows       Whether to disable the `SQL_CALC_FOUND_ROWS` query.
     171         *                                             Default: true.
    135172         *     @type string|array $orderby             Comment status or array of statuses. To use 'meta_value' or
    136173         *                                             'meta_value_num', `$meta_key` must also be defined. To sort by
    137174         *                                             a specific `$meta_query` clause, use that clause's array key.
    class WP_Comment_Query { 
    170207         *     @type array        $type__in            Include comments from a given array of comment types. Default empty.
    171208         *     @type array        $type__not_in        Exclude comments from a given array of comment types. Default empty.
    172209         *     @type int          $user_id             Include comments for a specific user ID. Default empty.
     210         *     @type bool|string  $hierarchical        Whether to include comment descendants in the results.
     211         *                                             'threaded' returns a tree, with each comment's children stored
     212         *                                             in a `children` property on the `WP_Comment` object. 'flat'
     213         *                                             returns a flat array of found comments plus their children.
     214         *                                             This parameter is ignored when `$fields` is 'ids'.
     215         *                                             Accepts 'threaded', 'flat', or false. Default: false.
    173216         *     @type bool         $update_comment_meta_cache Whether to prime the metadata cache for found comments.
    174217         *                                                   Default true.
    175218         * }
    class WP_Comment_Query { 
    187230                        'karma' => '',
    188231                        'number' => '',
    189232                        'offset' => '',
     233                        'no_found_rows' => true,
    190234                        'orderby' => '',
    191235                        'order' => 'DESC',
    192236                        'parent' => '',
    class WP_Comment_Query { 
    212256                        'meta_value' => '',
    213257                        'meta_query' => '',
    214258                        'date_query' => null, // See WP_Date_Query
     259                        'hierarchical' => false,
    215260                        'update_comment_meta_cache' => true,
    216261                );
    217262
    class WP_Comment_Query { 
    314359
    315360                $comment_ids = array_map( 'intval', $comment_ids );
    316361
     362                $this->comment_count = count( $this->comments );
     363
     364                if ( $comment_ids && $this->query_vars['number'] && ! $this->query_vars['no_found_rows'] ) {
     365                        /**
     366                         * Filter the query used to retrieve found comment count.
     367                         *
     368                         * @since 4.4.0
     369                         *
     370                         * @param string           $found_comments_query SQL query. Default 'SELECT FOUND_ROWS()'.
     371                         * @param WP_Comment_Query $comment_query        The `WP_Comment_Query` instance.
     372                         */
     373                        $found_comments_query = apply_filters( 'found_comments_query', 'SELECT FOUND_ROWS()', $this );
     374                        $this->found_comments = (int) $wpdb->get_var( $found_comments_query );
     375
     376                        $this->max_num_pages = ceil( $this->found_comments / $this->query_vars['number'] );
     377                }
     378
    317379                if ( 'ids' == $this->query_vars['fields'] ) {
    318380                        $this->comments = $comment_ids;
    319381                        return $this->comments;
    class WP_Comment_Query { 
    342404                // Convert to WP_Comment instances
    343405                $comments = array_map( 'get_comment', $_comments );
    344406
     407                if ( $this->query_vars['hierarchical'] ) {
     408                        $comments = $this->fill_descendants( $comments );
     409                }
     410
    345411                $this->comments = $comments;
    346412                return $this->comments;
    347413        }
    class WP_Comment_Query { 
    357423        protected function get_comment_ids() {
    358424                global $wpdb;
    359425
    360                 $groupby = '';
    361                 $where = array();
    362 
    363426                // Assemble clauses related to 'comment_approved'.
    364427                $approved_clauses = array();
    365428
    class WP_Comment_Query { 
    423486                // Collapse comment_approved clauses into a single OR-separated clause.
    424487                if ( ! empty( $approved_clauses ) ) {
    425488                        if ( 1 === count( $approved_clauses ) ) {
    426                                 $where[] = $approved_clauses[0];
     489                                $this->sql_clauses['where']['approved'] = $approved_clauses[0];
    427490                        } else {
    428                                 $where[] = '( ' . implode( ' OR ', $approved_clauses ) . ' )';
     491                                $this->sql_clauses['where']['approved'] = '( ' . implode( ' OR ', $approved_clauses ) . ' )';
    429492                        }
    430493                }
    431494
    class WP_Comment_Query { 
    524587                        } else {
    525588                                $limits = 'LIMIT ' . $number;
    526589                        }
    527                 } else {
    528                         $limits = '';
    529590                }
    530591
    531592                if ( $this->query_vars['count'] ) {
    class WP_Comment_Query { 
    534595                        $fields = "$wpdb->comments.comment_ID";
    535596                }
    536597
    537                 $join = '';
    538 
    539598                $post_id = absint( $this->query_vars['post_id'] );
    540599                if ( ! empty( $post_id ) ) {
    541                         $where[] = $wpdb->prepare( 'comment_post_ID = %d', $post_id );
     600                        $this->sql_clauses['where']['post_id'] = $wpdb->prepare( 'comment_post_ID = %d', $post_id );
    542601                }
    543602
    544603                // Parse comment IDs for an IN clause.
    545604                if ( ! empty( $this->query_vars['comment__in'] ) ) {
    546                         $where[] = "$wpdb->comments.comment_ID IN ( " . implode( ',', wp_parse_id_list( $this->query_vars['comment__in'] ) ) . ' )';
     605                        $this->sql_clauses['where']['comment__in'] = "$wpdb->comments.comment_ID IN ( " . implode( ',', wp_parse_id_list( $this->query_vars['comment__in'] ) ) . ' )';
    547606                }
    548607
    549608                // Parse comment IDs for a NOT IN clause.
    550609                if ( ! empty( $this->query_vars['comment__not_in'] ) ) {
    551                         $where[] = "$wpdb->comments.comment_ID NOT IN ( " . implode( ',', wp_parse_id_list( $this->query_vars['comment__not_in'] ) ) . ' )';
     610                        $this->sql_clauses['where']['comment__not_in'] = "$wpdb->comments.comment_ID NOT IN ( " . implode( ',', wp_parse_id_list( $this->query_vars['comment__not_in'] ) ) . ' )';
    552611                }
    553612
    554613                // Parse comment parent IDs for an IN clause.
    555614                if ( ! empty( $this->query_vars['parent__in'] ) ) {
    556                         $where[] = 'comment_parent IN ( ' . implode( ',', wp_parse_id_list( $this->query_vars['parent__in'] ) ) . ' )';
     615                        $this->sql_clauses['where']['parent__in'] = 'comment_parent IN ( ' . implode( ',', wp_parse_id_list( $this->query_vars['parent__in'] ) ) . ' )';
    557616                }
    558617
    559618                // Parse comment parent IDs for a NOT IN clause.
    560619                if ( ! empty( $this->query_vars['parent__not_in'] ) ) {
    561                         $where[] = 'comment_parent NOT IN ( ' . implode( ',', wp_parse_id_list( $this->query_vars['parent__not_in'] ) ) . ' )';
     620                        $this->sql_clauses['where']['parent__not_in'] = 'comment_parent NOT IN ( ' . implode( ',', wp_parse_id_list( $this->query_vars['parent__not_in'] ) ) . ' )';
    562621                }
    563622
    564623                // Parse comment post IDs for an IN clause.
    565624                if ( ! empty( $this->query_vars['post__in'] ) ) {
    566                         $where[] = 'comment_post_ID IN ( ' . implode( ',', wp_parse_id_list( $this->query_vars['post__in'] ) ) . ' )';
     625                        $this->sql_clauses['where']['post__in'] = 'comment_post_ID IN ( ' . implode( ',', wp_parse_id_list( $this->query_vars['post__in'] ) ) . ' )';
    567626                }
    568627
    569628                // Parse comment post IDs for a NOT IN clause.
    570629                if ( ! empty( $this->query_vars['post__not_in'] ) ) {
    571                         $where[] = 'comment_post_ID NOT IN ( ' . implode( ',', wp_parse_id_list( $this->query_vars['post__not_in'] ) ) . ' )';
     630                        $this->sql_clauses['where']['post__not_in'] = 'comment_post_ID NOT IN ( ' . implode( ',', wp_parse_id_list( $this->query_vars['post__not_in'] ) ) . ' )';
    572631                }
    573632
    574633                if ( '' !== $this->query_vars['author_email'] ) {
    575                         $where[] = $wpdb->prepare( 'comment_author_email = %s', $this->query_vars['author_email'] );
     634                        $this->sql_clauses['where']['author_email'] = $wpdb->prepare( 'comment_author_email = %s', $this->query_vars['author_email'] );
    576635                }
    577636
    578637                if ( '' !== $this->query_vars['karma'] ) {
    579                         $where[] = $wpdb->prepare( 'comment_karma = %d', $this->query_vars['karma'] );
     638                        $this->sql_clauses['where']['karma'] = $wpdb->prepare( 'comment_karma = %d', $this->query_vars['karma'] );
    580639                }
    581640
    582641                // Filtering by comment_type: 'type', 'type__in', 'type__not_in'.
    class WP_Comment_Query { 
    614673
    615674                        if ( ! empty( $comment_types[ $operator ] ) ) {
    616675                                $types_sql = implode( ', ', $comment_types[ $operator ] );
    617                                 $where[] = "comment_type $operator ($types_sql)";
     676                                $this->sql_clauses['where']['comment_type__' . strtolower( str_replace( ' ', '_', $operator ) ) ] = "comment_type $operator ($types_sql)";
    618677                        }
    619678                }
    620679
     680                if ( $this->query_vars['hierarchical'] && ! $this->query_vars['parent'] ) {
     681                        $this->query_vars['parent'] = 0;
     682                }
     683
    621684                if ( '' !== $this->query_vars['parent'] ) {
    622                         $where[] = $wpdb->prepare( 'comment_parent = %d', $this->query_vars['parent'] );
     685                        $this->sql_clauses['where']['parent'] = $wpdb->prepare( 'comment_parent = %d', $this->query_vars['parent'] );
    623686                }
    624687
    625688                if ( is_array( $this->query_vars['user_id'] ) ) {
    626                         $where[] = 'user_id IN (' . implode( ',', array_map( 'absint', $this->query_vars['user_id'] ) ) . ')';
     689                        $this->sql_clauses['where']['user_id'] = 'user_id IN (' . implode( ',', array_map( 'absint', $this->query_vars['user_id'] ) ) . ')';
    627690                } elseif ( '' !== $this->query_vars['user_id'] ) {
    628                         $where[] = $wpdb->prepare( 'user_id = %d', $this->query_vars['user_id'] );
     691                        $this->sql_clauses['where']['user_id'] = $wpdb->prepare( 'user_id = %d', $this->query_vars['user_id'] );
    629692                }
    630693
    631694                if ( '' !== $this->query_vars['search'] ) {
    class WP_Comment_Query { 
    635698                        );
    636699
    637700                        // Strip leading 'AND'.
    638                         $where[] = preg_replace( '/^\s*AND\s*/', '', $search_sql );
     701                        $this->sql_clauses['where']['search'] = preg_replace( '/^\s*AND\s*/', '', $search_sql );
    639702                }
    640703
    641704                // If any post-related query vars are passed, join the posts table.
    class WP_Comment_Query { 
    648711                        foreach ( $post_fields as $field_name => $field_value ) {
    649712                                // $field_value may be an array.
    650713                                $esses = array_fill( 0, count( (array) $field_value ), '%s' );
    651                                 $where[] = $wpdb->prepare( " {$wpdb->posts}.{$field_name} IN (" . implode( ',', $esses ) . ')', $field_value );
     714                                $this->sql_clauses['where']['post_fields'] = $wpdb->prepare( " {$wpdb->posts}.{$field_name} IN (" . implode( ',', $esses ) . ')', $field_value );
    652715                        }
    653716                }
    654717
    655718                // Comment author IDs for an IN clause.
    656719                if ( ! empty( $this->query_vars['author__in'] ) ) {
    657                         $where[] = 'user_id IN ( ' . implode( ',', wp_parse_id_list( $this->query_vars['author__in'] ) ) . ' )';
     720                        $this->sql_clauses['where']['author__in'] = 'user_id IN ( ' . implode( ',', wp_parse_id_list( $this->query_vars['author__in'] ) ) . ' )';
    658721                }
    659722
    660723                // Comment author IDs for a NOT IN clause.
    661724                if ( ! empty( $this->query_vars['author__not_in'] ) ) {
    662                         $where[] = 'user_id NOT IN ( ' . implode( ',', wp_parse_id_list( $this->query_vars['author__not_in'] ) ) . ' )';
     725                        $this->sql_clauses['where']['author__not_in'] = 'user_id NOT IN ( ' . implode( ',', wp_parse_id_list( $this->query_vars['author__not_in'] ) ) . ' )';
    663726                }
    664727
    665728                // Post author IDs for an IN clause.
    666729                if ( ! empty( $this->query_vars['post_author__in'] ) ) {
    667730                        $join_posts_table = true;
    668                         $where[] = 'post_author IN ( ' . implode( ',', wp_parse_id_list( $this->query_vars['post_author__in'] ) ) . ' )';
     731                        $this->sql_clauses['where']['post_author__in'] = 'post_author IN ( ' . implode( ',', wp_parse_id_list( $this->query_vars['post_author__in'] ) ) . ' )';
    669732                }
    670733
    671734                // Post author IDs for a NOT IN clause.
    672735                if ( ! empty( $this->query_vars['post_author__not_in'] ) ) {
    673736                        $join_posts_table = true;
    674                         $where[] = 'post_author NOT IN ( ' . implode( ',', wp_parse_id_list( $this->query_vars['post_author__not_in'] ) ) . ' )';
     737                        $this->sql_clauses['where']['post_author__not_in'] = 'post_author NOT IN ( ' . implode( ',', wp_parse_id_list( $this->query_vars['post_author__not_in'] ) ) . ' )';
    675738                }
    676739
     740                $join = '';
     741
    677742                if ( $join_posts_table ) {
    678                         $join = "JOIN $wpdb->posts ON $wpdb->posts.ID = $wpdb->comments.comment_post_ID";
     743                        $join .= "JOIN $wpdb->posts ON $wpdb->posts.ID = $wpdb->comments.comment_post_ID";
    679744                }
    680745
    681746                if ( ! empty( $this->meta_query_clauses ) ) {
    682747                        $join .= $this->meta_query_clauses['join'];
    683748
    684749                        // Strip leading 'AND'.
    685                         $where[] = preg_replace( '/^\s*AND\s*/', '', $this->meta_query_clauses['where'] );
     750                        $this->sql_clauses['where']['meta_query'] = preg_replace( '/^\s*AND\s*/', '', $this->meta_query_clauses['where'] );
    686751
    687752                        if ( ! $this->query_vars['count'] ) {
    688753                                $groupby = "{$wpdb->comments}.comment_ID";
    class WP_Comment_Query { 
    692757                $date_query = $this->query_vars['date_query'];
    693758                if ( ! empty( $date_query ) && is_array( $date_query ) ) {
    694759                        $date_query_object = new WP_Date_Query( $date_query, 'comment_date' );
    695                         $where[] = preg_replace( '/^\s*AND\s*/', '', $date_query_object->get_sql() );
     760                        $this->sql_clauses['where']['date_query'] = preg_replace( '/^\s*AND\s*/', '', $date_query_object->get_sql() );
    696761                }
    697762
    698                 $where = implode( ' AND ', $where );
     763                $where = implode( ' AND ', $this->sql_clauses['where'] );
    699764
    700765                $pieces = array( 'fields', 'join', 'where', 'orderby', 'limits', 'groupby' );
    701766                /**
    class WP_Comment_Query { 
    727792                        $orderby = "ORDER BY $orderby";
    728793                }
    729794
    730                 $this->request = "SELECT $fields FROM $wpdb->comments $join $where $groupby $orderby $limits";
     795                $found_rows = '';
     796                if ( ! $this->query_vars['no_found_rows'] ) {
     797                        $found_rows = 'SQL_CALC_FOUND_ROWS';
     798                }
     799
     800                $this->sql_clauses['select']  = "SELECT $found_rows $fields";
     801                $this->sql_clauses['from']    = "FROM $wpdb->comments $join";
     802                $this->sql_clauses['groupby'] = $groupby;
     803                $this->sql_clauses['orderby'] = $orderby;
     804                $this->sql_clauses['limits']  = $limits;
     805
     806                $this->request = "{$this->sql_clauses['select']} {$this->sql_clauses['from']} {$where} {$this->sql_clauses['groupby']} {$this->sql_clauses['orderby']} {$this->sql_clauses['limits']}";
    731807
    732808                if ( $this->query_vars['count'] ) {
    733809                        return intval( $wpdb->get_var( $this->request ) );
    class WP_Comment_Query { 
    738814        }
    739815
    740816        /**
     817         * Fetch descendants for located comments.
     818         *
     819         * @since 4.4.0
     820         *
     821         * @param array $comments Array of top-level comments whose descendants should be filled in.
     822         * @return array
     823         */
     824        protected function fill_descendants( $comments ) {
     825                global $wpdb;
     826
     827                $levels = array(
     828                        0 => wp_list_pluck( $comments, 'comment_ID' ),
     829                );
     830
     831                $where_clauses = $this->sql_clauses['where'];
     832                unset(
     833                        $where_clauses['parent'],
     834                        $where_clauses['parent__in'],
     835                        $where_clauses['parent__not_in']
     836                );
     837
     838                // Fetch an entire level of the descendant tree at a time.
     839                $level = 0;
     840                do {
     841                        $parent_ids = $levels[ $level ];
     842                        $where = 'WHERE ' . implode( ' AND ', $where_clauses ) . ' AND comment_parent IN (' . implode( ',', array_map( 'intval', $parent_ids ) ) . ')';
     843                        $comment_ids = $wpdb->get_col( "{$this->sql_clauses['select']} {$this->sql_clauses['from']} {$where} {$this->sql_clauses['groupby']}" );
     844
     845                        $level++;
     846                        $levels[ $level ] = $comment_ids;
     847                } while ( $comment_ids );
     848
     849                // Prime comment caches for non-top-level comments.
     850                $descendant_ids = array();
     851                for ( $i = 1; $i < count( $levels ); $i++ ) {
     852                        $descendant_ids = array_merge( $descendant_ids, $levels[ $i ] );
     853                }
     854
     855                _prime_comment_caches( $descendant_ids, $this->query_vars['update_comment_meta_cache'] );
     856
     857                // Assemble a flat array of all comments + descendants.
     858                $all_comments = $comments;
     859                foreach ( $descendant_ids as $descendant_id ) {
     860                        $all_comments[] = get_comment( $descendant_id );
     861                }
     862
     863                // If a threaded representation was requested, build the tree.
     864                if ( 'threaded' === $this->query_vars['hierarchical'] ) {
     865                        $threaded_comments = $ref = array();
     866                        foreach ( $all_comments as $k => $c ) {
     867                                $_c = get_comment( $c->comment_ID );
     868
     869                                // If the comment isn't in the reference array, it goes in the top level of the thread.
     870                                if ( ! isset( $ref[ $c->comment_parent ] ) ) {
     871                                        $threaded_comments[ $_c->comment_ID ] = $_c;
     872                                        $ref[ $_c->comment_ID ] = $threaded_comments[ $_c->comment_ID ];
     873
     874                                // Otherwise, set it as a child of its parent.
     875                                } else {
     876
     877                                        $ref[ $_c->comment_parent ]->add_child( $_c );
     878//                                      $ref[ $c->comment_parent ]->children[ $c->comment_ID ] = $c;
     879                                        $ref[ $_c->comment_ID ] = $ref[ $_c->comment_parent ]->get_child( $_c->comment_ID );
     880                                }
     881                        }
     882
     883                        $comments = $threaded_comments;
     884                } else {
     885                        $comments = $all_comments;
     886                }
     887
     888                return $comments;
     889        }
     890
     891        /**
    741892         * Used internally to generate an SQL string for searching across multiple columns
    742893         *
    743894         * @since 3.1.0
  • src/wp-includes/class-wp-comment.php

    diff --git src/wp-includes/class-wp-comment.php src/wp-includes/class-wp-comment.php
    index 581fca2..b1d84b5 100644
    final class WP_Comment { 
    150150        public $user_id = 0;
    151151
    152152        /**
     153         * Comment children.
     154         *
     155         * @since 4.4.0
     156         * @access protected
     157         * @var array
     158         */
     159        protected $children;
     160
     161        /**
    153162         * Retrieves a WP_Comment instance.
    154163         *
    155164         * @since 4.4.0
    final class WP_Comment { 
    211220        public function to_array() {
    212221                return get_object_vars( $this );
    213222        }
     223
     224        /**
     225         * Get the children of a comment.
     226         *
     227         * @since 4.4.0
     228         * @access public
     229         *
     230         * @return array Array of `WP_Comment` objects.
     231         */
     232        public function get_children() {
     233                if ( is_null( $this->children ) ) {
     234                        $this->children = get_comments( array(
     235                                'parent' => $this->comment_ID,
     236                                'hierarchical' => 'threaded',
     237                        ) );
     238                }
     239
     240                return $this->children;
     241        }
     242
     243        /**
     244         * Add a child to the comment.
     245         *
     246         * Used by `WP_Comment_Query` when bulk-filling descendants.
     247         *
     248         * @since 4.4.0
     249         * @access public
     250         *
     251         * @param WP_Comment $child Child comment.
     252         */
     253        public function add_child( WP_Comment $child ) {
     254                $this->comments[ $child->comment_ID ] = $child;
     255        }
     256
     257        /**
     258         * Get a child comment by ID.
     259         *
     260         * @since 4.4.0
     261         * @access public
     262         *
     263         * @param int $child_id ID of the child.
     264         * @return WP_Comment|bool Returns the comment object if found, otherwise false.
     265         */
     266        public function get_child( $child_id ) {
     267                if ( isset( $this->comments[ $child_id ] ) ) {
     268                        return $this->comments[ $child_id ];
     269                }
     270
     271                return false;
     272        }
    214273}
     274
  • src/wp-includes/comment-template.php

    diff --git src/wp-includes/comment-template.php src/wp-includes/comment-template.php
    index 79b51c1..9650d93 100644
    function comments_template( $file = '/comments.php', $separate_comments = false 
    12111211                'orderby' => 'comment_date_gmt',
    12121212                'status'  => 'approve',
    12131213                'post_id' => $post->ID,
     1214                'hierarchical' => 'flat',
     1215                'no_found_rows' => false,
    12141216                'update_comment_meta_cache' => false, // We lazy-load comment meta for performance.
    12151217        );
    12161218
    function comments_template( $file = '/comments.php', $separate_comments = false 
    12201222                $comment_args['include_unapproved'] = array( $comment_author_email );
    12211223        }
    12221224
    1223         $comments = get_comments( $comment_args );
     1225        $per_page = (int) get_query_var( 'comments_per_page' );
     1226        if ( 0 === $per_page ) {
     1227                $per_page = (int) get_option( 'comments_per_page' );
     1228        }
     1229
     1230        $paging_threshold = $per_page ? $per_page * 2 : 100;
     1231
     1232        /**
     1233         * Filter the threshold at which comment pagination is forced.
     1234         *
     1235         * Pages with very large numbers of comments do not perform well, so we force pagination when the comment count
     1236         * exceeds a certain threshold. Use this filter to change the default threshold.
     1237         *
     1238         * @since 4.4.0
     1239         *
     1240         * @param int     $paging_threshold Number of comments a post must have before pagination is forced.
     1241         * @param WP_Post $post             The WP_Post instance.
     1242         */
     1243        $paging_threshold = apply_filters( 'force_comment_paging_threshold', $paging_threshold, $post );
     1244
     1245        if ( (int) get_option( 'page_comments' ) || $post->comment_count > $paging_threshold ) {
     1246                // Force pagination to be turned on in theme comment navigation functions.
     1247                add_filter( 'pre_option_page_comments', '__return_true' );
     1248
     1249                if ( $per_page ) {
     1250                        $comment_args['number'] = $per_page;
     1251                } else {
     1252                        $comment_args['number'] = $paging_threshold / 2;
     1253                }
     1254
     1255                $page = (int) get_query_var( 'cpage' );
     1256                if ( $page ) {
     1257                        $comment_args['offset'] = ( $page - 1 ) * $per_page;
     1258                }
     1259        }
     1260
     1261        $comment_query = new WP_Comment_Query( $comment_args );
    12241262
    12251263        /**
    12261264         * Filter the comments array.
    function comments_template( $file = '/comments.php', $separate_comments = false 
    12301268         * @param array $comments Array of comments supplied to the comments template.
    12311269         * @param int   $post_ID  Post ID.
    12321270         */
    1233         $wp_query->comments = apply_filters( 'comments_array', $comments, $post->ID );
     1271        $wp_query->comments = apply_filters( 'comments_array', $comment_query->comments, $post->ID );
    12341272        $comments = &$wp_query->comments;
    12351273        $wp_query->comment_count = count($wp_query->comments);
     1274        $wp_query->max_num_comment_pages = $comment_query->max_num_pages;
    12361275
    12371276        if ( $separate_comments ) {
    12381277                $wp_query->comments_by_type = separate_comments($comments);
    function wp_list_comments( $args = array(), $comments = null ) { 
    18161855                } else {
    18171856                        $_comments = $wp_query->comments;
    18181857                }
     1858
     1859                // Pagination is already handled by `WP_Comment_Query`, so we tell Walker not to bother.
     1860                if ( 1 < $wp_query->max_num_comment_pages ) {
     1861                        $r['page'] = 1;
     1862                }
    18191863        }
    18201864
    18211865        if ( '' === $r['per_page'] && get_option('page_comments') )
    function wp_list_comments( $args = array(), $comments = null ) { 
    18571901        }
    18581902
    18591903        $output = $walker->paged_walk( $_comments, $r['max_depth'], $r['page'], $r['per_page'], $r );
    1860         $wp_query->max_num_comment_pages = $walker->max_pages;
    18611904
    18621905        $in_comment_loop = false;
    18631906
  • tests/phpunit/tests/comment.php

    diff --git tests/phpunit/tests/comment.php tests/phpunit/tests/comment.php
    index a20f58d..80a7e65 100644
    class Tests_Comment extends WP_UnitTestCase { 
    273273                $sent = wp_new_comment_notify_postauthor( $c );
    274274                $this->assertFalse( $sent );
    275275        }
     276
     277        /**
     278         * @ticket 8071
     279         */
     280        public function test_wp_comment_get_children_should_fill_children() {
     281
     282                $p = $this->factory->post->create();
     283
     284                $c1 = $this->factory->comment->create( array(
     285                        'comment_post_ID' => $p,
     286                        'comment_approved' => '1',
     287                ) );
     288
     289                $c2 = $this->factory->comment->create( array(
     290                        'comment_post_ID' => $p,
     291                        'comment_approved' => '1',
     292                        'comment_parent' => $c1,
     293                ) );
     294
     295                $c3 = $this->factory->comment->create( array(
     296                        'comment_post_ID' => $p,
     297                        'comment_approved' => '1',
     298                        'comment_parent' => $c2,
     299                ) );
     300
     301                $c4 = $this->factory->comment->create( array(
     302                        'comment_post_ID' => $p,
     303                        'comment_approved' => '1',
     304                        'comment_parent' => $c1,
     305                ) );
     306
     307                $c5 = $this->factory->comment->create( array(
     308                        'comment_post_ID' => $p,
     309                        'comment_approved' => '1',
     310                ) );
     311
     312                $c6 = $this->factory->comment->create( array(
     313                        'comment_post_ID' => $p,
     314                        'comment_approved' => '1',
     315                        'comment_parent' => $c5,
     316                ) );
     317
     318                $comment = get_comment( $c1 );
     319                $children = $comment->get_children();
     320
     321                // Direct descendants of $c1.
     322                $this->assertEquals( array( $c2, $c4 ), array_values( wp_list_pluck( $children, 'comment_ID' ) ) );
     323
     324                // Direct descendants of $c2.
     325                $this->assertEquals( array( $c3 ), array_values( wp_list_pluck( $children[ $c2 ]->get_children(), 'comment_ID' ) ) );
     326        }
    276327}
  • tests/phpunit/tests/comment/query.php

    diff --git tests/phpunit/tests/comment/query.php tests/phpunit/tests/comment/query.php
    index 73ab4aa..f90857b 100644
    class Tests_Comment_Query extends WP_UnitTestCase { 
    18731873                $this->assertEquals( array( $c2, $c3 ), $ids->comments );
    18741874
    18751875        }
     1876
     1877        /**
     1878         * @ticket 8071
     1879         */
     1880        public function test_hierarchical_should_skip_child_comments_in_offset() {
     1881                $top_level_0 = $this->factory->comment->create( array(
     1882                        'comment_post_ID' => $this->post_id,
     1883                        'comment_approved' => '1',
     1884                ) );
     1885
     1886                $child_of_0 = $this->factory->comment->create( array(
     1887                        'comment_post_ID' => $this->post_id,
     1888                        'comment_approved' => '1',
     1889                        'comment_parent' => $top_level_0,
     1890                ) );
     1891
     1892                $top_level_comments = $this->factory->comment->create_many( 3, array(
     1893                        'comment_post_ID' => $this->post_id,
     1894                        'comment_approved' => '1',
     1895                ) );
     1896
     1897                $q = new WP_Comment_Query( array(
     1898                        'post_id' => $this->post_id,
     1899                        'hierarchical' => 'flat',
     1900                        'number' => 2,
     1901                        'offset' => 1,
     1902                        'orderby' => 'comment_ID',
     1903                        'order' => 'ASC',
     1904                        'fields' => 'ids',
     1905                ) );
     1906
     1907                $this->assertEquals( array( $top_level_comments[0], $top_level_comments[1] ), $q->comments );
     1908        }
     1909
     1910        /**
     1911         * @ticket 8071
     1912         */
     1913        public function test_hierarchical_should_not_include_child_comments_in_number() {
     1914                $top_level_0 = $this->factory->comment->create( array(
     1915                        'comment_post_ID' => $this->post_id,
     1916                        'comment_approved' => '1',
     1917                ) );
     1918
     1919                $child_of_0 = $this->factory->comment->create( array(
     1920                        'comment_post_ID' => $this->post_id,
     1921                        'comment_approved' => '1',
     1922                        'comment_parent' => $top_level_0,
     1923                ) );
     1924
     1925                $top_level_comments = $this->factory->comment->create_many( 3, array(
     1926                        'comment_post_ID' => $this->post_id,
     1927                        'comment_approved' => '1',
     1928                ) );
     1929
     1930                $q = new WP_Comment_Query( array(
     1931                        'post_id' => $this->post_id,
     1932                        'hierarchical' => 'flat',
     1933                        'number' => 2,
     1934                        'orderby' => 'comment_ID',
     1935                        'order' => 'ASC',
     1936                ) );
     1937
     1938                $this->assertEqualSets( array( $top_level_0, $child_of_0, $top_level_comments[0] ), wp_list_pluck( $q->comments, 'comment_ID' ) );
     1939        }
     1940
     1941        /**
     1942         * @ticket 8071
     1943         */
     1944        public function test_hierarchical_threaded() {
     1945                $c1 = $this->factory->comment->create( array(
     1946                        'comment_post_ID' => $this->post_id,
     1947                        'comment_approved' => '1',
     1948                ) );
     1949
     1950                $c2 = $this->factory->comment->create( array(
     1951                        'comment_post_ID' => $this->post_id,
     1952                        'comment_approved' => '1',
     1953                        'comment_parent' => $c1,
     1954                ) );
     1955
     1956                $c3 = $this->factory->comment->create( array(
     1957                        'comment_post_ID' => $this->post_id,
     1958                        'comment_approved' => '1',
     1959                        'comment_parent' => $c2,
     1960                ) );
     1961
     1962                $c4 = $this->factory->comment->create( array(
     1963                        'comment_post_ID' => $this->post_id,
     1964                        'comment_approved' => '1',
     1965                        'comment_parent' => $c1,
     1966                ) );
     1967
     1968                $c5 = $this->factory->comment->create( array(
     1969                        'comment_post_ID' => $this->post_id,
     1970                        'comment_approved' => '1',
     1971                ) );
     1972
     1973                $c6 = $this->factory->comment->create( array(
     1974                        'comment_post_ID' => $this->post_id,
     1975                        'comment_approved' => '1',
     1976                        'comment_parent' => $c5,
     1977                ) );
     1978
     1979                $q = new WP_Comment_Query( array(
     1980                        'post_id' => $this->post_id,
     1981                        'hierarchical' => 'threaded',
     1982                        'orderby' => 'comment_ID',
     1983                        'order' => 'ASC',
     1984                ) );
     1985
     1986                // Top-level comments.
     1987                $this->assertEquals( array( $c1, $c5 ), array_values( wp_list_pluck( $q->comments, 'comment_ID' ) ) );
     1988
     1989                // Direct descendants of $c1.
     1990                $this->assertEquals( array( $c2, $c4 ), array_values( wp_list_pluck( $q->comments[ $c1 ]->get_children(), 'comment_ID' ) ) );
     1991
     1992                // Direct descendants of $c2.
     1993                $this->assertEquals( array( $c3 ), array_values( wp_list_pluck( $q->comments[ $c1 ]->get_child( $c2 )->get_children(), 'comment_ID' ) ) );
     1994
     1995                // Direct descendants of $c5.
     1996                $this->assertEquals( array( $c6 ), array_values( wp_list_pluck( $q->comments[ $c5 ]->get_children(), 'comment_ID' ) ) );
     1997        }
    18761998}