Make WordPress Core

Changeset 33750


Ignore:
Timestamp:
08/26/2015 04:26:29 AM (9 years ago)
Author:
wonderboymusic
Message:

Comments: move WP_Comment_Query into its own file. comment.php loads the new files, so this is 100% BC if someone is loading comment.php directly. New files created using svn cp.

Creates:
class-wp-comment-query.php
comment-functions.php

comment.php contains only top-level code. Class file only contains the class. Functions file only contains functions.

See #33413.

Location:
trunk/src/wp-includes
Files:
1 edited
2 copied

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/class-wp-comment-query.php

    r33746 r33750  
    11<?php
    2 /**
    3  * Manages WordPress comments
    4  *
    5  * @package WordPress
    6  * @subpackage Comment
    7  */
    8 
    9 /**
    10  * Check whether a comment passes internal checks to be allowed to add.
    11  *
    12  * If manual comment moderation is set in the administration, then all checks,
    13  * regardless of their type and whitelist, will fail and the function will
    14  * return false.
    15  *
    16  * If the number of links exceeds the amount in the administration, then the
    17  * check fails. If any of the parameter contents match the blacklist of words,
    18  * then the check fails.
    19  *
    20  * If the comment author was approved before, then the comment is automatically
    21  * whitelisted.
    22  *
    23  * If all checks pass, the function will return true.
    24  *
    25  * @since 1.2.0
    26  *
    27  * @global wpdb $wpdb WordPress database abstraction object.
    28  *
    29  * @param string $author       Comment author name.
    30  * @param string $email        Comment author email.
    31  * @param string $url          Comment author URL.
    32  * @param string $comment      Content of the comment.
    33  * @param string $user_ip      Comment author IP address.
    34  * @param string $user_agent   Comment author User-Agent.
    35  * @param string $comment_type Comment type, either user-submitted comment,
    36  *                             trackback, or pingback.
    37  * @return bool If all checks pass, true, otherwise false.
    38  */
    39 function check_comment($author, $email, $url, $comment, $user_ip, $user_agent, $comment_type) {
    40     global $wpdb;
    41 
    42     // If manual moderation is enabled, skip all checks and return false.
    43     if ( 1 == get_option('comment_moderation') )
    44         return false;
    45 
    46     /** This filter is documented in wp-includes/comment-template.php */
    47     $comment = apply_filters( 'comment_text', $comment );
    48 
    49     // Check for the number of external links if a max allowed number is set.
    50     if ( $max_links = get_option( 'comment_max_links' ) ) {
    51         $num_links = preg_match_all( '/<a [^>]*href/i', $comment, $out );
    52 
    53         /**
    54          * Filter the maximum number of links allowed in a comment.
    55          *
    56          * @since 3.0.0
    57          *
    58          * @param int    $num_links The number of links allowed.
    59          * @param string $url       Comment author's URL. Included in allowed links total.
    60          */
    61         $num_links = apply_filters( 'comment_max_links_url', $num_links, $url );
    62 
    63         /*
    64          * If the number of links in the comment exceeds the allowed amount,
    65          * fail the check by returning false.
    66          */
    67         if ( $num_links >= $max_links )
    68             return false;
    69     }
    70 
    71     $mod_keys = trim(get_option('moderation_keys'));
    72 
    73     // If moderation 'keys' (keywords) are set, process them.
    74     if ( !empty($mod_keys) ) {
    75         $words = explode("\n", $mod_keys );
    76 
    77         foreach ( (array) $words as $word) {
    78             $word = trim($word);
    79 
    80             // Skip empty lines.
    81             if ( empty($word) )
    82                 continue;
    83 
    84             /*
    85              * Do some escaping magic so that '#' (number of) characters in the spam
    86              * words don't break things:
    87              */
    88             $word = preg_quote($word, '#');
    89 
    90             /*
    91              * Check the comment fields for moderation keywords. If any are found,
    92              * fail the check for the given field by returning false.
    93              */
    94             $pattern = "#$word#i";
    95             if ( preg_match($pattern, $author) ) return false;
    96             if ( preg_match($pattern, $email) ) return false;
    97             if ( preg_match($pattern, $url) ) return false;
    98             if ( preg_match($pattern, $comment) ) return false;
    99             if ( preg_match($pattern, $user_ip) ) return false;
    100             if ( preg_match($pattern, $user_agent) ) return false;
    101         }
    102     }
    103 
    104     /*
    105      * Check if the option to approve comments by previously-approved authors is enabled.
    106      *
    107      * If it is enabled, check whether the comment author has a previously-approved comment,
    108      * as well as whether there are any moderation keywords (if set) present in the author
    109      * email address. If both checks pass, return true. Otherwise, return false.
    110      */
    111     if ( 1 == get_option('comment_whitelist')) {
    112         if ( 'trackback' != $comment_type && 'pingback' != $comment_type && $author != '' && $email != '' ) {
    113             // expected_slashed ($author, $email)
    114             $ok_to_comment = $wpdb->get_var("SELECT comment_approved FROM $wpdb->comments WHERE comment_author = '$author' AND comment_author_email = '$email' and comment_approved = '1' LIMIT 1");
    115             if ( ( 1 == $ok_to_comment ) &&
    116                 ( empty($mod_keys) || false === strpos( $email, $mod_keys) ) )
    117                     return true;
    118             else
    119                 return false;
    120         } else {
    121             return false;
    122         }
    123     }
    124     return true;
    125 }
    126 
    127 /**
    128  * Retrieve the approved comments for post $post_id.
    129  *
    130  * @since 2.0.0
    131  * @since 4.1.0 Refactored to leverage {@see WP_Comment_Query} over a direct query.
    132  *
    133  * @param  int   $post_id The ID of the post.
    134  * @param  array $args    Optional. See {@see WP_Comment_Query::query()} for information
    135  *                        on accepted arguments.
    136  * @return int|array $comments The approved comments, or number of comments if `$count`
    137  *                             argument is true.
    138  */
    139 function get_approved_comments( $post_id, $args = array() ) {
    140     if ( ! $post_id ) {
    141         return array();
    142     }
    143 
    144     $defaults = array(
    145         'status'  => 1,
    146         'post_id' => $post_id,
    147         'order'   => 'ASC',
    148     );
    149     $r = wp_parse_args( $args, $defaults );
    150 
    151     $query = new WP_Comment_Query;
    152     return $query->query( $r );
    153 }
    154 
    155 /**
    156  * Retrieves comment data given a comment ID or comment object.
    157  *
    158  * If an object is passed then the comment data will be cached and then returned
    159  * after being passed through a filter. If the comment is empty, then the global
    160  * comment variable will be used, if it is set.
    161  *
    162  * @since 2.0.0
    163  *
    164  * @global wpdb   $wpdb WordPress database abstraction object.
    165  * @global object $comment
    166  *
    167  * @param object|string|int $comment Comment to retrieve.
    168  * @param string $output Optional. OBJECT or ARRAY_A or ARRAY_N constants.
    169  * @return object|array|null Depends on $output value.
    170  */
    171 function get_comment(&$comment, $output = OBJECT) {
    172     global $wpdb;
    173 
    174     if ( empty($comment) ) {
    175         if ( isset($GLOBALS['comment']) )
    176             $_comment = & $GLOBALS['comment'];
    177         else
    178             $_comment = null;
    179     } elseif ( is_object($comment) ) {
    180         wp_cache_add($comment->comment_ID, $comment, 'comment');
    181         $_comment = $comment;
    182     } else {
    183         if ( isset($GLOBALS['comment']) && ($GLOBALS['comment']->comment_ID == $comment) ) {
    184             $_comment = & $GLOBALS['comment'];
    185         } elseif ( ! $_comment = wp_cache_get($comment, 'comment') ) {
    186             $_comment = $wpdb->get_row($wpdb->prepare("SELECT * FROM $wpdb->comments WHERE comment_ID = %d LIMIT 1", $comment));
    187             if ( ! $_comment )
    188                 return null;
    189             wp_cache_add($_comment->comment_ID, $_comment, 'comment');
    190         }
    191     }
    192 
    193     /**
    194      * Fires after a comment is retrieved.
    195      *
    196      * @since 2.3.0
    197      *
    198      * @param mixed $_comment Comment data.
    199      */
    200     $_comment = apply_filters( 'get_comment', $_comment );
    201 
    202     if ( $output == OBJECT ) {
    203         return $_comment;
    204     } elseif ( $output == ARRAY_A ) {
    205         $__comment = get_object_vars($_comment);
    206         return $__comment;
    207     } elseif ( $output == ARRAY_N ) {
    208         $__comment = array_values(get_object_vars($_comment));
    209         return $__comment;
    210     } else {
    211         return $_comment;
    212     }
    213 }
    214 
    215 /**
    216  * Retrieve a list of comments.
    217  *
    218  * The comment list can be for the blog as a whole or for an individual post.
    219  *
    220  * @since 2.7.0
    221  *
    222  * @param string|array $args Optional. Array or string of arguments. See {@see WP_Comment_Query::parse_query()}
    223  *                           for information on accepted arguments. Default empty.
    224  * @return int|array List of comments or number of found comments if `$count` argument is true.
    225  */
    226 function get_comments( $args = '' ) {
    227     $query = new WP_Comment_Query;
    228     return $query->query( $args );
    229 }
    230 
    2312/**
    2323 * WordPress Comment Query class.
     
    2356 *
    2367 * @since 3.1.0
     8 * @package WordPress
     9 * @subpackage Comment
    23710 */
    23811class WP_Comment_Query {
     
    1011784    }
    1012785}
    1013 
    1014 /**
    1015  * Retrieve all of the WordPress supported comment statuses.
    1016  *
    1017  * Comments have a limited set of valid status values, this provides the comment
    1018  * status values and descriptions.
    1019  *
    1020  * @since 2.7.0
    1021  *
    1022  * @return array List of comment statuses.
    1023  */
    1024 function get_comment_statuses() {
    1025     $status = array(
    1026         'hold'      => __('Unapproved'),
    1027         /* translators: comment status  */
    1028         'approve'   => _x('Approved', 'adjective'),
    1029         /* translators: comment status */
    1030         'spam'      => _x('Spam', 'adjective'),
    1031     );
    1032 
    1033     return $status;
    1034 }
    1035 
    1036 /**
    1037  * Gets the default comment status for a post type.
    1038  *
    1039  * @since 4.3.0
    1040  *
    1041  * @param string $post_type    Optional. Post type. Default 'post'.
    1042  * @param string $comment_type Optional. Comment type. Default 'comment'.
    1043  * @return string Expected return value is 'open' or 'closed'.
    1044  */
    1045 function get_default_comment_status( $post_type = 'post', $comment_type = 'comment' ) {
    1046     switch ( $comment_type ) {
    1047         case 'pingback' :
    1048         case 'trackback' :
    1049             $supports = 'trackbacks';
    1050             $option = 'ping';
    1051             break;
    1052         default :
    1053             $supports = 'comments';
    1054             $option = 'comment';
    1055     }
    1056 
    1057     // Set the status.
    1058     if ( 'page' === $post_type ) {
    1059         $status = 'closed';
    1060     } elseif ( post_type_supports( $post_type, $supports ) ) {
    1061         $status = get_option( "default_{$option}_status" );
    1062     } else {
    1063         $status = 'closed';
    1064     }
    1065 
    1066     /**
    1067      * Filter the default comment status for the given post type.
    1068      *
    1069      * @since 4.3.0
    1070      *
    1071      * @param string $status       Default status for the given post type,
    1072      *                             either 'open' or 'closed'.
    1073      * @param string $post_type    Post type. Default is `post`.
    1074      * @param string $comment_type Type of comment. Default is `comment`.
    1075      */
    1076     return apply_filters( 'get_default_comment_status' , $status, $post_type, $comment_type );
    1077 }
    1078 
    1079 /**
    1080  * The date the last comment was modified.
    1081  *
    1082  * @since 1.5.0
    1083  *
    1084  * @global wpdb $wpdb WordPress database abstraction object.
    1085  * @staticvar array $cache_lastcommentmodified
    1086  *
    1087  * @param string $timezone Which timezone to use in reference to 'gmt', 'blog',
    1088  *      or 'server' locations.
    1089  * @return string Last comment modified date.
    1090  */
    1091 function get_lastcommentmodified($timezone = 'server') {
    1092     global $wpdb;
    1093     static $cache_lastcommentmodified = array();
    1094 
    1095     if ( isset($cache_lastcommentmodified[$timezone]) )
    1096         return $cache_lastcommentmodified[$timezone];
    1097 
    1098     $add_seconds_server = date('Z');
    1099 
    1100     switch ( strtolower($timezone)) {
    1101         case 'gmt':
    1102             $lastcommentmodified = $wpdb->get_var("SELECT comment_date_gmt FROM $wpdb->comments WHERE comment_approved = '1' ORDER BY comment_date_gmt DESC LIMIT 1");
    1103             break;
    1104         case 'blog':
    1105             $lastcommentmodified = $wpdb->get_var("SELECT comment_date FROM $wpdb->comments WHERE comment_approved = '1' ORDER BY comment_date_gmt DESC LIMIT 1");
    1106             break;
    1107         case 'server':
    1108             $lastcommentmodified = $wpdb->get_var($wpdb->prepare("SELECT DATE_ADD(comment_date_gmt, INTERVAL %s SECOND) FROM $wpdb->comments WHERE comment_approved = '1' ORDER BY comment_date_gmt DESC LIMIT 1", $add_seconds_server));
    1109             break;
    1110     }
    1111 
    1112     $cache_lastcommentmodified[$timezone] = $lastcommentmodified;
    1113 
    1114     return $lastcommentmodified;
    1115 }
    1116 
    1117 /**
    1118  * The amount of comments in a post or total comments.
    1119  *
    1120  * A lot like {@link wp_count_comments()}, in that they both return comment
    1121  * stats (albeit with different types). The {@link wp_count_comments()} actual
    1122  * caches, but this function does not.
    1123  *
    1124  * @since 2.0.0
    1125  *
    1126  * @global wpdb $wpdb WordPress database abstraction object.
    1127  *
    1128  * @param int $post_id Optional. Comment amount in post if > 0, else total comments blog wide.
    1129  * @return array The amount of spam, approved, awaiting moderation, and total comments.
    1130  */
    1131 function get_comment_count( $post_id = 0 ) {
    1132     global $wpdb;
    1133 
    1134     $post_id = (int) $post_id;
    1135 
    1136     $where = '';
    1137     if ( $post_id > 0 ) {
    1138         $where = $wpdb->prepare("WHERE comment_post_ID = %d", $post_id);
    1139     }
    1140 
    1141     $totals = (array) $wpdb->get_results("
    1142         SELECT comment_approved, COUNT( * ) AS total
    1143         FROM {$wpdb->comments}
    1144         {$where}
    1145         GROUP BY comment_approved
    1146     ", ARRAY_A);
    1147 
    1148     $comment_count = array(
    1149         "approved"              => 0,
    1150         "awaiting_moderation"   => 0,
    1151         "spam"                  => 0,
    1152         "total_comments"        => 0
    1153     );
    1154 
    1155     foreach ( $totals as $row ) {
    1156         switch ( $row['comment_approved'] ) {
    1157             case 'spam':
    1158                 $comment_count['spam'] = $row['total'];
    1159                 $comment_count["total_comments"] += $row['total'];
    1160                 break;
    1161             case 1:
    1162                 $comment_count['approved'] = $row['total'];
    1163                 $comment_count['total_comments'] += $row['total'];
    1164                 break;
    1165             case 0:
    1166                 $comment_count['awaiting_moderation'] = $row['total'];
    1167                 $comment_count['total_comments'] += $row['total'];
    1168                 break;
    1169             default:
    1170                 break;
    1171         }
    1172     }
    1173 
    1174     return $comment_count;
    1175 }
    1176 
    1177 //
    1178 // Comment meta functions
    1179 //
    1180 
    1181 /**
    1182  * Add meta data field to a comment.
    1183  *
    1184  * @since 2.9.0
    1185  * @link https://codex.wordpress.org/Function_Reference/add_comment_meta
    1186  *
    1187  * @param int $comment_id Comment ID.
    1188  * @param string $meta_key Metadata name.
    1189  * @param mixed $meta_value Metadata value.
    1190  * @param bool $unique Optional, default is false. Whether the same key should not be added.
    1191  * @return int|bool Meta ID on success, false on failure.
    1192  */
    1193 function add_comment_meta($comment_id, $meta_key, $meta_value, $unique = false) {
    1194     return add_metadata('comment', $comment_id, $meta_key, $meta_value, $unique);
    1195 }
    1196 
    1197 /**
    1198  * Remove metadata matching criteria from a comment.
    1199  *
    1200  * You can match based on the key, or key and value. Removing based on key and
    1201  * value, will keep from removing duplicate metadata with the same key. It also
    1202  * allows removing all metadata matching key, if needed.
    1203  *
    1204  * @since 2.9.0
    1205  * @link https://codex.wordpress.org/Function_Reference/delete_comment_meta
    1206  *
    1207  * @param int $comment_id comment ID
    1208  * @param string $meta_key Metadata name.
    1209  * @param mixed $meta_value Optional. Metadata value.
    1210  * @return bool True on success, false on failure.
    1211  */
    1212 function delete_comment_meta($comment_id, $meta_key, $meta_value = '') {
    1213     return delete_metadata('comment', $comment_id, $meta_key, $meta_value);
    1214 }
    1215 
    1216 /**
    1217  * Retrieve comment meta field for a comment.
    1218  *
    1219  * @since 2.9.0
    1220  * @link https://codex.wordpress.org/Function_Reference/get_comment_meta
    1221  *
    1222  * @param int $comment_id Comment ID.
    1223  * @param string $key Optional. The meta key to retrieve. By default, returns data for all keys.
    1224  * @param bool $single Whether to return a single value.
    1225  * @return mixed Will be an array if $single is false. Will be value of meta data field if $single
    1226  *  is true.
    1227  */
    1228 function get_comment_meta($comment_id, $key = '', $single = false) {
    1229     return get_metadata('comment', $comment_id, $key, $single);
    1230 }
    1231 
    1232 /**
    1233  * Update comment meta field based on comment ID.
    1234  *
    1235  * Use the $prev_value parameter to differentiate between meta fields with the
    1236  * same key and comment ID.
    1237  *
    1238  * If the meta field for the comment does not exist, it will be added.
    1239  *
    1240  * @since 2.9.0
    1241  * @link https://codex.wordpress.org/Function_Reference/update_comment_meta
    1242  *
    1243  * @param int $comment_id Comment ID.
    1244  * @param string $meta_key Metadata key.
    1245  * @param mixed $meta_value Metadata value.
    1246  * @param mixed $prev_value Optional. Previous value to check before removing.
    1247  * @return int|bool Meta ID if the key didn't exist, true on successful update, false on failure.
    1248  */
    1249 function update_comment_meta($comment_id, $meta_key, $meta_value, $prev_value = '') {
    1250     return update_metadata('comment', $comment_id, $meta_key, $meta_value, $prev_value);
    1251 }
    1252 
    1253 /**
    1254  * Sets the cookies used to store an unauthenticated commentator's identity. Typically used
    1255  * to recall previous comments by this commentator that are still held in moderation.
    1256  *
    1257  * @param object $comment Comment object.
    1258  * @param object $user Comment author's object.
    1259  *
    1260  * @since 3.4.0
    1261  */
    1262 function wp_set_comment_cookies($comment, $user) {
    1263     if ( $user->exists() )
    1264         return;
    1265 
    1266     /**
    1267      * Filter the lifetime of the comment cookie in seconds.
    1268      *
    1269      * @since 2.8.0
    1270      *
    1271      * @param int $seconds Comment cookie lifetime. Default 30000000.
    1272      */
    1273     $comment_cookie_lifetime = apply_filters( 'comment_cookie_lifetime', 30000000 );
    1274     $secure = ( 'https' === parse_url( home_url(), PHP_URL_SCHEME ) );
    1275     setcookie( 'comment_author_' . COOKIEHASH, $comment->comment_author, time() + $comment_cookie_lifetime, COOKIEPATH, COOKIE_DOMAIN, $secure );
    1276     setcookie( 'comment_author_email_' . COOKIEHASH, $comment->comment_author_email, time() + $comment_cookie_lifetime, COOKIEPATH, COOKIE_DOMAIN, $secure );
    1277     setcookie( 'comment_author_url_' . COOKIEHASH, esc_url($comment->comment_author_url), time() + $comment_cookie_lifetime, COOKIEPATH, COOKIE_DOMAIN, $secure );
    1278 }
    1279 
    1280 /**
    1281  * Sanitizes the cookies sent to the user already.
    1282  *
    1283  * Will only do anything if the cookies have already been created for the user.
    1284  * Mostly used after cookies had been sent to use elsewhere.
    1285  *
    1286  * @since 2.0.4
    1287  */
    1288 function sanitize_comment_cookies() {
    1289     if ( isset( $_COOKIE['comment_author_' . COOKIEHASH] ) ) {
    1290         /**
    1291          * Filter the comment author's name cookie before it is set.
    1292          *
    1293          * When this filter hook is evaluated in wp_filter_comment(),
    1294          * the comment author's name string is passed.
    1295          *
    1296          * @since 1.5.0
    1297          *
    1298          * @param string $author_cookie The comment author name cookie.
    1299          */
    1300         $comment_author = apply_filters( 'pre_comment_author_name', $_COOKIE['comment_author_' . COOKIEHASH] );
    1301         $comment_author = wp_unslash($comment_author);
    1302         $comment_author = esc_attr($comment_author);
    1303         $_COOKIE['comment_author_' . COOKIEHASH] = $comment_author;
    1304     }
    1305 
    1306     if ( isset( $_COOKIE['comment_author_email_' . COOKIEHASH] ) ) {
    1307         /**
    1308          * Filter the comment author's email cookie before it is set.
    1309          *
    1310          * When this filter hook is evaluated in wp_filter_comment(),
    1311          * the comment author's email string is passed.
    1312          *
    1313          * @since 1.5.0
    1314          *
    1315          * @param string $author_email_cookie The comment author email cookie.
    1316          */
    1317         $comment_author_email = apply_filters( 'pre_comment_author_email', $_COOKIE['comment_author_email_' . COOKIEHASH] );
    1318         $comment_author_email = wp_unslash($comment_author_email);
    1319         $comment_author_email = esc_attr($comment_author_email);
    1320         $_COOKIE['comment_author_email_'.COOKIEHASH] = $comment_author_email;
    1321     }
    1322 
    1323     if ( isset( $_COOKIE['comment_author_url_' . COOKIEHASH] ) ) {
    1324         /**
    1325          * Filter the comment author's URL cookie before it is set.
    1326          *
    1327          * When this filter hook is evaluated in wp_filter_comment(),
    1328          * the comment author's URL string is passed.
    1329          *
    1330          * @since 1.5.0
    1331          *
    1332          * @param string $author_url_cookie The comment author URL cookie.
    1333          */
    1334         $comment_author_url = apply_filters( 'pre_comment_author_url', $_COOKIE['comment_author_url_' . COOKIEHASH] );
    1335         $comment_author_url = wp_unslash($comment_author_url);
    1336         $_COOKIE['comment_author_url_'.COOKIEHASH] = $comment_author_url;
    1337     }
    1338 }
    1339 
    1340 /**
    1341  * Validates whether this comment is allowed to be made.
    1342  *
    1343  * @since 2.0.0
    1344  *
    1345  * @global wpdb $wpdb WordPress database abstraction object.
    1346  *
    1347  * @param array $commentdata Contains information on the comment
    1348  * @return int|string Signifies the approval status (0|1|'spam')
    1349  */
    1350 function wp_allow_comment( $commentdata ) {
    1351     global $wpdb;
    1352 
    1353     // Simple duplicate check
    1354     // expected_slashed ($comment_post_ID, $comment_author, $comment_author_email, $comment_content)
    1355     $dupe = $wpdb->prepare(
    1356         "SELECT comment_ID FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_parent = %s AND comment_approved != 'trash' AND ( comment_author = %s ",
    1357         wp_unslash( $commentdata['comment_post_ID'] ),
    1358         wp_unslash( $commentdata['comment_parent'] ),
    1359         wp_unslash( $commentdata['comment_author'] )
    1360     );
    1361     if ( $commentdata['comment_author_email'] ) {
    1362         $dupe .= $wpdb->prepare(
    1363             "OR comment_author_email = %s ",
    1364             wp_unslash( $commentdata['comment_author_email'] )
    1365         );
    1366     }
    1367     $dupe .= $wpdb->prepare(
    1368         ") AND comment_content = %s LIMIT 1",
    1369         wp_unslash( $commentdata['comment_content'] )
    1370     );
    1371     if ( $wpdb->get_var( $dupe ) ) {
    1372         /**
    1373          * Fires immediately after a duplicate comment is detected.
    1374          *
    1375          * @since 3.0.0
    1376          *
    1377          * @param array $commentdata Comment data.
    1378          */
    1379         do_action( 'comment_duplicate_trigger', $commentdata );
    1380         if ( defined( 'DOING_AJAX' ) ) {
    1381             die( __('Duplicate comment detected; it looks as though you&#8217;ve already said that!') );
    1382         }
    1383         wp_die( __( 'Duplicate comment detected; it looks as though you&#8217;ve already said that!' ), 409 );
    1384     }
    1385 
    1386     /**
    1387      * Fires immediately before a comment is marked approved.
    1388      *
    1389      * Allows checking for comment flooding.
    1390      *
    1391      * @since 2.3.0
    1392      *
    1393      * @param string $comment_author_IP    Comment author's IP address.
    1394      * @param string $comment_author_email Comment author's email.
    1395      * @param string $comment_date_gmt     GMT date the comment was posted.
    1396      */
    1397     do_action(
    1398         'check_comment_flood',
    1399         $commentdata['comment_author_IP'],
    1400         $commentdata['comment_author_email'],
    1401         $commentdata['comment_date_gmt']
    1402     );
    1403 
    1404     if ( ! empty( $commentdata['user_id'] ) ) {
    1405         $user = get_userdata( $commentdata['user_id'] );
    1406         $post_author = $wpdb->get_var( $wpdb->prepare(
    1407             "SELECT post_author FROM $wpdb->posts WHERE ID = %d LIMIT 1",
    1408             $commentdata['comment_post_ID']
    1409         ) );
    1410     }
    1411 
    1412     if ( isset( $user ) && ( $commentdata['user_id'] == $post_author || $user->has_cap( 'moderate_comments' ) ) ) {
    1413         // The author and the admins get respect.
    1414         $approved = 1;
    1415     } else {
    1416         // Everyone else's comments will be checked.
    1417         if ( check_comment(
    1418             $commentdata['comment_author'],
    1419             $commentdata['comment_author_email'],
    1420             $commentdata['comment_author_url'],
    1421             $commentdata['comment_content'],
    1422             $commentdata['comment_author_IP'],
    1423             $commentdata['comment_agent'],
    1424             $commentdata['comment_type']
    1425         ) ) {
    1426             $approved = 1;
    1427         } else {
    1428             $approved = 0;
    1429         }
    1430 
    1431         if ( wp_blacklist_check(
    1432             $commentdata['comment_author'],
    1433             $commentdata['comment_author_email'],
    1434             $commentdata['comment_author_url'],
    1435             $commentdata['comment_content'],
    1436             $commentdata['comment_author_IP'],
    1437             $commentdata['comment_agent']
    1438         ) ) {
    1439             $approved = 'spam';
    1440         }
    1441     }
    1442 
    1443     /**
    1444      * Filter a comment's approval status before it is set.
    1445      *
    1446      * @since 2.1.0
    1447      *
    1448      * @param bool|string $approved    The approval status. Accepts 1, 0, or 'spam'.
    1449      * @param array       $commentdata Comment data.
    1450      */
    1451     $approved = apply_filters( 'pre_comment_approved', $approved, $commentdata );
    1452     return $approved;
    1453 }
    1454 
    1455 /**
    1456  * Check whether comment flooding is occurring.
    1457  *
    1458  * Won't run, if current user can manage options, so to not block
    1459  * administrators.
    1460  *
    1461  * @since 2.3.0
    1462  *
    1463  * @global wpdb $wpdb WordPress database abstraction object.
    1464  *
    1465  * @param string $ip Comment IP.
    1466  * @param string $email Comment author email address.
    1467  * @param string $date MySQL time string.
    1468  */
    1469 function check_comment_flood_db( $ip, $email, $date ) {
    1470     global $wpdb;
    1471     if ( current_user_can( 'manage_options' ) )
    1472         return; // don't throttle admins
    1473     $hour_ago = gmdate( 'Y-m-d H:i:s', time() - HOUR_IN_SECONDS );
    1474     if ( $lasttime = $wpdb->get_var( $wpdb->prepare( "SELECT `comment_date_gmt` FROM `$wpdb->comments` WHERE `comment_date_gmt` >= %s AND ( `comment_author_IP` = %s OR `comment_author_email` = %s ) ORDER BY `comment_date_gmt` DESC LIMIT 1", $hour_ago, $ip, $email ) ) ) {
    1475         $time_lastcomment = mysql2date('U', $lasttime, false);
    1476         $time_newcomment  = mysql2date('U', $date, false);
    1477         /**
    1478          * Filter the comment flood status.
    1479          *
    1480          * @since 2.1.0
    1481          *
    1482          * @param bool $bool             Whether a comment flood is occurring. Default false.
    1483          * @param int  $time_lastcomment Timestamp of when the last comment was posted.
    1484          * @param int  $time_newcomment  Timestamp of when the new comment was posted.
    1485          */
    1486         $flood_die = apply_filters( 'comment_flood_filter', false, $time_lastcomment, $time_newcomment );
    1487         if ( $flood_die ) {
    1488             /**
    1489              * Fires before the comment flood message is triggered.
    1490              *
    1491              * @since 1.5.0
    1492              *
    1493              * @param int $time_lastcomment Timestamp of when the last comment was posted.
    1494              * @param int $time_newcomment  Timestamp of when the new comment was posted.
    1495              */
    1496             do_action( 'comment_flood_trigger', $time_lastcomment, $time_newcomment );
    1497 
    1498             if ( defined('DOING_AJAX') )
    1499                 die( __('You are posting comments too quickly. Slow down.') );
    1500 
    1501             wp_die( __( 'You are posting comments too quickly. Slow down.' ), 429 );
    1502         }
    1503     }
    1504 }
    1505 
    1506 /**
    1507  * Separates an array of comments into an array keyed by comment_type.
    1508  *
    1509  * @since 2.7.0
    1510  *
    1511  * @param array $comments Array of comments
    1512  * @return array Array of comments keyed by comment_type.
    1513  */
    1514 function separate_comments(&$comments) {
    1515     $comments_by_type = array('comment' => array(), 'trackback' => array(), 'pingback' => array(), 'pings' => array());
    1516     $count = count($comments);
    1517     for ( $i = 0; $i < $count; $i++ ) {
    1518         $type = $comments[$i]->comment_type;
    1519         if ( empty($type) )
    1520             $type = 'comment';
    1521         $comments_by_type[$type][] = &$comments[$i];
    1522         if ( 'trackback' == $type || 'pingback' == $type )
    1523             $comments_by_type['pings'][] = &$comments[$i];
    1524     }
    1525 
    1526     return $comments_by_type;
    1527 }
    1528 
    1529 /**
    1530  * Calculate the total number of comment pages.
    1531  *
    1532  * @since 2.7.0
    1533  *
    1534  * @uses Walker_Comment
    1535  *
    1536  * @global WP_Query $wp_query
    1537  *
    1538  * @param array $comments Optional array of comment objects. Defaults to $wp_query->comments
    1539  * @param int   $per_page Optional comments per page.
    1540  * @param bool  $threaded Optional control over flat or threaded comments.
    1541  * @return int Number of comment pages.
    1542  */
    1543 function get_comment_pages_count( $comments = null, $per_page = null, $threaded = null ) {
    1544     global $wp_query;
    1545 
    1546     if ( null === $comments && null === $per_page && null === $threaded && !empty($wp_query->max_num_comment_pages) )
    1547         return $wp_query->max_num_comment_pages;
    1548 
    1549     if ( ( ! $comments || ! is_array( $comments ) ) && ! empty( $wp_query->comments )  )
    1550         $comments = $wp_query->comments;
    1551 
    1552     if ( empty($comments) )
    1553         return 0;
    1554 
    1555     if ( ! get_option( 'page_comments' ) )
    1556         return 1;
    1557 
    1558     if ( !isset($per_page) )
    1559         $per_page = (int) get_query_var('comments_per_page');
    1560     if ( 0 === $per_page )
    1561         $per_page = (int) get_option('comments_per_page');
    1562     if ( 0 === $per_page )
    1563         return 1;
    1564 
    1565     if ( !isset($threaded) )
    1566         $threaded = get_option('thread_comments');
    1567 
    1568     if ( $threaded ) {
    1569         $walker = new Walker_Comment;
    1570         $count = ceil( $walker->get_number_of_root_elements( $comments ) / $per_page );
    1571     } else {
    1572         $count = ceil( count( $comments ) / $per_page );
    1573     }
    1574 
    1575     return $count;
    1576 }
    1577 
    1578 /**
    1579  * Calculate what page number a comment will appear on for comment paging.
    1580  *
    1581  * @since 2.7.0
    1582  *
    1583  * @global wpdb $wpdb
    1584  *
    1585  * @param int $comment_ID Comment ID.
    1586  * @param array $args Optional args.
    1587  * @return int|null Comment page number or null on error.
    1588  */
    1589 function get_page_of_comment( $comment_ID, $args = array() ) {
    1590     global $wpdb;
    1591 
    1592     if ( !$comment = get_comment( $comment_ID ) )
    1593         return;
    1594 
    1595     $defaults = array( 'type' => 'all', 'page' => '', 'per_page' => '', 'max_depth' => '' );
    1596     $args = wp_parse_args( $args, $defaults );
    1597 
    1598     if ( '' === $args['per_page'] && get_option('page_comments') )
    1599         $args['per_page'] = get_query_var('comments_per_page');
    1600     if ( empty($args['per_page']) ) {
    1601         $args['per_page'] = 0;
    1602         $args['page'] = 0;
    1603     }
    1604     if ( $args['per_page'] < 1 )
    1605         return 1;
    1606 
    1607     if ( '' === $args['max_depth'] ) {
    1608         if ( get_option('thread_comments') )
    1609             $args['max_depth'] = get_option('thread_comments_depth');
    1610         else
    1611             $args['max_depth'] = -1;
    1612     }
    1613 
    1614     // Find this comment's top level parent if threading is enabled
    1615     if ( $args['max_depth'] > 1 && 0 != $comment->comment_parent )
    1616         return get_page_of_comment( $comment->comment_parent, $args );
    1617 
    1618     $allowedtypes = array(
    1619         'comment' => '',
    1620         'pingback' => 'pingback',
    1621         'trackback' => 'trackback',
    1622     );
    1623 
    1624     $comtypewhere = ( 'all' != $args['type'] && isset($allowedtypes[$args['type']]) ) ? " AND comment_type = '" . $allowedtypes[$args['type']] . "'" : '';
    1625 
    1626     // Count comments older than this one
    1627     $oldercoms = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(comment_ID) FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_parent = 0 AND comment_approved = '1' AND comment_date_gmt < '%s'" . $comtypewhere, $comment->comment_post_ID, $comment->comment_date_gmt ) );
    1628 
    1629     // No older comments? Then it's page #1.
    1630     if ( 0 == $oldercoms )
    1631         return 1;
    1632 
    1633     // Divide comments older than this one by comments per page to get this comment's page number
    1634     return ceil( ( $oldercoms + 1 ) / $args['per_page'] );
    1635 }
    1636 
    1637 /**
    1638  * Does comment contain blacklisted characters or words.
    1639  *
    1640  * @since 1.5.0
    1641  *
    1642  * @param string $author The author of the comment
    1643  * @param string $email The email of the comment
    1644  * @param string $url The url used in the comment
    1645  * @param string $comment The comment content
    1646  * @param string $user_ip The comment author IP address
    1647  * @param string $user_agent The author's browser user agent
    1648  * @return bool True if comment contains blacklisted content, false if comment does not
    1649  */
    1650 function wp_blacklist_check($author, $email, $url, $comment, $user_ip, $user_agent) {
    1651     /**
    1652      * Fires before the comment is tested for blacklisted characters or words.
    1653      *
    1654      * @since 1.5.0
    1655      *
    1656      * @param string $author     Comment author.
    1657      * @param string $email      Comment author's email.
    1658      * @param string $url        Comment author's URL.
    1659      * @param string $comment    Comment content.
    1660      * @param string $user_ip    Comment author's IP address.
    1661      * @param string $user_agent Comment author's browser user agent.
    1662      */
    1663     do_action( 'wp_blacklist_check', $author, $email, $url, $comment, $user_ip, $user_agent );
    1664 
    1665     $mod_keys = trim( get_option('blacklist_keys') );
    1666     if ( '' == $mod_keys )
    1667         return false; // If moderation keys are empty
    1668     $words = explode("\n", $mod_keys );
    1669 
    1670     foreach ( (array) $words as $word ) {
    1671         $word = trim($word);
    1672 
    1673         // Skip empty lines
    1674         if ( empty($word) ) { continue; }
    1675 
    1676         // Do some escaping magic so that '#' chars in the
    1677         // spam words don't break things:
    1678         $word = preg_quote($word, '#');
    1679 
    1680         $pattern = "#$word#i";
    1681         if (
    1682                preg_match($pattern, $author)
    1683             || preg_match($pattern, $email)
    1684             || preg_match($pattern, $url)
    1685             || preg_match($pattern, $comment)
    1686             || preg_match($pattern, $user_ip)
    1687             || preg_match($pattern, $user_agent)
    1688          )
    1689             return true;
    1690     }
    1691     return false;
    1692 }
    1693 
    1694 /**
    1695  * Retrieve total comments for blog or single post.
    1696  *
    1697  * The properties of the returned object contain the 'moderated', 'approved',
    1698  * and spam comments for either the entire blog or single post. Those properties
    1699  * contain the amount of comments that match the status. The 'total_comments'
    1700  * property contains the integer of total comments.
    1701  *
    1702  * The comment stats are cached and then retrieved, if they already exist in the
    1703  * cache.
    1704  *
    1705  * @since 2.5.0
    1706  *
    1707  * @global wpdb $wpdb
    1708  *
    1709  * @param int $post_id Optional. Post ID.
    1710  * @return object|array Comment stats.
    1711  */
    1712 function wp_count_comments( $post_id = 0 ) {
    1713     global $wpdb;
    1714 
    1715     $post_id = (int) $post_id;
    1716 
    1717     /**
    1718      * Filter the comments count for a given post.
    1719      *
    1720      * @since 2.7.0
    1721      *
    1722      * @param array $count   An empty array.
    1723      * @param int   $post_id The post ID.
    1724      */
    1725     $stats = apply_filters( 'wp_count_comments', array(), $post_id );
    1726     if ( !empty($stats) )
    1727         return $stats;
    1728 
    1729     $count = wp_cache_get("comments-{$post_id}", 'counts');
    1730 
    1731     if ( false !== $count )
    1732         return $count;
    1733 
    1734     $where = '';
    1735     if ( $post_id > 0 )
    1736         $where = $wpdb->prepare( "WHERE comment_post_ID = %d", $post_id );
    1737 
    1738     $count = $wpdb->get_results( "SELECT comment_approved, COUNT( * ) AS num_comments FROM {$wpdb->comments} {$where} GROUP BY comment_approved", ARRAY_A );
    1739 
    1740     $total = 0;
    1741     $approved = array('0' => 'moderated', '1' => 'approved', 'spam' => 'spam', 'trash' => 'trash', 'post-trashed' => 'post-trashed');
    1742     foreach ( (array) $count as $row ) {
    1743         // Don't count post-trashed toward totals
    1744         if ( 'post-trashed' != $row['comment_approved'] && 'trash' != $row['comment_approved'] )
    1745             $total += $row['num_comments'];
    1746         if ( isset( $approved[$row['comment_approved']] ) )
    1747             $stats[$approved[$row['comment_approved']]] = $row['num_comments'];
    1748     }
    1749 
    1750     $stats['total_comments'] = $total;
    1751     foreach ( $approved as $key ) {
    1752         if ( empty($stats[$key]) )
    1753             $stats[$key] = 0;
    1754     }
    1755 
    1756     $stats = (object) $stats;
    1757     wp_cache_set("comments-{$post_id}", $stats, 'counts');
    1758 
    1759     return $stats;
    1760 }
    1761 
    1762 /**
    1763  * Trashes or deletes a comment.
    1764  *
    1765  * The comment is moved to trash instead of permanently deleted unless trash is
    1766  * disabled, item is already in the trash, or $force_delete is true.
    1767  *
    1768  * The post comment count will be updated if the comment was approved and has a
    1769  * post ID available.
    1770  *
    1771  * @since 2.0.0
    1772  *
    1773  * @global wpdb $wpdb WordPress database abstraction object.
    1774  *
    1775  * @param int $comment_id Comment ID
    1776  * @param bool $force_delete Whether to bypass trash and force deletion. Default is false.
    1777  * @return bool True on success, false on failure.
    1778  */
    1779 function wp_delete_comment($comment_id, $force_delete = false) {
    1780     global $wpdb;
    1781     if (!$comment = get_comment($comment_id))
    1782         return false;
    1783 
    1784     if ( !$force_delete && EMPTY_TRASH_DAYS && !in_array( wp_get_comment_status($comment_id), array( 'trash', 'spam' ) ) )
    1785         return wp_trash_comment($comment_id);
    1786 
    1787     /**
    1788      * Fires immediately before a comment is deleted from the database.
    1789      *
    1790      * @since 1.2.0
    1791      *
    1792      * @param int $comment_id The comment ID.
    1793      */
    1794     do_action( 'delete_comment', $comment_id );
    1795 
    1796     // Move children up a level.
    1797     $children = $wpdb->get_col( $wpdb->prepare("SELECT comment_ID FROM $wpdb->comments WHERE comment_parent = %d", $comment_id) );
    1798     if ( !empty($children) ) {
    1799         $wpdb->update($wpdb->comments, array('comment_parent' => $comment->comment_parent), array('comment_parent' => $comment_id));
    1800         clean_comment_cache($children);
    1801     }
    1802 
    1803     // Delete metadata
    1804     $meta_ids = $wpdb->get_col( $wpdb->prepare( "SELECT meta_id FROM $wpdb->commentmeta WHERE comment_id = %d", $comment_id ) );
    1805     foreach ( $meta_ids as $mid )
    1806         delete_metadata_by_mid( 'comment', $mid );
    1807 
    1808     if ( ! $wpdb->delete( $wpdb->comments, array( 'comment_ID' => $comment_id ) ) )
    1809         return false;
    1810 
    1811     /**
    1812      * Fires immediately after a comment is deleted from the database.
    1813      *
    1814      * @since 2.9.0
    1815      *
    1816      * @param int $comment_id The comment ID.
    1817      */
    1818     do_action( 'deleted_comment', $comment_id );
    1819 
    1820     $post_id = $comment->comment_post_ID;
    1821     if ( $post_id && $comment->comment_approved == 1 )
    1822         wp_update_comment_count($post_id);
    1823 
    1824     clean_comment_cache($comment_id);
    1825 
    1826     /** This action is documented in wp-includes/comment.php */
    1827     do_action( 'wp_set_comment_status', $comment_id, 'delete' );
    1828 
    1829     wp_transition_comment_status('delete', $comment->comment_approved, $comment);
    1830     return true;
    1831 }
    1832 
    1833 /**
    1834  * Moves a comment to the Trash
    1835  *
    1836  * If trash is disabled, comment is permanently deleted.
    1837  *
    1838  * @since 2.9.0
    1839  *
    1840  * @param int $comment_id Comment ID.
    1841  * @return bool True on success, false on failure.
    1842  */
    1843 function wp_trash_comment($comment_id) {
    1844     if ( !EMPTY_TRASH_DAYS )
    1845         return wp_delete_comment($comment_id, true);
    1846 
    1847     if ( !$comment = get_comment($comment_id) )
    1848         return false;
    1849 
    1850     /**
    1851      * Fires immediately before a comment is sent to the Trash.
    1852      *
    1853      * @since 2.9.0
    1854      *
    1855      * @param int $comment_id The comment ID.
    1856      */
    1857     do_action( 'trash_comment', $comment_id );
    1858 
    1859     if ( wp_set_comment_status($comment_id, 'trash') ) {
    1860         delete_comment_meta( $comment_id, '_wp_trash_meta_status' );
    1861         delete_comment_meta( $comment_id, '_wp_trash_meta_time' );
    1862         add_comment_meta( $comment_id, '_wp_trash_meta_status', $comment->comment_approved );
    1863         add_comment_meta( $comment_id, '_wp_trash_meta_time', time() );
    1864 
    1865         /**
    1866          * Fires immediately after a comment is sent to Trash.
    1867          *
    1868          * @since 2.9.0
    1869          *
    1870          * @param int $comment_id The comment ID.
    1871          */
    1872         do_action( 'trashed_comment', $comment_id );
    1873         return true;
    1874     }
    1875 
    1876     return false;
    1877 }
    1878 
    1879 /**
    1880  * Removes a comment from the Trash
    1881  *
    1882  * @since 2.9.0
    1883  *
    1884  * @param int $comment_id Comment ID.
    1885  * @return bool True on success, false on failure.
    1886  */
    1887 function wp_untrash_comment($comment_id) {
    1888     if ( ! (int)$comment_id )
    1889         return false;
    1890 
    1891     /**
    1892      * Fires immediately before a comment is restored from the Trash.
    1893      *
    1894      * @since 2.9.0
    1895      *
    1896      * @param int $comment_id The comment ID.
    1897      */
    1898     do_action( 'untrash_comment', $comment_id );
    1899 
    1900     $status = (string) get_comment_meta($comment_id, '_wp_trash_meta_status', true);
    1901     if ( empty($status) )
    1902         $status = '0';
    1903 
    1904     if ( wp_set_comment_status($comment_id, $status) ) {
    1905         delete_comment_meta($comment_id, '_wp_trash_meta_time');
    1906         delete_comment_meta($comment_id, '_wp_trash_meta_status');
    1907         /**
    1908          * Fires immediately after a comment is restored from the Trash.
    1909          *
    1910          * @since 2.9.0
    1911          *
    1912          * @param int $comment_id The comment ID.
    1913          */
    1914         do_action( 'untrashed_comment', $comment_id );
    1915         return true;
    1916     }
    1917 
    1918     return false;
    1919 }
    1920 
    1921 /**
    1922  * Marks a comment as Spam
    1923  *
    1924  * @since 2.9.0
    1925  *
    1926  * @param int $comment_id Comment ID.
    1927  * @return bool True on success, false on failure.
    1928  */
    1929 function wp_spam_comment($comment_id) {
    1930     if ( !$comment = get_comment($comment_id) )
    1931         return false;
    1932 
    1933     /**
    1934      * Fires immediately before a comment is marked as Spam.
    1935      *
    1936      * @since 2.9.0
    1937      *
    1938      * @param int $comment_id The comment ID.
    1939      */
    1940     do_action( 'spam_comment', $comment_id );
    1941 
    1942     if ( wp_set_comment_status($comment_id, 'spam') ) {
    1943         delete_comment_meta( $comment_id, '_wp_trash_meta_status' );
    1944         delete_comment_meta( $comment_id, '_wp_trash_meta_time' );
    1945         add_comment_meta( $comment_id, '_wp_trash_meta_status', $comment->comment_approved );
    1946         add_comment_meta( $comment_id, '_wp_trash_meta_time', time() );
    1947         /**
    1948          * Fires immediately after a comment is marked as Spam.
    1949          *
    1950          * @since 2.9.0
    1951          *
    1952          * @param int $comment_id The comment ID.
    1953          */
    1954         do_action( 'spammed_comment', $comment_id );
    1955         return true;
    1956     }
    1957 
    1958     return false;
    1959 }
    1960 
    1961 /**
    1962  * Removes a comment from the Spam
    1963  *
    1964  * @since 2.9.0
    1965  *
    1966  * @param int $comment_id Comment ID.
    1967  * @return bool True on success, false on failure.
    1968  */
    1969 function wp_unspam_comment($comment_id) {
    1970     if ( ! (int)$comment_id )
    1971         return false;
    1972 
    1973     /**
    1974      * Fires immediately before a comment is unmarked as Spam.
    1975      *
    1976      * @since 2.9.0
    1977      *
    1978      * @param int $comment_id The comment ID.
    1979      */
    1980     do_action( 'unspam_comment', $comment_id );
    1981 
    1982     $status = (string) get_comment_meta($comment_id, '_wp_trash_meta_status', true);
    1983     if ( empty($status) )
    1984         $status = '0';
    1985 
    1986     if ( wp_set_comment_status($comment_id, $status) ) {
    1987         delete_comment_meta( $comment_id, '_wp_trash_meta_status' );
    1988         delete_comment_meta( $comment_id, '_wp_trash_meta_time' );
    1989         /**
    1990          * Fires immediately after a comment is unmarked as Spam.
    1991          *
    1992          * @since 2.9.0
    1993          *
    1994          * @param int $comment_id The comment ID.
    1995          */
    1996         do_action( 'unspammed_comment', $comment_id );
    1997         return true;
    1998     }
    1999 
    2000     return false;
    2001 }
    2002 
    2003 /**
    2004  * The status of a comment by ID.
    2005  *
    2006  * @since 1.0.0
    2007  *
    2008  * @param int $comment_id Comment ID
    2009  * @return false|string Status might be 'trash', 'approved', 'unapproved', 'spam'. False on failure.
    2010  */
    2011 function wp_get_comment_status($comment_id) {
    2012     $comment = get_comment($comment_id);
    2013     if ( !$comment )
    2014         return false;
    2015 
    2016     $approved = $comment->comment_approved;
    2017 
    2018     if ( $approved == null )
    2019         return false;
    2020     elseif ( $approved == '1' )
    2021         return 'approved';
    2022     elseif ( $approved == '0' )
    2023         return 'unapproved';
    2024     elseif ( $approved == 'spam' )
    2025         return 'spam';
    2026     elseif ( $approved == 'trash' )
    2027         return 'trash';
    2028     else
    2029         return false;
    2030 }
    2031 
    2032 /**
    2033  * Call hooks for when a comment status transition occurs.
    2034  *
    2035  * Calls hooks for comment status transitions. If the new comment status is not the same
    2036  * as the previous comment status, then two hooks will be ran, the first is
    2037  * 'transition_comment_status' with new status, old status, and comment data. The
    2038  * next action called is 'comment_OLDSTATUS_to_NEWSTATUS' the NEWSTATUS is the
    2039  * $new_status parameter and the OLDSTATUS is $old_status parameter; it has the
    2040  * comment data.
    2041  *
    2042  * The final action will run whether or not the comment statuses are the same. The
    2043  * action is named 'comment_NEWSTATUS_COMMENTTYPE', NEWSTATUS is from the $new_status
    2044  * parameter and COMMENTTYPE is comment_type comment data.
    2045  *
    2046  * @since 2.7.0
    2047  *
    2048  * @param string $new_status New comment status.
    2049  * @param string $old_status Previous comment status.
    2050  * @param object $comment Comment data.
    2051  */
    2052 function wp_transition_comment_status($new_status, $old_status, $comment) {
    2053     /*
    2054      * Translate raw statuses to human readable formats for the hooks.
    2055      * This is not a complete list of comment status, it's only the ones
    2056      * that need to be renamed
    2057      */
    2058     $comment_statuses = array(
    2059         0         => 'unapproved',
    2060         'hold'    => 'unapproved', // wp_set_comment_status() uses "hold"
    2061         1         => 'approved',
    2062         'approve' => 'approved', // wp_set_comment_status() uses "approve"
    2063     );
    2064     if ( isset($comment_statuses[$new_status]) ) $new_status = $comment_statuses[$new_status];
    2065     if ( isset($comment_statuses[$old_status]) ) $old_status = $comment_statuses[$old_status];
    2066 
    2067     // Call the hooks
    2068     if ( $new_status != $old_status ) {
    2069         /**
    2070          * Fires when the comment status is in transition.
    2071          *
    2072          * @since 2.7.0
    2073          *
    2074          * @param int|string $new_status The new comment status.
    2075          * @param int|string $old_status The old comment status.
    2076          * @param object     $comment    The comment data.
    2077          */
    2078         do_action( 'transition_comment_status', $new_status, $old_status, $comment );
    2079         /**
    2080          * Fires when the comment status is in transition from one specific status to another.
    2081          *
    2082          * The dynamic portions of the hook name, `$old_status`, and `$new_status`,
    2083          * refer to the old and new comment statuses, respectively.
    2084          *
    2085          * @since 2.7.0
    2086          *
    2087          * @param object $comment Comment object.
    2088          */
    2089         do_action( "comment_{$old_status}_to_{$new_status}", $comment );
    2090     }
    2091     /**
    2092      * Fires when the status of a specific comment type is in transition.
    2093      *
    2094      * The dynamic portions of the hook name, `$new_status`, and `$comment->comment_type`,
    2095      * refer to the new comment status, and the type of comment, respectively.
    2096      *
    2097      * Typical comment types include an empty string (standard comment), 'pingback',
    2098      * or 'trackback'.
    2099      *
    2100      * @since 2.7.0
    2101      *
    2102      * @param int $comment_ID The comment ID.
    2103      * @param obj $comment    Comment object.
    2104      */
    2105     do_action( "comment_{$new_status}_{$comment->comment_type}", $comment->comment_ID, $comment );
    2106 }
    2107 
    2108 /**
    2109  * Get current commenter's name, email, and URL.
    2110  *
    2111  * Expects cookies content to already be sanitized. User of this function might
    2112  * wish to recheck the returned array for validity.
    2113  *
    2114  * @see sanitize_comment_cookies() Use to sanitize cookies
    2115  *
    2116  * @since 2.0.4
    2117  *
    2118  * @return array Comment author, email, url respectively.
    2119  */
    2120 function wp_get_current_commenter() {
    2121     // Cookies should already be sanitized.
    2122 
    2123     $comment_author = '';
    2124     if ( isset($_COOKIE['comment_author_'.COOKIEHASH]) )
    2125         $comment_author = $_COOKIE['comment_author_'.COOKIEHASH];
    2126 
    2127     $comment_author_email = '';
    2128     if ( isset($_COOKIE['comment_author_email_'.COOKIEHASH]) )
    2129         $comment_author_email = $_COOKIE['comment_author_email_'.COOKIEHASH];
    2130 
    2131     $comment_author_url = '';
    2132     if ( isset($_COOKIE['comment_author_url_'.COOKIEHASH]) )
    2133         $comment_author_url = $_COOKIE['comment_author_url_'.COOKIEHASH];
    2134 
    2135     /**
    2136      * Filter the current commenter's name, email, and URL.
    2137      *
    2138      * @since 3.1.0
    2139      *
    2140      * @param string $comment_author       Comment author's name.
    2141      * @param string $comment_author_email Comment author's email.
    2142      * @param string $comment_author_url   Comment author's URL.
    2143      */
    2144     return apply_filters( 'wp_get_current_commenter', compact('comment_author', 'comment_author_email', 'comment_author_url') );
    2145 }
    2146 
    2147 /**
    2148  * Inserts a comment into the database.
    2149  *
    2150  * @since 2.0.0
    2151  *
    2152  * @global wpdb $wpdb WordPress database abstraction object.
    2153  *
    2154  * @param array $commentdata {
    2155  *     Array of arguments for inserting a new comment.
    2156  *
    2157  *     @type string     $comment_agent        The HTTP user agent of the `$comment_author` when
    2158  *                                            the comment was submitted. Default empty.
    2159  *     @type int|string $comment_approved     Whether the comment has been approved. Default 1.
    2160  *     @type string     $comment_author       The name of the author of the comment. Default empty.
    2161  *     @type string     $comment_author_email The email address of the `$comment_author`. Default empty.
    2162  *     @type string     $comment_author_IP    The IP address of the `$comment_author`. Default empty.
    2163  *     @type string     $comment_author_url   The URL address of the `$comment_author`. Default empty.
    2164  *     @type string     $comment_content      The content of the comment. Default empty.
    2165  *     @type string     $comment_date         The date the comment was submitted. To set the date
    2166  *                                            manually, `$comment_date_gmt` must also be specified.
    2167  *                                            Default is the current time.
    2168  *     @type string     $comment_date_gmt     The date the comment was submitted in the GMT timezone.
    2169  *                                            Default is `$comment_date` in the site's GMT timezone.
    2170  *     @type int        $comment_karma        The karma of the comment. Default 0.
    2171  *     @type int        $comment_parent       ID of this comment's parent, if any. Default 0.
    2172  *     @type int        $comment_post_ID      ID of the post that relates to the comment, if any.
    2173  *                                            Default empty.
    2174  *     @type string     $comment_type         Comment type. Default empty.
    2175  *     @type int        $user_id              ID of the user who submitted the comment. Default 0.
    2176  * }
    2177  * @return int|false The new comment's ID on success, false on failure.
    2178  */
    2179 function wp_insert_comment( $commentdata ) {
    2180     global $wpdb;
    2181     $data = wp_unslash( $commentdata );
    2182 
    2183     $comment_author       = ! isset( $data['comment_author'] )       ? '' : $data['comment_author'];
    2184     $comment_author_email = ! isset( $data['comment_author_email'] ) ? '' : $data['comment_author_email'];
    2185     $comment_author_url   = ! isset( $data['comment_author_url'] )   ? '' : $data['comment_author_url'];
    2186     $comment_author_IP    = ! isset( $data['comment_author_IP'] )    ? '' : $data['comment_author_IP'];
    2187 
    2188     $comment_date     = ! isset( $data['comment_date'] )     ? current_time( 'mysql' )            : $data['comment_date'];
    2189     $comment_date_gmt = ! isset( $data['comment_date_gmt'] ) ? get_gmt_from_date( $comment_date ) : $data['comment_date_gmt'];
    2190 
    2191     $comment_post_ID  = ! isset( $data['comment_post_ID'] )  ? '' : $data['comment_post_ID'];
    2192     $comment_content  = ! isset( $data['comment_content'] )  ? '' : $data['comment_content'];
    2193     $comment_karma    = ! isset( $data['comment_karma'] )    ? 0  : $data['comment_karma'];
    2194     $comment_approved = ! isset( $data['comment_approved'] ) ? 1  : $data['comment_approved'];
    2195     $comment_agent    = ! isset( $data['comment_agent'] )    ? '' : $data['comment_agent'];
    2196     $comment_type     = ! isset( $data['comment_type'] )     ? '' : $data['comment_type'];
    2197     $comment_parent   = ! isset( $data['comment_parent'] )   ? 0  : $data['comment_parent'];
    2198 
    2199     $user_id  = ! isset( $data['user_id'] ) ? 0 : $data['user_id'];
    2200 
    2201     $compacted = compact( 'comment_post_ID', 'comment_author', 'comment_author_email', 'comment_author_url', 'comment_author_IP', 'comment_date', 'comment_date_gmt', 'comment_content', 'comment_karma', 'comment_approved', 'comment_agent', 'comment_type', 'comment_parent', 'user_id' );
    2202     if ( ! $wpdb->insert( $wpdb->comments, $compacted ) ) {
    2203         return false;
    2204     }
    2205 
    2206     $id = (int) $wpdb->insert_id;
    2207 
    2208     if ( $comment_approved == 1 ) {
    2209         wp_update_comment_count( $comment_post_ID );
    2210     }
    2211     $comment = get_comment( $id );
    2212 
    2213     /**
    2214      * Fires immediately after a comment is inserted into the database.
    2215      *
    2216      * @since 2.8.0
    2217      *
    2218      * @param int $id      The comment ID.
    2219      * @param obj $comment Comment object.
    2220      */
    2221     do_action( 'wp_insert_comment', $id, $comment );
    2222 
    2223     wp_cache_set( 'last_changed', microtime(), 'comment' );
    2224 
    2225     return $id;
    2226 }
    2227 
    2228 /**
    2229  * Filters and sanitizes comment data.
    2230  *
    2231  * Sets the comment data 'filtered' field to true when finished. This can be
    2232  * checked as to whether the comment should be filtered and to keep from
    2233  * filtering the same comment more than once.
    2234  *
    2235  * @since 2.0.0
    2236  *
    2237  * @param array $commentdata Contains information on the comment.
    2238  * @return array Parsed comment information.
    2239  */
    2240 function wp_filter_comment($commentdata) {
    2241     if ( isset( $commentdata['user_ID'] ) ) {
    2242         /**
    2243          * Filter the comment author's user id before it is set.
    2244          *
    2245          * The first time this filter is evaluated, 'user_ID' is checked
    2246          * (for back-compat), followed by the standard 'user_id' value.
    2247          *
    2248          * @since 1.5.0
    2249          *
    2250          * @param int $user_ID The comment author's user ID.
    2251          */
    2252         $commentdata['user_id'] = apply_filters( 'pre_user_id', $commentdata['user_ID'] );
    2253     } elseif ( isset( $commentdata['user_id'] ) ) {
    2254         /** This filter is documented in wp-includes/comment.php */
    2255         $commentdata['user_id'] = apply_filters( 'pre_user_id', $commentdata['user_id'] );
    2256     }
    2257 
    2258     /**
    2259      * Filter the comment author's browser user agent before it is set.
    2260      *
    2261      * @since 1.5.0
    2262      *
    2263      * @param int $comment_agent The comment author's browser user agent.
    2264      */
    2265     $commentdata['comment_agent'] = apply_filters( 'pre_comment_user_agent', ( isset( $commentdata['comment_agent'] ) ? $commentdata['comment_agent'] : '' ) );
    2266     /** This filter is documented in wp-includes/comment.php */
    2267     $commentdata['comment_author'] = apply_filters( 'pre_comment_author_name', $commentdata['comment_author'] );
    2268     /**
    2269      * Filter the comment content before it is set.
    2270      *
    2271      * @since 1.5.0
    2272      *
    2273      * @param int $comment_content The comment content.
    2274      */
    2275     $commentdata['comment_content'] = apply_filters( 'pre_comment_content', $commentdata['comment_content'] );
    2276     /**
    2277      * Filter the comment author's IP before it is set.
    2278      *
    2279      * @since 1.5.0
    2280      *
    2281      * @param int $comment_author_ip The comment author's IP.
    2282      */
    2283     $commentdata['comment_author_IP'] = apply_filters( 'pre_comment_user_ip', $commentdata['comment_author_IP'] );
    2284     /** This filter is documented in wp-includes/comment.php */
    2285     $commentdata['comment_author_url'] = apply_filters( 'pre_comment_author_url', $commentdata['comment_author_url'] );
    2286     /** This filter is documented in wp-includes/comment.php */
    2287     $commentdata['comment_author_email'] = apply_filters( 'pre_comment_author_email', $commentdata['comment_author_email'] );
    2288     $commentdata['filtered'] = true;
    2289     return $commentdata;
    2290 }
    2291 
    2292 /**
    2293  * Whether a comment should be blocked because of comment flood.
    2294  *
    2295  * @since 2.1.0
    2296  *
    2297  * @param bool $block Whether plugin has already blocked comment.
    2298  * @param int $time_lastcomment Timestamp for last comment.
    2299  * @param int $time_newcomment Timestamp for new comment.
    2300  * @return bool Whether comment should be blocked.
    2301  */
    2302 function wp_throttle_comment_flood($block, $time_lastcomment, $time_newcomment) {
    2303     if ( $block ) // a plugin has already blocked... we'll let that decision stand
    2304         return $block;
    2305     if ( ($time_newcomment - $time_lastcomment) < 15 )
    2306         return true;
    2307     return false;
    2308 }
    2309 
    2310 /**
    2311  * Adds a new comment to the database.
    2312  *
    2313  * Filters new comment to ensure that the fields are sanitized and valid before
    2314  * inserting comment into database. Calls 'comment_post' action with comment ID
    2315  * and whether comment is approved by WordPress. Also has 'preprocess_comment'
    2316  * filter for processing the comment data before the function handles it.
    2317  *
    2318  * We use REMOTE_ADDR here directly. If you are behind a proxy, you should ensure
    2319  * that it is properly set, such as in wp-config.php, for your environment.
    2320  * See {@link https://core.trac.wordpress.org/ticket/9235}
    2321  *
    2322  * @since 1.5.0
    2323  * @since 4.3.0 'comment_agent' and 'comment_author_IP' can be set via `$commentdata`.
    2324  *
    2325  * @see wp_insert_comment()
    2326  *
    2327  * @global wpdb $wpdb
    2328  *
    2329  * @param array $commentdata {
    2330  *     Comment data.
    2331  *
    2332  *     @type string $comment_author       The name of the comment author.
    2333  *     @type string $comment_author_email The comment author email address.
    2334  *     @type string $comment_author_url   The comment author URL.
    2335  *     @type string $comment_content      The content of the comment.
    2336  *     @type string $comment_date         The date the comment was submitted. Default is the current time.
    2337  *     @type string $comment_date_gmt     The date the comment was submitted in the GMT timezone.
    2338  *                                        Default is `$comment_date` in the GMT timezone.
    2339  *     @type int    $comment_parent       The ID of this comment's parent, if any. Default 0.
    2340  *     @type int    $comment_post_ID      The ID of the post that relates to the comment.
    2341  *     @type int    $user_id              The ID of the user who submitted the comment. Default 0.
    2342  *     @type int    $user_ID              Kept for backward-compatibility. Use `$user_id` instead.
    2343  *     @type string $comment_agent        Comment author user agent. Default is the value of 'HTTP_USER_AGENT'
    2344  *                                        in the `$_SERVER` superglobal sent in the original request.
    2345  *     @type string $comment_author_IP    Comment author IP address in IPv4 format. Default is the value of
    2346  *                                        'REMOTE_ADDR' in the `$_SERVER` superglobal sent in the original request.
    2347  * }
    2348  * @return int|false The ID of the comment on success, false on failure.
    2349  */
    2350 function wp_new_comment( $commentdata ) {
    2351     global $wpdb;
    2352 
    2353     if ( isset( $commentdata['user_ID'] ) ) {
    2354         $commentdata['user_id'] = $commentdata['user_ID'] = (int) $commentdata['user_ID'];
    2355     }
    2356 
    2357     $prefiltered_user_id = ( isset( $commentdata['user_id'] ) ) ? (int) $commentdata['user_id'] : 0;
    2358 
    2359     /**
    2360      * Filter a comment's data before it is sanitized and inserted into the database.
    2361      *
    2362      * @since 1.5.0
    2363      *
    2364      * @param array $commentdata Comment data.
    2365      */
    2366     $commentdata = apply_filters( 'preprocess_comment', $commentdata );
    2367 
    2368     $commentdata['comment_post_ID'] = (int) $commentdata['comment_post_ID'];
    2369     if ( isset( $commentdata['user_ID'] ) && $prefiltered_user_id !== (int) $commentdata['user_ID'] ) {
    2370         $commentdata['user_id'] = $commentdata['user_ID'] = (int) $commentdata['user_ID'];
    2371     } elseif ( isset( $commentdata['user_id'] ) ) {
    2372         $commentdata['user_id'] = (int) $commentdata['user_id'];
    2373     }
    2374 
    2375     $commentdata['comment_parent'] = isset($commentdata['comment_parent']) ? absint($commentdata['comment_parent']) : 0;
    2376     $parent_status = ( 0 < $commentdata['comment_parent'] ) ? wp_get_comment_status($commentdata['comment_parent']) : '';
    2377     $commentdata['comment_parent'] = ( 'approved' == $parent_status || 'unapproved' == $parent_status ) ? $commentdata['comment_parent'] : 0;
    2378 
    2379     if ( ! isset( $commentdata['comment_author_IP'] ) ) {
    2380         $commentdata['comment_author_IP'] = $_SERVER['REMOTE_ADDR'];
    2381     }
    2382     $commentdata['comment_author_IP'] = preg_replace( '/[^0-9a-fA-F:., ]/', '', $commentdata['comment_author_IP'] );
    2383 
    2384     if ( ! isset( $commentdata['comment_agent'] ) ) {
    2385         $commentdata['comment_agent'] = isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT']: '';
    2386     }
    2387     $commentdata['comment_agent'] = substr( $commentdata['comment_agent'], 0, 254 );
    2388 
    2389     if ( empty( $commentdata['comment_date'] ) ) {
    2390         $commentdata['comment_date'] = current_time('mysql');
    2391     }
    2392 
    2393     if ( empty( $commentdata['comment_date_gmt'] ) ) {
    2394         $commentdata['comment_date_gmt'] = current_time( 'mysql', 1 );
    2395     }
    2396 
    2397     $commentdata = wp_filter_comment($commentdata);
    2398 
    2399     $commentdata['comment_approved'] = wp_allow_comment($commentdata);
    2400 
    2401     $comment_ID = wp_insert_comment($commentdata);
    2402     if ( ! $comment_ID ) {
    2403         $fields = array( 'comment_author', 'comment_author_email', 'comment_author_url', 'comment_content' );
    2404 
    2405         foreach ( $fields as $field ) {
    2406             if ( isset( $commentdata[ $field ] ) ) {
    2407                 $commentdata[ $field ] = $wpdb->strip_invalid_text_for_column( $wpdb->comments, $field, $commentdata[ $field ] );
    2408             }
    2409         }
    2410 
    2411         $commentdata = wp_filter_comment( $commentdata );
    2412 
    2413         $commentdata['comment_approved'] = wp_allow_comment( $commentdata );
    2414 
    2415         $comment_ID = wp_insert_comment( $commentdata );
    2416         if ( ! $comment_ID ) {
    2417             return false;
    2418         }
    2419     }
    2420 
    2421     /**
    2422      * Fires immediately after a comment is inserted into the database.
    2423      *
    2424      * @since 1.2.0
    2425      *
    2426      * @param int $comment_ID       The comment ID.
    2427      * @param int $comment_approved 1 (true) if the comment is approved, 0 (false) if not.
    2428      */
    2429     do_action( 'comment_post', $comment_ID, $commentdata['comment_approved'] );
    2430 
    2431     if ( 'spam' !== $commentdata['comment_approved'] ) { // If it's spam save it silently for later crunching
    2432         if ( '0' == $commentdata['comment_approved'] ) {
    2433             wp_notify_moderator( $comment_ID );
    2434         }
    2435 
    2436         // wp_notify_postauthor() checks if notifying the author of their own comment.
    2437         // By default, it won't, but filters can override this.
    2438         if ( get_option( 'comments_notify' ) && $commentdata['comment_approved'] ) {
    2439             wp_notify_postauthor( $comment_ID );
    2440         }
    2441     }
    2442 
    2443     return $comment_ID;
    2444 }
    2445 
    2446 /**
    2447  * Sets the status of a comment.
    2448  *
    2449  * The 'wp_set_comment_status' action is called after the comment is handled.
    2450  * If the comment status is not in the list, then false is returned.
    2451  *
    2452  * @since 1.0.0
    2453  *
    2454  * global wpdb $wpdb
    2455  *
    2456  * @param int $comment_id Comment ID.
    2457  * @param string $comment_status New comment status, either 'hold', 'approve', 'spam', or 'trash'.
    2458  * @param bool $wp_error Whether to return a WP_Error object if there is a failure. Default is false.
    2459  * @return bool|WP_Error True on success, false or WP_Error on failure.
    2460  */
    2461 function wp_set_comment_status($comment_id, $comment_status, $wp_error = false) {
    2462     global $wpdb;
    2463 
    2464     switch ( $comment_status ) {
    2465         case 'hold':
    2466         case '0':
    2467             $status = '0';
    2468             break;
    2469         case 'approve':
    2470         case '1':
    2471             $status = '1';
    2472             if ( get_option('comments_notify') ) {
    2473                 wp_notify_postauthor( $comment_id );
    2474             }
    2475             break;
    2476         case 'spam':
    2477             $status = 'spam';
    2478             break;
    2479         case 'trash':
    2480             $status = 'trash';
    2481             break;
    2482         default:
    2483             return false;
    2484     }
    2485 
    2486     $comment_old = clone get_comment($comment_id);
    2487 
    2488     if ( !$wpdb->update( $wpdb->comments, array('comment_approved' => $status), array('comment_ID' => $comment_id) ) ) {
    2489         if ( $wp_error )
    2490             return new WP_Error('db_update_error', __('Could not update comment status'), $wpdb->last_error);
    2491         else
    2492             return false;
    2493     }
    2494 
    2495     clean_comment_cache($comment_id);
    2496 
    2497     $comment = get_comment($comment_id);
    2498 
    2499     /**
    2500      * Fires immediately before transitioning a comment's status from one to another
    2501      * in the database.
    2502      *
    2503      * @since 1.5.0
    2504      *
    2505      * @param int         $comment_id     Comment ID.
    2506      * @param string|bool $comment_status Current comment status. Possible values include
    2507      *                                    'hold', 'approve', 'spam', 'trash', or false.
    2508      */
    2509     do_action( 'wp_set_comment_status', $comment_id, $comment_status );
    2510 
    2511     wp_transition_comment_status($comment_status, $comment_old->comment_approved, $comment);
    2512 
    2513     wp_update_comment_count($comment->comment_post_ID);
    2514 
    2515     return true;
    2516 }
    2517 
    2518 /**
    2519  * Updates an existing comment in the database.
    2520  *
    2521  * Filters the comment and makes sure certain fields are valid before updating.
    2522  *
    2523  * @since 2.0.0
    2524  *
    2525  * @global wpdb $wpdb WordPress database abstraction object.
    2526  *
    2527  * @param array $commentarr Contains information on the comment.
    2528  * @return int Comment was updated if value is 1, or was not updated if value is 0.
    2529  */
    2530 function wp_update_comment($commentarr) {
    2531     global $wpdb;
    2532 
    2533     // First, get all of the original fields
    2534     $comment = get_comment($commentarr['comment_ID'], ARRAY_A);
    2535     if ( empty( $comment ) ) {
    2536         return 0;
    2537     }
    2538 
    2539     // Make sure that the comment post ID is valid (if specified).
    2540     if ( isset( $commentarr['comment_post_ID'] ) && ! get_post( $commentarr['comment_post_ID'] ) ) {
    2541         return 0;
    2542     }
    2543 
    2544     // Escape data pulled from DB.
    2545     $comment = wp_slash($comment);
    2546 
    2547     $old_status = $comment['comment_approved'];
    2548 
    2549     // Merge old and new fields with new fields overwriting old ones.
    2550     $commentarr = array_merge($comment, $commentarr);
    2551 
    2552     $commentarr = wp_filter_comment( $commentarr );
    2553 
    2554     // Now extract the merged array.
    2555     $data = wp_unslash( $commentarr );
    2556 
    2557     /**
    2558      * Filter the comment content before it is updated in the database.
    2559      *
    2560      * @since 1.5.0
    2561      *
    2562      * @param string $comment_content The comment data.
    2563      */
    2564     $data['comment_content'] = apply_filters( 'comment_save_pre', $data['comment_content'] );
    2565 
    2566     $data['comment_date_gmt'] = get_gmt_from_date( $data['comment_date'] );
    2567 
    2568     if ( ! isset( $data['comment_approved'] ) ) {
    2569         $data['comment_approved'] = 1;
    2570     } elseif ( 'hold' == $data['comment_approved'] ) {
    2571         $data['comment_approved'] = 0;
    2572     } elseif ( 'approve' == $data['comment_approved'] ) {
    2573         $data['comment_approved'] = 1;
    2574     }
    2575 
    2576     $comment_ID = $data['comment_ID'];
    2577     $comment_post_ID = $data['comment_post_ID'];
    2578     $keys = array( 'comment_post_ID', 'comment_content', 'comment_author', 'comment_author_email', 'comment_approved', 'comment_karma', 'comment_author_url', 'comment_date', 'comment_date_gmt', 'comment_type', 'comment_parent', 'user_id' );
    2579     $data = wp_array_slice_assoc( $data, $keys );
    2580     $rval = $wpdb->update( $wpdb->comments, $data, compact( 'comment_ID' ) );
    2581 
    2582     clean_comment_cache( $comment_ID );
    2583     wp_update_comment_count( $comment_post_ID );
    2584     /**
    2585      * Fires immediately after a comment is updated in the database.
    2586      *
    2587      * The hook also fires immediately before comment status transition hooks are fired.
    2588      *
    2589      * @since 1.2.0
    2590      *
    2591      * @param int $comment_ID The comment ID.
    2592      */
    2593     do_action( 'edit_comment', $comment_ID );
    2594     $comment = get_comment($comment_ID);
    2595     wp_transition_comment_status($comment->comment_approved, $old_status, $comment);
    2596     return $rval;
    2597 }
    2598 
    2599 /**
    2600  * Whether to defer comment counting.
    2601  *
    2602  * When setting $defer to true, all post comment counts will not be updated
    2603  * until $defer is set to false. When $defer is set to false, then all
    2604  * previously deferred updated post comment counts will then be automatically
    2605  * updated without having to call wp_update_comment_count() after.
    2606  *
    2607  * @since 2.5.0
    2608  * @staticvar bool $_defer
    2609  *
    2610  * @param bool $defer
    2611  * @return bool
    2612  */
    2613 function wp_defer_comment_counting($defer=null) {
    2614     static $_defer = false;
    2615 
    2616     if ( is_bool($defer) ) {
    2617         $_defer = $defer;
    2618         // flush any deferred counts
    2619         if ( !$defer )
    2620             wp_update_comment_count( null, true );
    2621     }
    2622 
    2623     return $_defer;
    2624 }
    2625 
    2626 /**
    2627  * Updates the comment count for post(s).
    2628  *
    2629  * When $do_deferred is false (is by default) and the comments have been set to
    2630  * be deferred, the post_id will be added to a queue, which will be updated at a
    2631  * later date and only updated once per post ID.
    2632  *
    2633  * If the comments have not be set up to be deferred, then the post will be
    2634  * updated. When $do_deferred is set to true, then all previous deferred post
    2635  * IDs will be updated along with the current $post_id.
    2636  *
    2637  * @since 2.1.0
    2638  * @see wp_update_comment_count_now() For what could cause a false return value
    2639  *
    2640  * @staticvar array $_deferred
    2641  *
    2642  * @param int $post_id Post ID
    2643  * @param bool $do_deferred Whether to process previously deferred post comment counts
    2644  * @return bool|void True on success, false on failure
    2645  */
    2646 function wp_update_comment_count($post_id, $do_deferred=false) {
    2647     static $_deferred = array();
    2648 
    2649     if ( $do_deferred ) {
    2650         $_deferred = array_unique($_deferred);
    2651         foreach ( $_deferred as $i => $_post_id ) {
    2652             wp_update_comment_count_now($_post_id);
    2653             unset( $_deferred[$i] ); /** @todo Move this outside of the foreach and reset $_deferred to an array instead */
    2654         }
    2655     }
    2656 
    2657     if ( wp_defer_comment_counting() ) {
    2658         $_deferred[] = $post_id;
    2659         return true;
    2660     }
    2661     elseif ( $post_id ) {
    2662         return wp_update_comment_count_now($post_id);
    2663     }
    2664 
    2665 }
    2666 
    2667 /**
    2668  * Updates the comment count for the post.
    2669  *
    2670  * @since 2.5.0
    2671  *
    2672  * @global wpdb $wpdb WordPress database abstraction object.
    2673  *
    2674  * @param int $post_id Post ID
    2675  * @return bool True on success, false on '0' $post_id or if post with ID does not exist.
    2676  */
    2677 function wp_update_comment_count_now($post_id) {
    2678     global $wpdb;
    2679     $post_id = (int) $post_id;
    2680     if ( !$post_id )
    2681         return false;
    2682     if ( !$post = get_post($post_id) )
    2683         return false;
    2684 
    2685     $old = (int) $post->comment_count;
    2686     $new = (int) $wpdb->get_var( $wpdb->prepare("SELECT COUNT(*) FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_approved = '1'", $post_id) );
    2687     $wpdb->update( $wpdb->posts, array('comment_count' => $new), array('ID' => $post_id) );
    2688 
    2689     clean_post_cache( $post );
    2690 
    2691     /**
    2692      * Fires immediately after a post's comment count is updated in the database.
    2693      *
    2694      * @since 2.3.0
    2695      *
    2696      * @param int $post_id Post ID.
    2697      * @param int $new     The new comment count.
    2698      * @param int $old     The old comment count.
    2699      */
    2700     do_action( 'wp_update_comment_count', $post_id, $new, $old );
    2701     /** This action is documented in wp-includes/post.php */
    2702     do_action( 'edit_post', $post_id, $post );
    2703 
    2704     return true;
    2705 }
    2706 
    2707 //
    2708 // Ping and trackback functions.
    2709 //
    2710 
    2711 /**
    2712  * Finds a pingback server URI based on the given URL.
    2713  *
    2714  * Checks the HTML for the rel="pingback" link and x-pingback headers. It does
    2715  * a check for the x-pingback headers first and returns that, if available. The
    2716  * check for the rel="pingback" has more overhead than just the header.
    2717  *
    2718  * @since 1.5.0
    2719  *
    2720  * @param string $url URL to ping.
    2721  * @param int $deprecated Not Used.
    2722  * @return false|string False on failure, string containing URI on success.
    2723  */
    2724 function discover_pingback_server_uri( $url, $deprecated = '' ) {
    2725     if ( !empty( $deprecated ) )
    2726         _deprecated_argument( __FUNCTION__, '2.7' );
    2727 
    2728     $pingback_str_dquote = 'rel="pingback"';
    2729     $pingback_str_squote = 'rel=\'pingback\'';
    2730 
    2731     /** @todo Should use Filter Extension or custom preg_match instead. */
    2732     $parsed_url = parse_url($url);
    2733 
    2734     if ( ! isset( $parsed_url['host'] ) ) // Not an URL. This should never happen.
    2735         return false;
    2736 
    2737     //Do not search for a pingback server on our own uploads
    2738     $uploads_dir = wp_upload_dir();
    2739     if ( 0 === strpos($url, $uploads_dir['baseurl']) )
    2740         return false;
    2741 
    2742     $response = wp_safe_remote_head( $url, array( 'timeout' => 2, 'httpversion' => '1.0' ) );
    2743 
    2744     if ( is_wp_error( $response ) )
    2745         return false;
    2746 
    2747     if ( wp_remote_retrieve_header( $response, 'x-pingback' ) )
    2748         return wp_remote_retrieve_header( $response, 'x-pingback' );
    2749 
    2750     // Not an (x)html, sgml, or xml page, no use going further.
    2751     if ( preg_match('#(image|audio|video|model)/#is', wp_remote_retrieve_header( $response, 'content-type' )) )
    2752         return false;
    2753 
    2754     // Now do a GET since we're going to look in the html headers (and we're sure it's not a binary file)
    2755     $response = wp_safe_remote_get( $url, array( 'timeout' => 2, 'httpversion' => '1.0' ) );
    2756 
    2757     if ( is_wp_error( $response ) )
    2758         return false;
    2759 
    2760     $contents = wp_remote_retrieve_body( $response );
    2761 
    2762     $pingback_link_offset_dquote = strpos($contents, $pingback_str_dquote);
    2763     $pingback_link_offset_squote = strpos($contents, $pingback_str_squote);
    2764     if ( $pingback_link_offset_dquote || $pingback_link_offset_squote ) {
    2765         $quote = ($pingback_link_offset_dquote) ? '"' : '\'';
    2766         $pingback_link_offset = ($quote=='"') ? $pingback_link_offset_dquote : $pingback_link_offset_squote;
    2767         $pingback_href_pos = @strpos($contents, 'href=', $pingback_link_offset);
    2768         $pingback_href_start = $pingback_href_pos+6;
    2769         $pingback_href_end = @strpos($contents, $quote, $pingback_href_start);
    2770         $pingback_server_url_len = $pingback_href_end - $pingback_href_start;
    2771         $pingback_server_url = substr($contents, $pingback_href_start, $pingback_server_url_len);
    2772 
    2773         // We may find rel="pingback" but an incomplete pingback URL
    2774         if ( $pingback_server_url_len > 0 ) { // We got it!
    2775             return $pingback_server_url;
    2776         }
    2777     }
    2778 
    2779     return false;
    2780 }
    2781 
    2782 /**
    2783  * Perform all pingbacks, enclosures, trackbacks, and send to pingback services.
    2784  *
    2785  * @since 2.1.0
    2786  *
    2787  * @global wpdb $wpdb WordPress database abstraction object.
    2788  */
    2789 function do_all_pings() {
    2790     global $wpdb;
    2791 
    2792     // Do pingbacks
    2793     while ($ping = $wpdb->get_row("SELECT ID, post_content, meta_id FROM {$wpdb->posts}, {$wpdb->postmeta} WHERE {$wpdb->posts}.ID = {$wpdb->postmeta}.post_id AND {$wpdb->postmeta}.meta_key = '_pingme' LIMIT 1")) {
    2794         delete_metadata_by_mid( 'post', $ping->meta_id );
    2795         pingback( $ping->post_content, $ping->ID );
    2796     }
    2797 
    2798     // Do Enclosures
    2799     while ($enclosure = $wpdb->get_row("SELECT ID, post_content, meta_id FROM {$wpdb->posts}, {$wpdb->postmeta} WHERE {$wpdb->posts}.ID = {$wpdb->postmeta}.post_id AND {$wpdb->postmeta}.meta_key = '_encloseme' LIMIT 1")) {
    2800         delete_metadata_by_mid( 'post', $enclosure->meta_id );
    2801         do_enclose( $enclosure->post_content, $enclosure->ID );
    2802     }
    2803 
    2804     // Do Trackbacks
    2805     $trackbacks = $wpdb->get_col("SELECT ID FROM $wpdb->posts WHERE to_ping <> '' AND post_status = 'publish'");
    2806     if ( is_array($trackbacks) )
    2807         foreach ( $trackbacks as $trackback )
    2808             do_trackbacks($trackback);
    2809 
    2810     //Do Update Services/Generic Pings
    2811     generic_ping();
    2812 }
    2813 
    2814 /**
    2815  * Perform trackbacks.
    2816  *
    2817  * @since 1.5.0
    2818  *
    2819  * @global wpdb $wpdb WordPress database abstraction object.
    2820  *
    2821  * @param int $post_id Post ID to do trackbacks on.
    2822  */
    2823 function do_trackbacks($post_id) {
    2824     global $wpdb;
    2825 
    2826     $post = get_post( $post_id );
    2827     $to_ping = get_to_ping($post_id);
    2828     $pinged  = get_pung($post_id);
    2829     if ( empty($to_ping) ) {
    2830         $wpdb->update($wpdb->posts, array('to_ping' => ''), array('ID' => $post_id) );
    2831         return;
    2832     }
    2833 
    2834     if ( empty($post->post_excerpt) ) {
    2835         /** This filter is documented in wp-includes/post-template.php */
    2836         $excerpt = apply_filters( 'the_content', $post->post_content, $post->ID );
    2837     } else {
    2838         /** This filter is documented in wp-includes/post-template.php */
    2839         $excerpt = apply_filters( 'the_excerpt', $post->post_excerpt );
    2840     }
    2841 
    2842     $excerpt = str_replace(']]>', ']]&gt;', $excerpt);
    2843     $excerpt = wp_html_excerpt($excerpt, 252, '&#8230;');
    2844 
    2845     /** This filter is documented in wp-includes/post-template.php */
    2846     $post_title = apply_filters( 'the_title', $post->post_title, $post->ID );
    2847     $post_title = strip_tags($post_title);
    2848 
    2849     if ( $to_ping ) {
    2850         foreach ( (array) $to_ping as $tb_ping ) {
    2851             $tb_ping = trim($tb_ping);
    2852             if ( !in_array($tb_ping, $pinged) ) {
    2853                 trackback($tb_ping, $post_title, $excerpt, $post_id);
    2854                 $pinged[] = $tb_ping;
    2855             } else {
    2856                 $wpdb->query( $wpdb->prepare("UPDATE $wpdb->posts SET to_ping = TRIM(REPLACE(to_ping, %s, '')) WHERE ID = %d", $tb_ping, $post_id) );
    2857             }
    2858         }
    2859     }
    2860 }
    2861 
    2862 /**
    2863  * Sends pings to all of the ping site services.
    2864  *
    2865  * @since 1.2.0
    2866  *
    2867  * @param int $post_id Post ID.
    2868  * @return int Same as Post ID from parameter
    2869  */
    2870 function generic_ping( $post_id = 0 ) {
    2871     $services = get_option('ping_sites');
    2872 
    2873     $services = explode("\n", $services);
    2874     foreach ( (array) $services as $service ) {
    2875         $service = trim($service);
    2876         if ( '' != $service )
    2877             weblog_ping($service);
    2878     }
    2879 
    2880     return $post_id;
    2881 }
    2882 
    2883 /**
    2884  * Pings back the links found in a post.
    2885  *
    2886  * @since 0.71
    2887  *
    2888  * @global string $wp_version
    2889  *
    2890  * @param string $content Post content to check for links.
    2891  * @param int $post_ID Post ID.
    2892  */
    2893 function pingback($content, $post_ID) {
    2894     global $wp_version;
    2895     include_once(ABSPATH . WPINC . '/class-IXR.php');
    2896     include_once(ABSPATH . WPINC . '/class-wp-http-ixr-client.php');
    2897 
    2898     // original code by Mort (http://mort.mine.nu:8080)
    2899     $post_links = array();
    2900 
    2901     $pung = get_pung($post_ID);
    2902 
    2903     // Step 1
    2904     // Parsing the post, external links (if any) are stored in the $post_links array
    2905     $post_links_temp = wp_extract_urls( $content );
    2906 
    2907     // Step 2.
    2908     // Walking thru the links array
    2909     // first we get rid of links pointing to sites, not to specific files
    2910     // Example:
    2911     // http://dummy-weblog.org
    2912     // http://dummy-weblog.org/
    2913     // http://dummy-weblog.org/post.php
    2914     // We don't wanna ping first and second types, even if they have a valid <link/>
    2915 
    2916     foreach ( (array) $post_links_temp as $link_test ) :
    2917         if ( !in_array($link_test, $pung) && (url_to_postid($link_test) != $post_ID) // If we haven't pung it already and it isn't a link to itself
    2918                 && !is_local_attachment($link_test) ) : // Also, let's never ping local attachments.
    2919             if ( $test = @parse_url($link_test) ) {
    2920                 if ( isset($test['query']) )
    2921                     $post_links[] = $link_test;
    2922                 elseif ( isset( $test['path'] ) && ( $test['path'] != '/' ) && ( $test['path'] != '' ) )
    2923                     $post_links[] = $link_test;
    2924             }
    2925         endif;
    2926     endforeach;
    2927 
    2928     $post_links = array_unique( $post_links );
    2929     /**
    2930      * Fires just before pinging back links found in a post.
    2931      *
    2932      * @since 2.0.0
    2933      *
    2934      * @param array &$post_links An array of post links to be checked, passed by reference.
    2935      * @param array &$pung       Whether a link has already been pinged, passed by reference.
    2936      * @param int   $post_ID     The post ID.
    2937      */
    2938     do_action_ref_array( 'pre_ping', array( &$post_links, &$pung, $post_ID ) );
    2939 
    2940     foreach ( (array) $post_links as $pagelinkedto ) {
    2941         $pingback_server_url = discover_pingback_server_uri( $pagelinkedto );
    2942 
    2943         if ( $pingback_server_url ) {
    2944             @ set_time_limit( 60 );
    2945             // Now, the RPC call
    2946             $pagelinkedfrom = get_permalink($post_ID);
    2947 
    2948             // using a timeout of 3 seconds should be enough to cover slow servers
    2949             $client = new WP_HTTP_IXR_Client($pingback_server_url);
    2950             $client->timeout = 3;
    2951             /**
    2952              * Filter the user agent sent when pinging-back a URL.
    2953              *
    2954              * @since 2.9.0
    2955              *
    2956              * @param string $concat_useragent    The user agent concatenated with ' -- WordPress/'
    2957              *                                    and the WordPress version.
    2958              * @param string $useragent           The useragent.
    2959              * @param string $pingback_server_url The server URL being linked to.
    2960              * @param string $pagelinkedto        URL of page linked to.
    2961              * @param string $pagelinkedfrom      URL of page linked from.
    2962              */
    2963             $client->useragent = apply_filters( 'pingback_useragent', $client->useragent . ' -- WordPress/' . $wp_version, $client->useragent, $pingback_server_url, $pagelinkedto, $pagelinkedfrom );
    2964             // when set to true, this outputs debug messages by itself
    2965             $client->debug = false;
    2966 
    2967             if ( $client->query('pingback.ping', $pagelinkedfrom, $pagelinkedto) || ( isset($client->error->code) && 48 == $client->error->code ) ) // Already registered
    2968                 add_ping( $post_ID, $pagelinkedto );
    2969         }
    2970     }
    2971 }
    2972 
    2973 /**
    2974  * Check whether blog is public before returning sites.
    2975  *
    2976  * @since 2.1.0
    2977  *
    2978  * @param mixed $sites Will return if blog is public, will not return if not public.
    2979  * @return mixed Empty string if blog is not public, returns $sites, if site is public.
    2980  */
    2981 function privacy_ping_filter($sites) {
    2982     if ( '0' != get_option('blog_public') )
    2983         return $sites;
    2984     else
    2985         return '';
    2986 }
    2987 
    2988 /**
    2989  * Send a Trackback.
    2990  *
    2991  * Updates database when sending trackback to prevent duplicates.
    2992  *
    2993  * @since 0.71
    2994  *
    2995  * @global wpdb $wpdb WordPress database abstraction object.
    2996  *
    2997  * @param string $trackback_url URL to send trackbacks.
    2998  * @param string $title Title of post.
    2999  * @param string $excerpt Excerpt of post.
    3000  * @param int $ID Post ID.
    3001  * @return int|false|void Database query from update.
    3002  */
    3003 function trackback($trackback_url, $title, $excerpt, $ID) {
    3004     global $wpdb;
    3005 
    3006     if ( empty($trackback_url) )
    3007         return;
    3008 
    3009     $options = array();
    3010     $options['timeout'] = 4;
    3011     $options['body'] = array(
    3012         'title' => $title,
    3013         'url' => get_permalink($ID),
    3014         'blog_name' => get_option('blogname'),
    3015         'excerpt' => $excerpt
    3016     );
    3017 
    3018     $response = wp_safe_remote_post( $trackback_url, $options );
    3019 
    3020     if ( is_wp_error( $response ) )
    3021         return;
    3022 
    3023     $wpdb->query( $wpdb->prepare("UPDATE $wpdb->posts SET pinged = CONCAT(pinged, '\n', %s) WHERE ID = %d", $trackback_url, $ID) );
    3024     return $wpdb->query( $wpdb->prepare("UPDATE $wpdb->posts SET to_ping = TRIM(REPLACE(to_ping, %s, '')) WHERE ID = %d", $trackback_url, $ID) );
    3025 }
    3026 
    3027 /**
    3028  * Send a pingback.
    3029  *
    3030  * @since 1.2.0
    3031  *
    3032  * @global string $wp_version
    3033  *
    3034  * @param string $server Host of blog to connect to.
    3035  * @param string $path Path to send the ping.
    3036  */
    3037 function weblog_ping($server = '', $path = '') {
    3038     global $wp_version;
    3039     include_once(ABSPATH . WPINC . '/class-IXR.php');
    3040     include_once(ABSPATH . WPINC . '/class-wp-http-ixr-client.php');
    3041 
    3042     // using a timeout of 3 seconds should be enough to cover slow servers
    3043     $client = new WP_HTTP_IXR_Client($server, ((!strlen(trim($path)) || ('/' == $path)) ? false : $path));
    3044     $client->timeout = 3;
    3045     $client->useragent .= ' -- WordPress/'.$wp_version;
    3046 
    3047     // when set to true, this outputs debug messages by itself
    3048     $client->debug = false;
    3049     $home = trailingslashit( home_url() );
    3050     if ( !$client->query('weblogUpdates.extendedPing', get_option('blogname'), $home, get_bloginfo('rss2_url') ) ) // then try a normal ping
    3051         $client->query('weblogUpdates.ping', get_option('blogname'), $home);
    3052 }
    3053 
    3054 /**
    3055  * Default filter attached to pingback_ping_source_uri to validate the pingback's Source URI
    3056  *
    3057  * @since 3.5.1
    3058  * @see wp_http_validate_url()
    3059  *
    3060  * @param string $source_uri
    3061  * @return string
    3062  */
    3063 function pingback_ping_source_uri( $source_uri ) {
    3064     return (string) wp_http_validate_url( $source_uri );
    3065 }
    3066 
    3067 /**
    3068  * Default filter attached to xmlrpc_pingback_error.
    3069  *
    3070  * Returns a generic pingback error code unless the error code is 48,
    3071  * which reports that the pingback is already registered.
    3072  *
    3073  * @since 3.5.1
    3074  * @link http://www.hixie.ch/specs/pingback/pingback#TOC3
    3075  *
    3076  * @param IXR_Error $ixr_error
    3077  * @return IXR_Error
    3078  */
    3079 function xmlrpc_pingback_error( $ixr_error ) {
    3080     if ( $ixr_error->code === 48 )
    3081         return $ixr_error;
    3082     return new IXR_Error( 0, '' );
    3083 }
    3084 
    3085 //
    3086 // Cache
    3087 //
    3088 
    3089 /**
    3090  * Removes comment ID from the comment cache.
    3091  *
    3092  * @since 2.3.0
    3093  *
    3094  * @param int|array $ids Comment ID or array of comment IDs to remove from cache
    3095  */
    3096 function clean_comment_cache($ids) {
    3097     foreach ( (array) $ids as $id )
    3098         wp_cache_delete($id, 'comment');
    3099 
    3100     wp_cache_set( 'last_changed', microtime(), 'comment' );
    3101 }
    3102 
    3103 /**
    3104  * Updates the comment cache of given comments.
    3105  *
    3106  * Will add the comments in $comments to the cache. If comment ID already exists
    3107  * in the comment cache then it will not be updated. The comment is added to the
    3108  * cache using the comment group with the key using the ID of the comments.
    3109  *
    3110  * @since 2.3.0
    3111  *
    3112  * @param array $comments Array of comment row objects
    3113  */
    3114 function update_comment_cache($comments) {
    3115     foreach ( (array) $comments as $comment )
    3116         wp_cache_add($comment->comment_ID, $comment, 'comment');
    3117 }
    3118 
    3119 //
    3120 // Internal
    3121 //
    3122 
    3123 /**
    3124  * Close comments on old posts on the fly, without any extra DB queries. Hooked to the_posts.
    3125  *
    3126  * @access private
    3127  * @since 2.7.0
    3128  *
    3129  * @param WP_Post  $posts Post data object.
    3130  * @param WP_Query $query Query object.
    3131  * @return array
    3132  */
    3133 function _close_comments_for_old_posts( $posts, $query ) {
    3134     if ( empty( $posts ) || ! $query->is_singular() || ! get_option( 'close_comments_for_old_posts' ) )
    3135         return $posts;
    3136 
    3137     /**
    3138      * Filter the list of post types to automatically close comments for.
    3139      *
    3140      * @since 3.2.0
    3141      *
    3142      * @param array $post_types An array of registered post types. Default array with 'post'.
    3143      */
    3144     $post_types = apply_filters( 'close_comments_for_post_types', array( 'post' ) );
    3145     if ( ! in_array( $posts[0]->post_type, $post_types ) )
    3146         return $posts;
    3147 
    3148     $days_old = (int) get_option( 'close_comments_days_old' );
    3149     if ( ! $days_old )
    3150         return $posts;
    3151 
    3152     if ( time() - strtotime( $posts[0]->post_date_gmt ) > ( $days_old * DAY_IN_SECONDS ) ) {
    3153         $posts[0]->comment_status = 'closed';
    3154         $posts[0]->ping_status = 'closed';
    3155     }
    3156 
    3157     return $posts;
    3158 }
    3159 
    3160 /**
    3161  * Close comments on an old post. Hooked to comments_open and pings_open.
    3162  *
    3163  * @access private
    3164  * @since 2.7.0
    3165  *
    3166  * @param bool $open Comments open or closed
    3167  * @param int $post_id Post ID
    3168  * @return bool $open
    3169  */
    3170 function _close_comments_for_old_post( $open, $post_id ) {
    3171     if ( ! $open )
    3172         return $open;
    3173 
    3174     if ( !get_option('close_comments_for_old_posts') )
    3175         return $open;
    3176 
    3177     $days_old = (int) get_option('close_comments_days_old');
    3178     if ( !$days_old )
    3179         return $open;
    3180 
    3181     $post = get_post($post_id);
    3182 
    3183     /** This filter is documented in wp-includes/comment.php */
    3184     $post_types = apply_filters( 'close_comments_for_post_types', array( 'post' ) );
    3185     if ( ! in_array( $post->post_type, $post_types ) )
    3186         return $open;
    3187 
    3188     if ( time() - strtotime( $post->post_date_gmt ) > ( $days_old * DAY_IN_SECONDS ) )
    3189         return false;
    3190 
    3191     return $open;
    3192 }
  • trunk/src/wp-includes/comment-functions.php

    r33746 r33750  
    227227    $query = new WP_Comment_Query;
    228228    return $query->query( $args );
    229 }
    230 
    231 /**
    232  * WordPress Comment Query class.
    233  *
    234  * See WP_Comment_Query::__construct() for accepted arguments.
    235  *
    236  * @since 3.1.0
    237  */
    238 class WP_Comment_Query {
    239     /**
    240      * SQL for database query.
    241      *
    242      * @since 4.0.1
    243      * @access public
    244      * @var string
    245      */
    246     public $request;
    247 
    248     /**
    249      * Metadata query container
    250      *
    251      * @since 3.5.0
    252      * @access public
    253      * @var object WP_Meta_Query
    254      */
    255     public $meta_query = false;
    256 
    257     /**
    258      * Date query container
    259      *
    260      * @since 3.7.0
    261      * @access public
    262      * @var object WP_Date_Query
    263      */
    264     public $date_query = false;
    265 
    266     /**
    267      * Query vars set by the user.
    268      *
    269      * @since 3.1.0
    270      * @access public
    271      * @var array
    272      */
    273     public $query_vars;
    274 
    275     /**
    276      * Default values for query vars.
    277      *
    278      * @since 4.2.0
    279      * @access public
    280      * @var array
    281      */
    282     public $query_var_defaults;
    283 
    284     /**
    285      * List of comments located by the query.
    286      *
    287      * @since 4.0.0
    288      * @access public
    289      * @var array
    290      */
    291     public $comments;
    292 
    293     /**
    294      * Make private/protected methods readable for backwards compatibility.
    295      *
    296      * @since 4.0.0
    297      * @access public
    298      *
    299      * @param callable $name      Method to call.
    300      * @param array    $arguments Arguments to pass when calling.
    301      * @return mixed|false Return value of the callback, false otherwise.
    302      */
    303     public function __call( $name, $arguments ) {
    304         if ( 'get_search_sql' === $name ) {
    305             return call_user_func_array( array( $this, $name ), $arguments );
    306         }
    307         return false;
    308     }
    309 
    310     /**
    311      * Constructor.
    312      *
    313      * Sets up the comment query, based on the query vars passed.
    314      *
    315      * @since  4.2.0
    316      * @access public
    317      *
    318      * @param string|array $query {
    319      *     Optional. Array or query string of comment query parameters. Default empty.
    320      *
    321      *     @type string       $author_email        Comment author email address. Default empty.
    322      *     @type array        $author__in          Array of author IDs to include comments for. Default empty.
    323      *     @type array        $author__not_in      Array of author IDs to exclude comments for. Default empty.
    324      *     @type array        $comment__in         Array of comment IDs to include. Default empty.
    325      *     @type array        $comment__not_in     Array of comment IDs to exclude. Default empty.
    326      *     @type bool         $count               Whether to return a comment count (true) or array of comment
    327      *                                             objects (false). Default false.
    328      *     @type array        $date_query          Date query clauses to limit comments by. See WP_Date_Query.
    329      *                                             Default null.
    330      *     @type string       $fields              Comment fields to return. Accepts 'ids' for comment IDs only or
    331      *                                             empty for all fields. Default empty.
    332      *     @type int          $ID                  Currently unused.
    333      *     @type array        $include_unapproved  Array of IDs or email addresses of users whose unapproved comments
    334      *                                             will be returned by the query regardless of `$status`. Default empty.
    335      *     @type int          $karma               Karma score to retrieve matching comments for. Default empty.
    336      *     @type string       $meta_key            Include comments with a matching comment meta key. Default empty.
    337      *     @type string       $meta_value          Include comments with a matching comment meta value. Requires
    338      *                                             `$meta_key` to be set. Default empty.
    339      *     @type array        $meta_query          Meta query clauses to limit retrieved comments by.
    340      *                                             See WP_Meta_Query. Default empty.
    341      *     @type int          $number              Maximum number of comments to retrieve. Default null (no limit).
    342      *     @type int          $offset              Number of comments to offset the query. Used to build LIMIT clause.
    343      *                                             Default 0.
    344      *     @type string|array $orderby             Comment status or array of statuses. To use 'meta_value' or
    345      *                                             'meta_value_num', `$meta_key` must also be defined. To sort by
    346      *                                             a specific `$meta_query` clause, use that clause's array key.
    347      *                                             Accepts 'comment_agent', 'comment_approved', 'comment_author',
    348      *                                             'comment_author_email', 'comment_author_IP',
    349      *                                             'comment_author_url', 'comment_content', 'comment_date',
    350      *                                             'comment_date_gmt', 'comment_ID', 'comment_karma',
    351      *                                             'comment_parent', 'comment_post_ID', 'comment_type', 'user_id',
    352      *                                             'meta_value', 'meta_value_num', the value of $meta_key, and the
    353      *                                             array keys of `$meta_query`. Also accepts false, an empty array,
    354      *                                             or 'none' to disable `ORDER BY` clause.
    355      *                                             Default: 'comment_date_gmt'.
    356      *     @type string       $order               How to order retrieved comments. Accepts 'ASC', 'DESC'.
    357      *                                             Default: 'DESC'.
    358      *     @type int          $parent              Parent ID of comment to retrieve children of. Default empty.
    359      *     @type array        $post_author__in     Array of author IDs to retrieve comments for. Default empty.
    360      *     @type array        $post_author__not_in Array of author IDs *not* to retrieve comments for. Default empty.
    361      *     @type int          $post_ID             Currently unused.
    362      *     @type int          $post_id             Limit results to those affiliated with a given post ID. Default 0.
    363      *     @type array        $post__in            Array of post IDs to include affiliated comments for. Default empty.
    364      *     @type array        $post__not_in        Array of post IDs to exclude affiliated comments for. Default empty.
    365      *     @type int          $post_author         Comment author ID to limit results by. Default empty.
    366      *     @type string       $post_status         Post status to retrieve affiliated comments for. Default empty.
    367      *     @type string       $post_type           Post type to retrieve affiliated comments for. Default empty.
    368      *     @type string       $post_name           Post name to retrieve affiliated comments for. Default empty.
    369      *     @type int          $post_parent         Post parent ID to retrieve affiliated comments for. Default empty.
    370      *     @type string       $search              Search term(s) to retrieve matching comments for. Default empty.
    371      *     @type string       $status              Comment status to limit results by. Accepts 'hold'
    372      *                                             (`comment_status=0`), 'approve' (`comment_status=1`), 'all', or a
    373      *                                             custom comment status. Default 'all'.
    374      *     @type string|array $type                Include comments of a given type, or array of types. Accepts
    375      *                                             'comment', 'pings' (includes 'pingback' and 'trackback'), or any
    376      *                                             custom type string. Default empty.
    377      *     @type array        $type__in            Include comments from a given array of comment types. Default empty.
    378      *     @type array        $type__not_in        Exclude comments from a given array of comment types. Default empty.
    379      *     @type int          $user_id             Include comments for a specific user ID. Default empty.
    380      * }
    381      */
    382     public function __construct( $query = '' ) {
    383         $this->query_var_defaults = array(
    384             'author_email' => '',
    385             'author__in' => '',
    386             'author__not_in' => '',
    387             'include_unapproved' => '',
    388             'fields' => '',
    389             'ID' => '',
    390             'comment__in' => '',
    391             'comment__not_in' => '',
    392             'karma' => '',
    393             'number' => '',
    394             'offset' => '',
    395             'orderby' => '',
    396             'order' => 'DESC',
    397             'parent' => '',
    398             'post_author__in' => '',
    399             'post_author__not_in' => '',
    400             'post_ID' => '',
    401             'post_id' => 0,
    402             'post__in' => '',
    403             'post__not_in' => '',
    404             'post_author' => '',
    405             'post_name' => '',
    406             'post_parent' => '',
    407             'post_status' => '',
    408             'post_type' => '',
    409             'status' => 'all',
    410             'type' => '',
    411             'type__in' => '',
    412             'type__not_in' => '',
    413             'user_id' => '',
    414             'search' => '',
    415             'count' => false,
    416             'meta_key' => '',
    417             'meta_value' => '',
    418             'meta_query' => '',
    419             'date_query' => null, // See WP_Date_Query
    420         );
    421 
    422         if ( ! empty( $query ) ) {
    423             $this->query( $query );
    424         }
    425     }
    426 
    427     /**
    428      * Parse arguments passed to the comment query with default query parameters.
    429      *
    430      * @since  4.2.0 Extracted from WP_Comment_Query::query().
    431      *
    432      * @access public
    433      *
    434      * @param string|array $query WP_Comment_Query arguments. See WP_Comment_Query::__construct()
    435      */
    436     public function parse_query( $query = '' ) {
    437         if ( empty( $query ) ) {
    438             $query = $this->query_vars;
    439         }
    440 
    441         $this->query_vars = wp_parse_args( $query, $this->query_var_defaults );
    442         do_action_ref_array( 'parse_comment_query', array( &$this ) );
    443     }
    444 
    445     /**
    446      * Sets up the WordPress query for retrieving comments.
    447      *
    448      * @since 3.1.0
    449      * @since 4.1.0 Introduced 'comment__in', 'comment__not_in', 'post_author__in',
    450      *              'post_author__not_in', 'author__in', 'author__not_in', 'post__in',
    451      *              'post__not_in', 'include_unapproved', 'type__in', and 'type__not_in'
    452      *              arguments to $query_vars.
    453      * @since 4.2.0 Moved parsing to WP_Comment_Query::parse_query().
    454      * @access public
    455      *
    456      * @param string|array $query Array or URL query string of parameters.
    457      * @return array|int List of comments, or number of comments when 'count' is passed as a query var.
    458      */
    459     public function query( $query ) {
    460         $this->query_vars = wp_parse_args( $query );
    461         return $this->get_comments();
    462     }
    463 
    464     /**
    465      * Get a list of comments matching the query vars.
    466      *
    467      * @since 4.2.0
    468      * @access public
    469      *
    470      * @global wpdb $wpdb WordPress database abstraction object.
    471      *
    472      * @return int|array The list of comments.
    473      */
    474     public function get_comments() {
    475         global $wpdb;
    476 
    477         $groupby = '';
    478 
    479         $this->parse_query();
    480 
    481         // Parse meta query
    482         $this->meta_query = new WP_Meta_Query();
    483         $this->meta_query->parse_query_vars( $this->query_vars );
    484 
    485         /**
    486          * Fires before comments are retrieved.
    487          *
    488          * @since 3.1.0
    489          *
    490          * @param WP_Comment_Query &$this Current instance of WP_Comment_Query, passed by reference.
    491          */
    492         do_action_ref_array( 'pre_get_comments', array( &$this ) );
    493 
    494         // Reparse query vars, in case they were modified in a 'pre_get_comments' callback.
    495         $this->meta_query->parse_query_vars( $this->query_vars );
    496         if ( ! empty( $this->meta_query->queries ) ) {
    497             $meta_query_clauses = $this->meta_query->get_sql( 'comment', $wpdb->comments, 'comment_ID', $this );
    498         }
    499 
    500         // $args can include anything. Only use the args defined in the query_var_defaults to compute the key.
    501         $key = md5( serialize( wp_array_slice_assoc( $this->query_vars, array_keys( $this->query_var_defaults ) ) ) );
    502         $last_changed = wp_cache_get( 'last_changed', 'comment' );
    503         if ( ! $last_changed ) {
    504             $last_changed = microtime();
    505             wp_cache_set( 'last_changed', $last_changed, 'comment' );
    506         }
    507         $cache_key = "get_comments:$key:$last_changed";
    508 
    509         if ( $cache = wp_cache_get( $cache_key, 'comment' ) ) {
    510             $this->comments = $cache;
    511             return $this->comments;
    512         }
    513 
    514         $where = array();
    515 
    516         // Assemble clauses related to 'comment_approved'.
    517         $approved_clauses = array();
    518 
    519         // 'status' accepts an array or a comma-separated string.
    520         $status_clauses = array();
    521         $statuses = $this->query_vars['status'];
    522         if ( ! is_array( $statuses ) ) {
    523             $statuses = preg_split( '/[\s,]+/', $statuses );
    524         }
    525 
    526         // 'any' overrides other statuses.
    527         if ( ! in_array( 'any', $statuses ) ) {
    528             foreach ( $statuses as $status ) {
    529                 switch ( $status ) {
    530                     case 'hold' :
    531                         $status_clauses[] = "comment_approved = '0'";
    532                         break;
    533 
    534                     case 'approve' :
    535                         $status_clauses[] = "comment_approved = '1'";
    536                         break;
    537 
    538                     case 'all' :
    539                     case '' :
    540                         $status_clauses[] = "( comment_approved = '0' OR comment_approved = '1' )";
    541                         break;
    542 
    543                     default :
    544                         $status_clauses[] = $wpdb->prepare( "comment_approved = %s", $status );
    545                         break;
    546                 }
    547             }
    548 
    549             if ( ! empty( $status_clauses ) ) {
    550                 $approved_clauses[] = '( ' . implode( ' OR ', $status_clauses ) . ' )';
    551             }
    552         }
    553 
    554         // User IDs or emails whose unapproved comments are included, regardless of $status.
    555         if ( ! empty( $this->query_vars['include_unapproved'] ) ) {
    556             $include_unapproved = $this->query_vars['include_unapproved'];
    557 
    558             // Accepts arrays or comma-separated strings.
    559             if ( ! is_array( $include_unapproved ) ) {
    560                 $include_unapproved = preg_split( '/[\s,]+/', $include_unapproved );
    561             }
    562 
    563             $unapproved_ids = $unapproved_emails = array();
    564             foreach ( $include_unapproved as $unapproved_identifier ) {
    565                 // Numeric values are assumed to be user ids.
    566                 if ( is_numeric( $unapproved_identifier ) ) {
    567                     $approved_clauses[] = $wpdb->prepare( "( user_id = %d AND comment_approved = '0' )", $unapproved_identifier );
    568 
    569                 // Otherwise we match against email addresses.
    570                 } else {
    571                     $approved_clauses[] = $wpdb->prepare( "( comment_author_email = %s AND comment_approved = '0' )", $unapproved_identifier );
    572                 }
    573             }
    574         }
    575 
    576         // Collapse comment_approved clauses into a single OR-separated clause.
    577         if ( ! empty( $approved_clauses ) ) {
    578             if ( 1 === count( $approved_clauses ) ) {
    579                 $where[] = $approved_clauses[0];
    580             } else {
    581                 $where[] = '( ' . implode( ' OR ', $approved_clauses ) . ' )';
    582             }
    583         }
    584 
    585         $order = ( 'ASC' == strtoupper( $this->query_vars['order'] ) ) ? 'ASC' : 'DESC';
    586 
    587         // Disable ORDER BY with 'none', an empty array, or boolean false.
    588         if ( in_array( $this->query_vars['orderby'], array( 'none', array(), false ), true ) ) {
    589             $orderby = '';
    590         } elseif ( ! empty( $this->query_vars['orderby'] ) ) {
    591             $ordersby = is_array( $this->query_vars['orderby'] ) ?
    592                 $this->query_vars['orderby'] :
    593                 preg_split( '/[,\s]/', $this->query_vars['orderby'] );
    594 
    595             $orderby_array = array();
    596             $found_orderby_comment_ID = false;
    597             foreach ( $ordersby as $_key => $_value ) {
    598                 if ( ! $_value ) {
    599                     continue;
    600                 }
    601 
    602                 if ( is_int( $_key ) ) {
    603                     $_orderby = $_value;
    604                     $_order = $order;
    605                 } else {
    606                     $_orderby = $_key;
    607                     $_order = $_value;
    608                 }
    609 
    610                 if ( ! $found_orderby_comment_ID && 'comment_ID' === $_orderby ) {
    611                     $found_orderby_comment_ID = true;
    612                 }
    613 
    614                 $parsed = $this->parse_orderby( $_orderby );
    615 
    616                 if ( ! $parsed ) {
    617                     continue;
    618                 }
    619 
    620                 $orderby_array[] = $parsed . ' ' . $this->parse_order( $_order );
    621             }
    622 
    623             // If no valid clauses were found, order by comment_date_gmt.
    624             if ( empty( $orderby_array ) ) {
    625                 $orderby_array[] = "$wpdb->comments.comment_date_gmt $order";
    626             }
    627 
    628             // To ensure determinate sorting, always include a comment_ID clause.
    629             if ( ! $found_orderby_comment_ID ) {
    630                 $comment_ID_order = '';
    631 
    632                 // Inherit order from comment_date or comment_date_gmt, if available.
    633                 foreach ( $orderby_array as $orderby_clause ) {
    634                     if ( preg_match( '/comment_date(?:_gmt)*\ (ASC|DESC)/', $orderby_clause, $match ) ) {
    635                         $comment_ID_order = $match[1];
    636                         break;
    637                     }
    638                 }
    639 
    640                 // If no date-related order is available, use the date from the first available clause.
    641                 if ( ! $comment_ID_order ) {
    642                     foreach ( $orderby_array as $orderby_clause ) {
    643                         if ( false !== strpos( 'ASC', $orderby_clause ) ) {
    644                             $comment_ID_order = 'ASC';
    645                         } else {
    646                             $comment_ID_order = 'DESC';
    647                         }
    648 
    649                         break;
    650                     }
    651                 }
    652 
    653                 // Default to DESC.
    654                 if ( ! $comment_ID_order ) {
    655                     $comment_ID_order = 'DESC';
    656                 }
    657 
    658                 $orderby_array[] = "$wpdb->comments.comment_ID $comment_ID_order";
    659             }
    660 
    661             $orderby = implode( ', ', $orderby_array );
    662         } else {
    663             $orderby = "$wpdb->comments.comment_date_gmt $order";
    664         }
    665 
    666         $number = absint( $this->query_vars['number'] );
    667         $offset = absint( $this->query_vars['offset'] );
    668 
    669         if ( ! empty( $number ) ) {
    670             if ( $offset ) {
    671                 $limits = 'LIMIT ' . $offset . ',' . $number;
    672             } else {
    673                 $limits = 'LIMIT ' . $number;
    674             }
    675         } else {
    676             $limits = '';
    677         }
    678 
    679         if ( $this->query_vars['count'] ) {
    680             $fields = 'COUNT(*)';
    681         } else {
    682             switch ( strtolower( $this->query_vars['fields'] ) ) {
    683                 case 'ids':
    684                     $fields = "$wpdb->comments.comment_ID";
    685                     break;
    686                 default:
    687                     $fields = "*";
    688                     break;
    689             }
    690         }
    691 
    692         $join = '';
    693 
    694         $post_id = absint( $this->query_vars['post_id'] );
    695         if ( ! empty( $post_id ) ) {
    696             $where[] = $wpdb->prepare( 'comment_post_ID = %d', $post_id );
    697         }
    698 
    699         // Parse comment IDs for an IN clause.
    700         if ( ! empty( $this->query_vars['comment__in'] ) ) {
    701             $where[] = "$wpdb->comments.comment_ID IN ( " . implode( ',', wp_parse_id_list( $this->query_vars['comment__in'] ) ) . ' )';
    702         }
    703 
    704         // Parse comment IDs for a NOT IN clause.
    705         if ( ! empty( $this->query_vars['comment__not_in'] ) ) {
    706             $where[] = "$wpdb->comments.comment_ID NOT IN ( " . implode( ',', wp_parse_id_list( $this->query_vars['comment__not_in'] ) ) . ' )';
    707         }
    708 
    709         // Parse comment post IDs for an IN clause.
    710         if ( ! empty( $this->query_vars['post__in'] ) ) {
    711             $where[] = 'comment_post_ID IN ( ' . implode( ',', wp_parse_id_list( $this->query_vars['post__in'] ) ) . ' )';
    712         }
    713 
    714         // Parse comment post IDs for a NOT IN clause.
    715         if ( ! empty( $this->query_vars['post__not_in'] ) ) {
    716             $where[] = 'comment_post_ID NOT IN ( ' . implode( ',', wp_parse_id_list( $this->query_vars['post__not_in'] ) ) . ' )';
    717         }
    718 
    719         if ( '' !== $this->query_vars['author_email'] ) {
    720             $where[] = $wpdb->prepare( 'comment_author_email = %s', $this->query_vars['author_email'] );
    721         }
    722 
    723         if ( '' !== $this->query_vars['karma'] ) {
    724             $where[] = $wpdb->prepare( 'comment_karma = %d', $this->query_vars['karma'] );
    725         }
    726 
    727         // Filtering by comment_type: 'type', 'type__in', 'type__not_in'.
    728         $raw_types = array(
    729             'IN' => array_merge( (array) $this->query_vars['type'], (array) $this->query_vars['type__in'] ),
    730             'NOT IN' => (array) $this->query_vars['type__not_in'],
    731         );
    732 
    733         $comment_types = array();
    734         foreach ( $raw_types as $operator => $_raw_types ) {
    735             $_raw_types = array_unique( $_raw_types );
    736 
    737             foreach ( $_raw_types as $type ) {
    738                 switch ( $type ) {
    739                     // An empty translates to 'all', for backward compatibility
    740                     case '':
    741                     case 'all' :
    742                         break;
    743 
    744                     case 'comment':
    745                     case 'comments':
    746                         $comment_types[ $operator ][] = "''";
    747                         break;
    748 
    749                     case 'pings':
    750                         $comment_types[ $operator ][] = "'pingback'";
    751                         $comment_types[ $operator ][] = "'trackback'";
    752                         break;
    753 
    754                     default:
    755                         $comment_types[ $operator ][] = $wpdb->prepare( '%s', $type );
    756                         break;
    757                 }
    758             }
    759 
    760             if ( ! empty( $comment_types[ $operator ] ) ) {
    761                 $types_sql = implode( ', ', $comment_types[ $operator ] );
    762                 $where[] = "comment_type $operator ($types_sql)";
    763             }
    764         }
    765 
    766         if ( '' !== $this->query_vars['parent'] ) {
    767             $where[] = $wpdb->prepare( 'comment_parent = %d', $this->query_vars['parent'] );
    768         }
    769 
    770         if ( is_array( $this->query_vars['user_id'] ) ) {
    771             $where[] = 'user_id IN (' . implode( ',', array_map( 'absint', $this->query_vars['user_id'] ) ) . ')';
    772         } elseif ( '' !== $this->query_vars['user_id'] ) {
    773             $where[] = $wpdb->prepare( 'user_id = %d', $this->query_vars['user_id'] );
    774         }
    775 
    776         if ( '' !== $this->query_vars['search'] ) {
    777             $search_sql = $this->get_search_sql(
    778                 $this->query_vars['search'],
    779                 array( 'comment_author', 'comment_author_email', 'comment_author_url', 'comment_author_IP', 'comment_content' )
    780             );
    781 
    782             // Strip leading 'AND'.
    783             $where[] = preg_replace( '/^\s*AND\s*/', '', $search_sql );
    784         }
    785 
    786         // If any post-related query vars are passed, join the posts table.
    787         $join_posts_table = false;
    788         $plucked = wp_array_slice_assoc( $this->query_vars, array( 'post_author', 'post_name', 'post_parent', 'post_status', 'post_type' ) );
    789         $post_fields = array_filter( $plucked );
    790 
    791         if ( ! empty( $post_fields ) ) {
    792             $join_posts_table = true;
    793             foreach ( $post_fields as $field_name => $field_value ) {
    794                 // $field_value may be an array.
    795                 $esses = array_fill( 0, count( (array) $field_value ), '%s' );
    796                 $where[] = $wpdb->prepare( " {$wpdb->posts}.{$field_name} IN (" . implode( ',', $esses ) . ')', $field_value );
    797             }
    798         }
    799 
    800         // Comment author IDs for an IN clause.
    801         if ( ! empty( $this->query_vars['author__in'] ) ) {
    802             $where[] = 'user_id IN ( ' . implode( ',', wp_parse_id_list( $this->query_vars['author__in'] ) ) . ' )';
    803         }
    804 
    805         // Comment author IDs for a NOT IN clause.
    806         if ( ! empty( $this->query_vars['author__not_in'] ) ) {
    807             $where[] = 'user_id NOT IN ( ' . implode( ',', wp_parse_id_list( $this->query_vars['author__not_in'] ) ) . ' )';
    808         }
    809 
    810         // Post author IDs for an IN clause.
    811         if ( ! empty( $this->query_vars['post_author__in'] ) ) {
    812             $join_posts_table = true;
    813             $where[] = 'post_author IN ( ' . implode( ',', wp_parse_id_list( $this->query_vars['post_author__in'] ) ) . ' )';
    814         }
    815 
    816         // Post author IDs for a NOT IN clause.
    817         if ( ! empty( $this->query_vars['post_author__not_in'] ) ) {
    818             $join_posts_table = true;
    819             $where[] = 'post_author NOT IN ( ' . implode( ',', wp_parse_id_list( $this->query_vars['post_author__not_in'] ) ) . ' )';
    820         }
    821 
    822         if ( $join_posts_table ) {
    823             $join = "JOIN $wpdb->posts ON $wpdb->posts.ID = $wpdb->comments.comment_post_ID";
    824         }
    825 
    826         if ( ! empty( $meta_query_clauses ) ) {
    827             $join .= $meta_query_clauses['join'];
    828 
    829             // Strip leading 'AND'.
    830             $where[] = preg_replace( '/^\s*AND\s*/', '', $meta_query_clauses['where'] );
    831 
    832             if ( ! $this->query_vars['count'] ) {
    833                 $groupby = "{$wpdb->comments}.comment_ID";
    834             }
    835         }
    836 
    837         $date_query = $this->query_vars['date_query'];
    838         if ( ! empty( $date_query ) && is_array( $date_query ) ) {
    839             $date_query_object = new WP_Date_Query( $date_query, 'comment_date' );
    840             $where[] = preg_replace( '/^\s*AND\s*/', '', $date_query_object->get_sql() );
    841         }
    842 
    843         $where = implode( ' AND ', $where );
    844 
    845         $pieces = array( 'fields', 'join', 'where', 'orderby', 'limits', 'groupby' );
    846         /**
    847          * Filter the comment query clauses.
    848          *
    849          * @since 3.1.0
    850          *
    851          * @param array            $pieces A compacted array of comment query clauses.
    852          * @param WP_Comment_Query &$this  Current instance of WP_Comment_Query, passed by reference.
    853          */
    854         $clauses = apply_filters_ref_array( 'comments_clauses', array( compact( $pieces ), &$this ) );
    855 
    856         $fields = isset( $clauses[ 'fields' ] ) ? $clauses[ 'fields' ] : '';
    857         $join = isset( $clauses[ 'join' ] ) ? $clauses[ 'join' ] : '';
    858         $where = isset( $clauses[ 'where' ] ) ? $clauses[ 'where' ] : '';
    859         $orderby = isset( $clauses[ 'orderby' ] ) ? $clauses[ 'orderby' ] : '';
    860         $limits = isset( $clauses[ 'limits' ] ) ? $clauses[ 'limits' ] : '';
    861         $groupby = isset( $clauses[ 'groupby' ] ) ? $clauses[ 'groupby' ] : '';
    862 
    863         if ( $where ) {
    864             $where = 'WHERE ' . $where;
    865         }
    866 
    867         if ( $groupby ) {
    868             $groupby = 'GROUP BY ' . $groupby;
    869         }
    870 
    871         if ( $orderby ) {
    872             $orderby = "ORDER BY $orderby";
    873         }
    874 
    875         $this->request = "SELECT $fields FROM $wpdb->comments $join $where $groupby $orderby $limits";
    876 
    877         if ( $this->query_vars['count'] ) {
    878             return $wpdb->get_var( $this->request );
    879         }
    880 
    881         if ( 'ids' == $this->query_vars['fields'] ) {
    882             $this->comments = $wpdb->get_col( $this->request );
    883             return array_map( 'intval', $this->comments );
    884         }
    885 
    886         $results = $wpdb->get_results( $this->request );
    887         /**
    888          * Filter the comment query results.
    889          *
    890          * @since 3.1.0
    891          *
    892          * @param array            $results  An array of comments.
    893          * @param WP_Comment_Query &$this    Current instance of WP_Comment_Query, passed by reference.
    894          */
    895         $comments = apply_filters_ref_array( 'the_comments', array( $results, &$this ) );
    896 
    897         wp_cache_add( $cache_key, $comments, 'comment' );
    898         if ( '*' === $fields ) {
    899             update_comment_cache( $comments );
    900         }
    901 
    902         $this->comments = $comments;
    903         return $this->comments;
    904     }
    905 
    906     /**
    907      * Used internally to generate an SQL string for searching across multiple columns
    908      *
    909      * @since 3.1.0
    910      * @access protected
    911      *
    912      * @global wpdb $wpdb
    913      *
    914      * @param string $string
    915      * @param array $cols
    916      * @return string
    917      */
    918     protected function get_search_sql( $string, $cols ) {
    919         global $wpdb;
    920 
    921         $like = '%' . $wpdb->esc_like( $string ) . '%';
    922 
    923         $searches = array();
    924         foreach ( $cols as $col ) {
    925             $searches[] = $wpdb->prepare( "$col LIKE %s", $like );
    926         }
    927 
    928         return ' AND (' . implode(' OR ', $searches) . ')';
    929     }
    930 
    931     /**
    932      * Parse and sanitize 'orderby' keys passed to the comment query.
    933      *
    934      * @since 4.2.0
    935      * @access protected
    936      *
    937      * @global wpdb $wpdb WordPress database abstraction object.
    938      *
    939      * @param string $orderby Alias for the field to order by.
    940      * @return string|false Value to used in the ORDER clause. False otherwise.
    941      */
    942     protected function parse_orderby( $orderby ) {
    943         global $wpdb;
    944 
    945         $allowed_keys = array(
    946             'comment_agent',
    947             'comment_approved',
    948             'comment_author',
    949             'comment_author_email',
    950             'comment_author_IP',
    951             'comment_author_url',
    952             'comment_content',
    953             'comment_date',
    954             'comment_date_gmt',
    955             'comment_ID',
    956             'comment_karma',
    957             'comment_parent',
    958             'comment_post_ID',
    959             'comment_type',
    960             'user_id',
    961         );
    962 
    963         if ( ! empty( $this->query_vars['meta_key'] ) ) {
    964             $allowed_keys[] = $this->query_vars['meta_key'];
    965             $allowed_keys[] = 'meta_value';
    966             $allowed_keys[] = 'meta_value_num';
    967         }
    968 
    969         $meta_query_clauses = $this->meta_query->get_clauses();
    970         if ( $meta_query_clauses ) {
    971             $allowed_keys = array_merge( $allowed_keys, array_keys( $meta_query_clauses ) );
    972         }
    973 
    974         $parsed = false;
    975         if ( $orderby == $this->query_vars['meta_key'] || $orderby == 'meta_value' ) {
    976             $parsed = "$wpdb->commentmeta.meta_value";
    977         } elseif ( $orderby == 'meta_value_num' ) {
    978             $parsed = "$wpdb->commentmeta.meta_value+0";
    979         } elseif ( in_array( $orderby, $allowed_keys ) ) {
    980 
    981             if ( isset( $meta_query_clauses[ $orderby ] ) ) {
    982                 $meta_clause = $meta_query_clauses[ $orderby ];
    983                 $parsed = sprintf( "CAST(%s.meta_value AS %s)", esc_sql( $meta_clause['alias'] ), esc_sql( $meta_clause['cast'] ) );
    984             } else {
    985                 $parsed = "$wpdb->comments.$orderby";
    986             }
    987         }
    988 
    989         return $parsed;
    990     }
    991 
    992     /**
    993      * Parse an 'order' query variable and cast it to ASC or DESC as necessary.
    994      *
    995      * @since 4.2.0
    996      * @access protected
    997      *
    998      * @param string $order The 'order' query variable.
    999      * @return string The sanitized 'order' query variable.
    1000      */
    1001     protected function parse_order( $order ) {
    1002         if ( ! is_string( $order ) || empty( $order ) ) {
    1003             return 'DESC';
    1004         }
    1005 
    1006         if ( 'ASC' === strtoupper( $order ) ) {
    1007             return 'ASC';
    1008         } else {
    1009             return 'DESC';
    1010         }
    1011     }
    1012229}
    1013230
  • trunk/src/wp-includes/comment.php

    r33734 r33750  
    77 */
    88
    9 /**
    10  * Check whether a comment passes internal checks to be allowed to add.
    11  *
    12  * If manual comment moderation is set in the administration, then all checks,
    13  * regardless of their type and whitelist, will fail and the function will
    14  * return false.
    15  *
    16  * If the number of links exceeds the amount in the administration, then the
    17  * check fails. If any of the parameter contents match the blacklist of words,
    18  * then the check fails.
    19  *
    20  * If the comment author was approved before, then the comment is automatically
    21  * whitelisted.
    22  *
    23  * If all checks pass, the function will return true.
    24  *
    25  * @since 1.2.0
    26  *
    27  * @global wpdb $wpdb WordPress database abstraction object.
    28  *
    29  * @param string $author       Comment author name.
    30  * @param string $email        Comment author email.
    31  * @param string $url          Comment author URL.
    32  * @param string $comment      Content of the comment.
    33  * @param string $user_ip      Comment author IP address.
    34  * @param string $user_agent   Comment author User-Agent.
    35  * @param string $comment_type Comment type, either user-submitted comment,
    36  *                             trackback, or pingback.
    37  * @return bool If all checks pass, true, otherwise false.
    38  */
    39 function check_comment($author, $email, $url, $comment, $user_ip, $user_agent, $comment_type) {
    40     global $wpdb;
    41 
    42     // If manual moderation is enabled, skip all checks and return false.
    43     if ( 1 == get_option('comment_moderation') )
    44         return false;
    45 
    46     /** This filter is documented in wp-includes/comment-template.php */
    47     $comment = apply_filters( 'comment_text', $comment );
    48 
    49     // Check for the number of external links if a max allowed number is set.
    50     if ( $max_links = get_option( 'comment_max_links' ) ) {
    51         $num_links = preg_match_all( '/<a [^>]*href/i', $comment, $out );
    52 
    53         /**
    54          * Filter the maximum number of links allowed in a comment.
    55          *
    56          * @since 3.0.0
    57          *
    58          * @param int    $num_links The number of links allowed.
    59          * @param string $url       Comment author's URL. Included in allowed links total.
    60          */
    61         $num_links = apply_filters( 'comment_max_links_url', $num_links, $url );
    62 
    63         /*
    64          * If the number of links in the comment exceeds the allowed amount,
    65          * fail the check by returning false.
    66          */
    67         if ( $num_links >= $max_links )
    68             return false;
    69     }
    70 
    71     $mod_keys = trim(get_option('moderation_keys'));
    72 
    73     // If moderation 'keys' (keywords) are set, process them.
    74     if ( !empty($mod_keys) ) {
    75         $words = explode("\n", $mod_keys );
    76 
    77         foreach ( (array) $words as $word) {
    78             $word = trim($word);
    79 
    80             // Skip empty lines.
    81             if ( empty($word) )
    82                 continue;
    83 
    84             /*
    85              * Do some escaping magic so that '#' (number of) characters in the spam
    86              * words don't break things:
    87              */
    88             $word = preg_quote($word, '#');
    89 
    90             /*
    91              * Check the comment fields for moderation keywords. If any are found,
    92              * fail the check for the given field by returning false.
    93              */
    94             $pattern = "#$word#i";
    95             if ( preg_match($pattern, $author) ) return false;
    96             if ( preg_match($pattern, $email) ) return false;
    97             if ( preg_match($pattern, $url) ) return false;
    98             if ( preg_match($pattern, $comment) ) return false;
    99             if ( preg_match($pattern, $user_ip) ) return false;
    100             if ( preg_match($pattern, $user_agent) ) return false;
    101         }
    102     }
    103 
    104     /*
    105      * Check if the option to approve comments by previously-approved authors is enabled.
    106      *
    107      * If it is enabled, check whether the comment author has a previously-approved comment,
    108      * as well as whether there are any moderation keywords (if set) present in the author
    109      * email address. If both checks pass, return true. Otherwise, return false.
    110      */
    111     if ( 1 == get_option('comment_whitelist')) {
    112         if ( 'trackback' != $comment_type && 'pingback' != $comment_type && $author != '' && $email != '' ) {
    113             // expected_slashed ($author, $email)
    114             $ok_to_comment = $wpdb->get_var("SELECT comment_approved FROM $wpdb->comments WHERE comment_author = '$author' AND comment_author_email = '$email' and comment_approved = '1' LIMIT 1");
    115             if ( ( 1 == $ok_to_comment ) &&
    116                 ( empty($mod_keys) || false === strpos( $email, $mod_keys) ) )
    117                     return true;
    118             else
    119                 return false;
    120         } else {
    121             return false;
    122         }
    123     }
    124     return true;
    125 }
    126 
    127 /**
    128  * Retrieve the approved comments for post $post_id.
    129  *
    130  * @since 2.0.0
    131  * @since 4.1.0 Refactored to leverage {@see WP_Comment_Query} over a direct query.
    132  *
    133  * @param  int   $post_id The ID of the post.
    134  * @param  array $args    Optional. See {@see WP_Comment_Query::query()} for information
    135  *                        on accepted arguments.
    136  * @return int|array $comments The approved comments, or number of comments if `$count`
    137  *                             argument is true.
    138  */
    139 function get_approved_comments( $post_id, $args = array() ) {
    140     if ( ! $post_id ) {
    141         return array();
    142     }
    143 
    144     $defaults = array(
    145         'status'  => 1,
    146         'post_id' => $post_id,
    147         'order'   => 'ASC',
    148     );
    149     $r = wp_parse_args( $args, $defaults );
    150 
    151     $query = new WP_Comment_Query;
    152     return $query->query( $r );
    153 }
    154 
    155 /**
    156  * Retrieves comment data given a comment ID or comment object.
    157  *
    158  * If an object is passed then the comment data will be cached and then returned
    159  * after being passed through a filter. If the comment is empty, then the global
    160  * comment variable will be used, if it is set.
    161  *
    162  * @since 2.0.0
    163  *
    164  * @global wpdb   $wpdb WordPress database abstraction object.
    165  * @global object $comment
    166  *
    167  * @param object|string|int $comment Comment to retrieve.
    168  * @param string $output Optional. OBJECT or ARRAY_A or ARRAY_N constants.
    169  * @return object|array|null Depends on $output value.
    170  */
    171 function get_comment(&$comment, $output = OBJECT) {
    172     global $wpdb;
    173 
    174     if ( empty($comment) ) {
    175         if ( isset($GLOBALS['comment']) )
    176             $_comment = & $GLOBALS['comment'];
    177         else
    178             $_comment = null;
    179     } elseif ( is_object($comment) ) {
    180         wp_cache_add($comment->comment_ID, $comment, 'comment');
    181         $_comment = $comment;
    182     } else {
    183         if ( isset($GLOBALS['comment']) && ($GLOBALS['comment']->comment_ID == $comment) ) {
    184             $_comment = & $GLOBALS['comment'];
    185         } elseif ( ! $_comment = wp_cache_get($comment, 'comment') ) {
    186             $_comment = $wpdb->get_row($wpdb->prepare("SELECT * FROM $wpdb->comments WHERE comment_ID = %d LIMIT 1", $comment));
    187             if ( ! $_comment )
    188                 return null;
    189             wp_cache_add($_comment->comment_ID, $_comment, 'comment');
    190         }
    191     }
    192 
    193     /**
    194      * Fires after a comment is retrieved.
    195      *
    196      * @since 2.3.0
    197      *
    198      * @param mixed $_comment Comment data.
    199      */
    200     $_comment = apply_filters( 'get_comment', $_comment );
    201 
    202     if ( $output == OBJECT ) {
    203         return $_comment;
    204     } elseif ( $output == ARRAY_A ) {
    205         $__comment = get_object_vars($_comment);
    206         return $__comment;
    207     } elseif ( $output == ARRAY_N ) {
    208         $__comment = array_values(get_object_vars($_comment));
    209         return $__comment;
    210     } else {
    211         return $_comment;
    212     }
    213 }
    214 
    215 /**
    216  * Retrieve a list of comments.
    217  *
    218  * The comment list can be for the blog as a whole or for an individual post.
    219  *
    220  * @since 2.7.0
    221  *
    222  * @param string|array $args Optional. Array or string of arguments. See {@see WP_Comment_Query::parse_query()}
    223  *                           for information on accepted arguments. Default empty.
    224  * @return int|array List of comments or number of found comments if `$count` argument is true.
    225  */
    226 function get_comments( $args = '' ) {
    227     $query = new WP_Comment_Query;
    228     return $query->query( $args );
    229 }
    230 
    231 /**
    232  * WordPress Comment Query class.
    233  *
    234  * See WP_Comment_Query::__construct() for accepted arguments.
    235  *
    236  * @since 3.1.0
    237  */
    238 class WP_Comment_Query {
    239     /**
    240      * SQL for database query.
    241      *
    242      * @since 4.0.1
    243      * @access public
    244      * @var string
    245      */
    246     public $request;
    247 
    248     /**
    249      * Metadata query container
    250      *
    251      * @since 3.5.0
    252      * @access public
    253      * @var object WP_Meta_Query
    254      */
    255     public $meta_query = false;
    256 
    257     /**
    258      * Date query container
    259      *
    260      * @since 3.7.0
    261      * @access public
    262      * @var object WP_Date_Query
    263      */
    264     public $date_query = false;
    265 
    266     /**
    267      * Query vars set by the user.
    268      *
    269      * @since 3.1.0
    270      * @access public
    271      * @var array
    272      */
    273     public $query_vars;
    274 
    275     /**
    276      * Default values for query vars.
    277      *
    278      * @since 4.2.0
    279      * @access public
    280      * @var array
    281      */
    282     public $query_var_defaults;
    283 
    284     /**
    285      * List of comments located by the query.
    286      *
    287      * @since 4.0.0
    288      * @access public
    289      * @var array
    290      */
    291     public $comments;
    292 
    293     /**
    294      * Make private/protected methods readable for backwards compatibility.
    295      *
    296      * @since 4.0.0
    297      * @access public
    298      *
    299      * @param callable $name      Method to call.
    300      * @param array    $arguments Arguments to pass when calling.
    301      * @return mixed|false Return value of the callback, false otherwise.
    302      */
    303     public function __call( $name, $arguments ) {
    304         if ( 'get_search_sql' === $name ) {
    305             return call_user_func_array( array( $this, $name ), $arguments );
    306         }
    307         return false;
    308     }
    309 
    310     /**
    311      * Constructor.
    312      *
    313      * Sets up the comment query, based on the query vars passed.
    314      *
    315      * @since  4.2.0
    316      * @access public
    317      *
    318      * @param string|array $query {
    319      *     Optional. Array or query string of comment query parameters. Default empty.
    320      *
    321      *     @type string       $author_email        Comment author email address. Default empty.
    322      *     @type array        $author__in          Array of author IDs to include comments for. Default empty.
    323      *     @type array        $author__not_in      Array of author IDs to exclude comments for. Default empty.
    324      *     @type array        $comment__in         Array of comment IDs to include. Default empty.
    325      *     @type array        $comment__not_in     Array of comment IDs to exclude. Default empty.
    326      *     @type bool         $count               Whether to return a comment count (true) or array of comment
    327      *                                             objects (false). Default false.
    328      *     @type array        $date_query          Date query clauses to limit comments by. See WP_Date_Query.
    329      *                                             Default null.
    330      *     @type string       $fields              Comment fields to return. Accepts 'ids' for comment IDs only or
    331      *                                             empty for all fields. Default empty.
    332      *     @type int          $ID                  Currently unused.
    333      *     @type array        $include_unapproved  Array of IDs or email addresses of users whose unapproved comments
    334      *                                             will be returned by the query regardless of `$status`. Default empty.
    335      *     @type int          $karma               Karma score to retrieve matching comments for. Default empty.
    336      *     @type string       $meta_key            Include comments with a matching comment meta key. Default empty.
    337      *     @type string       $meta_value          Include comments with a matching comment meta value. Requires
    338      *                                             `$meta_key` to be set. Default empty.
    339      *     @type array        $meta_query          Meta query clauses to limit retrieved comments by.
    340      *                                             See WP_Meta_Query. Default empty.
    341      *     @type int          $number              Maximum number of comments to retrieve. Default null (no limit).
    342      *     @type int          $offset              Number of comments to offset the query. Used to build LIMIT clause.
    343      *                                             Default 0.
    344      *     @type string|array $orderby             Comment status or array of statuses. To use 'meta_value' or
    345      *                                             'meta_value_num', `$meta_key` must also be defined. To sort by
    346      *                                             a specific `$meta_query` clause, use that clause's array key.
    347      *                                             Accepts 'comment_agent', 'comment_approved', 'comment_author',
    348      *                                             'comment_author_email', 'comment_author_IP',
    349      *                                             'comment_author_url', 'comment_content', 'comment_date',
    350      *                                             'comment_date_gmt', 'comment_ID', 'comment_karma',
    351      *                                             'comment_parent', 'comment_post_ID', 'comment_type', 'user_id',
    352      *                                             'meta_value', 'meta_value_num', the value of $meta_key, and the
    353      *                                             array keys of `$meta_query`. Also accepts false, an empty array,
    354      *                                             or 'none' to disable `ORDER BY` clause.
    355      *                                             Default: 'comment_date_gmt'.
    356      *     @type string       $order               How to order retrieved comments. Accepts 'ASC', 'DESC'.
    357      *                                             Default: 'DESC'.
    358      *     @type int          $parent              Parent ID of comment to retrieve children of. Default empty.
    359      *     @type array        $post_author__in     Array of author IDs to retrieve comments for. Default empty.
    360      *     @type array        $post_author__not_in Array of author IDs *not* to retrieve comments for. Default empty.
    361      *     @type int          $post_ID             Currently unused.
    362      *     @type int          $post_id             Limit results to those affiliated with a given post ID. Default 0.
    363      *     @type array        $post__in            Array of post IDs to include affiliated comments for. Default empty.
    364      *     @type array        $post__not_in        Array of post IDs to exclude affiliated comments for. Default empty.
    365      *     @type int          $post_author         Comment author ID to limit results by. Default empty.
    366      *     @type string       $post_status         Post status to retrieve affiliated comments for. Default empty.
    367      *     @type string       $post_type           Post type to retrieve affiliated comments for. Default empty.
    368      *     @type string       $post_name           Post name to retrieve affiliated comments for. Default empty.
    369      *     @type int          $post_parent         Post parent ID to retrieve affiliated comments for. Default empty.
    370      *     @type string       $search              Search term(s) to retrieve matching comments for. Default empty.
    371      *     @type string       $status              Comment status to limit results by. Accepts 'hold'
    372      *                                             (`comment_status=0`), 'approve' (`comment_status=1`), 'all', or a
    373      *                                             custom comment status. Default 'all'.
    374      *     @type string|array $type                Include comments of a given type, or array of types. Accepts
    375      *                                             'comment', 'pings' (includes 'pingback' and 'trackback'), or any
    376      *                                             custom type string. Default empty.
    377      *     @type array        $type__in            Include comments from a given array of comment types. Default empty.
    378      *     @type array        $type__not_in        Exclude comments from a given array of comment types. Default empty.
    379      *     @type int          $user_id             Include comments for a specific user ID. Default empty.
    380      * }
    381      */
    382     public function __construct( $query = '' ) {
    383         $this->query_var_defaults = array(
    384             'author_email' => '',
    385             'author__in' => '',
    386             'author__not_in' => '',
    387             'include_unapproved' => '',
    388             'fields' => '',
    389             'ID' => '',
    390             'comment__in' => '',
    391             'comment__not_in' => '',
    392             'karma' => '',
    393             'number' => '',
    394             'offset' => '',
    395             'orderby' => '',
    396             'order' => 'DESC',
    397             'parent' => '',
    398             'post_author__in' => '',
    399             'post_author__not_in' => '',
    400             'post_ID' => '',
    401             'post_id' => 0,
    402             'post__in' => '',
    403             'post__not_in' => '',
    404             'post_author' => '',
    405             'post_name' => '',
    406             'post_parent' => '',
    407             'post_status' => '',
    408             'post_type' => '',
    409             'status' => 'all',
    410             'type' => '',
    411             'type__in' => '',
    412             'type__not_in' => '',
    413             'user_id' => '',
    414             'search' => '',
    415             'count' => false,
    416             'meta_key' => '',
    417             'meta_value' => '',
    418             'meta_query' => '',
    419             'date_query' => null, // See WP_Date_Query
    420         );
    421 
    422         if ( ! empty( $query ) ) {
    423             $this->query( $query );
    424         }
    425     }
    426 
    427     /**
    428      * Parse arguments passed to the comment query with default query parameters.
    429      *
    430      * @since  4.2.0 Extracted from WP_Comment_Query::query().
    431      *
    432      * @access public
    433      *
    434      * @param string|array $query WP_Comment_Query arguments. See WP_Comment_Query::__construct()
    435      */
    436     public function parse_query( $query = '' ) {
    437         if ( empty( $query ) ) {
    438             $query = $this->query_vars;
    439         }
    440 
    441         $this->query_vars = wp_parse_args( $query, $this->query_var_defaults );
    442         do_action_ref_array( 'parse_comment_query', array( &$this ) );
    443     }
    444 
    445     /**
    446      * Sets up the WordPress query for retrieving comments.
    447      *
    448      * @since 3.1.0
    449      * @since 4.1.0 Introduced 'comment__in', 'comment__not_in', 'post_author__in',
    450      *              'post_author__not_in', 'author__in', 'author__not_in', 'post__in',
    451      *              'post__not_in', 'include_unapproved', 'type__in', and 'type__not_in'
    452      *              arguments to $query_vars.
    453      * @since 4.2.0 Moved parsing to WP_Comment_Query::parse_query().
    454      * @access public
    455      *
    456      * @param string|array $query Array or URL query string of parameters.
    457      * @return array|int List of comments, or number of comments when 'count' is passed as a query var.
    458      */
    459     public function query( $query ) {
    460         $this->query_vars = wp_parse_args( $query );
    461         return $this->get_comments();
    462     }
    463 
    464     /**
    465      * Get a list of comments matching the query vars.
    466      *
    467      * @since 4.2.0
    468      * @access public
    469      *
    470      * @global wpdb $wpdb WordPress database abstraction object.
    471      *
    472      * @return int|array The list of comments.
    473      */
    474     public function get_comments() {
    475         global $wpdb;
    476 
    477         $groupby = '';
    478 
    479         $this->parse_query();
    480 
    481         // Parse meta query
    482         $this->meta_query = new WP_Meta_Query();
    483         $this->meta_query->parse_query_vars( $this->query_vars );
    484 
    485         /**
    486          * Fires before comments are retrieved.
    487          *
    488          * @since 3.1.0
    489          *
    490          * @param WP_Comment_Query &$this Current instance of WP_Comment_Query, passed by reference.
    491          */
    492         do_action_ref_array( 'pre_get_comments', array( &$this ) );
    493 
    494         // Reparse query vars, in case they were modified in a 'pre_get_comments' callback.
    495         $this->meta_query->parse_query_vars( $this->query_vars );
    496         if ( ! empty( $this->meta_query->queries ) ) {
    497             $meta_query_clauses = $this->meta_query->get_sql( 'comment', $wpdb->comments, 'comment_ID', $this );
    498         }
    499 
    500         // $args can include anything. Only use the args defined in the query_var_defaults to compute the key.
    501         $key = md5( serialize( wp_array_slice_assoc( $this->query_vars, array_keys( $this->query_var_defaults ) ) ) );
    502         $last_changed = wp_cache_get( 'last_changed', 'comment' );
    503         if ( ! $last_changed ) {
    504             $last_changed = microtime();
    505             wp_cache_set( 'last_changed', $last_changed, 'comment' );
    506         }
    507         $cache_key = "get_comments:$key:$last_changed";
    508 
    509         if ( $cache = wp_cache_get( $cache_key, 'comment' ) ) {
    510             $this->comments = $cache;
    511             return $this->comments;
    512         }
    513 
    514         $where = array();
    515 
    516         // Assemble clauses related to 'comment_approved'.
    517         $approved_clauses = array();
    518 
    519         // 'status' accepts an array or a comma-separated string.
    520         $status_clauses = array();
    521         $statuses = $this->query_vars['status'];
    522         if ( ! is_array( $statuses ) ) {
    523             $statuses = preg_split( '/[\s,]+/', $statuses );
    524         }
    525 
    526         // 'any' overrides other statuses.
    527         if ( ! in_array( 'any', $statuses ) ) {
    528             foreach ( $statuses as $status ) {
    529                 switch ( $status ) {
    530                     case 'hold' :
    531                         $status_clauses[] = "comment_approved = '0'";
    532                         break;
    533 
    534                     case 'approve' :
    535                         $status_clauses[] = "comment_approved = '1'";
    536                         break;
    537 
    538                     case 'all' :
    539                     case '' :
    540                         $status_clauses[] = "( comment_approved = '0' OR comment_approved = '1' )";
    541                         break;
    542 
    543                     default :
    544                         $status_clauses[] = $wpdb->prepare( "comment_approved = %s", $status );
    545                         break;
    546                 }
    547             }
    548 
    549             if ( ! empty( $status_clauses ) ) {
    550                 $approved_clauses[] = '( ' . implode( ' OR ', $status_clauses ) . ' )';
    551             }
    552         }
    553 
    554         // User IDs or emails whose unapproved comments are included, regardless of $status.
    555         if ( ! empty( $this->query_vars['include_unapproved'] ) ) {
    556             $include_unapproved = $this->query_vars['include_unapproved'];
    557 
    558             // Accepts arrays or comma-separated strings.
    559             if ( ! is_array( $include_unapproved ) ) {
    560                 $include_unapproved = preg_split( '/[\s,]+/', $include_unapproved );
    561             }
    562 
    563             $unapproved_ids = $unapproved_emails = array();
    564             foreach ( $include_unapproved as $unapproved_identifier ) {
    565                 // Numeric values are assumed to be user ids.
    566                 if ( is_numeric( $unapproved_identifier ) ) {
    567                     $approved_clauses[] = $wpdb->prepare( "( user_id = %d AND comment_approved = '0' )", $unapproved_identifier );
    568 
    569                 // Otherwise we match against email addresses.
    570                 } else {
    571                     $approved_clauses[] = $wpdb->prepare( "( comment_author_email = %s AND comment_approved = '0' )", $unapproved_identifier );
    572                 }
    573             }
    574         }
    575 
    576         // Collapse comment_approved clauses into a single OR-separated clause.
    577         if ( ! empty( $approved_clauses ) ) {
    578             if ( 1 === count( $approved_clauses ) ) {
    579                 $where[] = $approved_clauses[0];
    580             } else {
    581                 $where[] = '( ' . implode( ' OR ', $approved_clauses ) . ' )';
    582             }
    583         }
    584 
    585         $order = ( 'ASC' == strtoupper( $this->query_vars['order'] ) ) ? 'ASC' : 'DESC';
    586 
    587         // Disable ORDER BY with 'none', an empty array, or boolean false.
    588         if ( in_array( $this->query_vars['orderby'], array( 'none', array(), false ), true ) ) {
    589             $orderby = '';
    590         } elseif ( ! empty( $this->query_vars['orderby'] ) ) {
    591             $ordersby = is_array( $this->query_vars['orderby'] ) ?
    592                 $this->query_vars['orderby'] :
    593                 preg_split( '/[,\s]/', $this->query_vars['orderby'] );
    594 
    595             $orderby_array = array();
    596             $found_orderby_comment_ID = false;
    597             foreach ( $ordersby as $_key => $_value ) {
    598                 if ( ! $_value ) {
    599                     continue;
    600                 }
    601 
    602                 if ( is_int( $_key ) ) {
    603                     $_orderby = $_value;
    604                     $_order = $order;
    605                 } else {
    606                     $_orderby = $_key;
    607                     $_order = $_value;
    608                 }
    609 
    610                 if ( ! $found_orderby_comment_ID && 'comment_ID' === $_orderby ) {
    611                     $found_orderby_comment_ID = true;
    612                 }
    613 
    614                 $parsed = $this->parse_orderby( $_orderby );
    615 
    616                 if ( ! $parsed ) {
    617                     continue;
    618                 }
    619 
    620                 $orderby_array[] = $parsed . ' ' . $this->parse_order( $_order );
    621             }
    622 
    623             // If no valid clauses were found, order by comment_date_gmt.
    624             if ( empty( $orderby_array ) ) {
    625                 $orderby_array[] = "$wpdb->comments.comment_date_gmt $order";
    626             }
    627 
    628             // To ensure determinate sorting, always include a comment_ID clause.
    629             if ( ! $found_orderby_comment_ID ) {
    630                 $comment_ID_order = '';
    631 
    632                 // Inherit order from comment_date or comment_date_gmt, if available.
    633                 foreach ( $orderby_array as $orderby_clause ) {
    634                     if ( preg_match( '/comment_date(?:_gmt)*\ (ASC|DESC)/', $orderby_clause, $match ) ) {
    635                         $comment_ID_order = $match[1];
    636                         break;
    637                     }
    638                 }
    639 
    640                 // If no date-related order is available, use the date from the first available clause.
    641                 if ( ! $comment_ID_order ) {
    642                     foreach ( $orderby_array as $orderby_clause ) {
    643                         if ( false !== strpos( 'ASC', $orderby_clause ) ) {
    644                             $comment_ID_order = 'ASC';
    645                         } else {
    646                             $comment_ID_order = 'DESC';
    647                         }
    648 
    649                         break;
    650                     }
    651                 }
    652 
    653                 // Default to DESC.
    654                 if ( ! $comment_ID_order ) {
    655                     $comment_ID_order = 'DESC';
    656                 }
    657 
    658                 $orderby_array[] = "$wpdb->comments.comment_ID $comment_ID_order";
    659             }
    660 
    661             $orderby = implode( ', ', $orderby_array );
    662         } else {
    663             $orderby = "$wpdb->comments.comment_date_gmt $order";
    664         }
    665 
    666         $number = absint( $this->query_vars['number'] );
    667         $offset = absint( $this->query_vars['offset'] );
    668 
    669         if ( ! empty( $number ) ) {
    670             if ( $offset ) {
    671                 $limits = 'LIMIT ' . $offset . ',' . $number;
    672             } else {
    673                 $limits = 'LIMIT ' . $number;
    674             }
    675         } else {
    676             $limits = '';
    677         }
    678 
    679         if ( $this->query_vars['count'] ) {
    680             $fields = 'COUNT(*)';
    681         } else {
    682             switch ( strtolower( $this->query_vars['fields'] ) ) {
    683                 case 'ids':
    684                     $fields = "$wpdb->comments.comment_ID";
    685                     break;
    686                 default:
    687                     $fields = "*";
    688                     break;
    689             }
    690         }
    691 
    692         $join = '';
    693 
    694         $post_id = absint( $this->query_vars['post_id'] );
    695         if ( ! empty( $post_id ) ) {
    696             $where[] = $wpdb->prepare( 'comment_post_ID = %d', $post_id );
    697         }
    698 
    699         // Parse comment IDs for an IN clause.
    700         if ( ! empty( $this->query_vars['comment__in'] ) ) {
    701             $where[] = "$wpdb->comments.comment_ID IN ( " . implode( ',', wp_parse_id_list( $this->query_vars['comment__in'] ) ) . ' )';
    702         }
    703 
    704         // Parse comment IDs for a NOT IN clause.
    705         if ( ! empty( $this->query_vars['comment__not_in'] ) ) {
    706             $where[] = "$wpdb->comments.comment_ID NOT IN ( " . implode( ',', wp_parse_id_list( $this->query_vars['comment__not_in'] ) ) . ' )';
    707         }
    708 
    709         // Parse comment post IDs for an IN clause.
    710         if ( ! empty( $this->query_vars['post__in'] ) ) {
    711             $where[] = 'comment_post_ID IN ( ' . implode( ',', wp_parse_id_list( $this->query_vars['post__in'] ) ) . ' )';
    712         }
    713 
    714         // Parse comment post IDs for a NOT IN clause.
    715         if ( ! empty( $this->query_vars['post__not_in'] ) ) {
    716             $where[] = 'comment_post_ID NOT IN ( ' . implode( ',', wp_parse_id_list( $this->query_vars['post__not_in'] ) ) . ' )';
    717         }
    718 
    719         if ( '' !== $this->query_vars['author_email'] ) {
    720             $where[] = $wpdb->prepare( 'comment_author_email = %s', $this->query_vars['author_email'] );
    721         }
    722 
    723         if ( '' !== $this->query_vars['karma'] ) {
    724             $where[] = $wpdb->prepare( 'comment_karma = %d', $this->query_vars['karma'] );
    725         }
    726 
    727         // Filtering by comment_type: 'type', 'type__in', 'type__not_in'.
    728         $raw_types = array(
    729             'IN' => array_merge( (array) $this->query_vars['type'], (array) $this->query_vars['type__in'] ),
    730             'NOT IN' => (array) $this->query_vars['type__not_in'],
    731         );
    732 
    733         $comment_types = array();
    734         foreach ( $raw_types as $operator => $_raw_types ) {
    735             $_raw_types = array_unique( $_raw_types );
    736 
    737             foreach ( $_raw_types as $type ) {
    738                 switch ( $type ) {
    739                     // An empty translates to 'all', for backward compatibility
    740                     case '':
    741                     case 'all' :
    742                         break;
    743 
    744                     case 'comment':
    745                     case 'comments':
    746                         $comment_types[ $operator ][] = "''";
    747                         break;
    748 
    749                     case 'pings':
    750                         $comment_types[ $operator ][] = "'pingback'";
    751                         $comment_types[ $operator ][] = "'trackback'";
    752                         break;
    753 
    754                     default:
    755                         $comment_types[ $operator ][] = $wpdb->prepare( '%s', $type );
    756                         break;
    757                 }
    758             }
    759 
    760             if ( ! empty( $comment_types[ $operator ] ) ) {
    761                 $types_sql = implode( ', ', $comment_types[ $operator ] );
    762                 $where[] = "comment_type $operator ($types_sql)";
    763             }
    764         }
    765 
    766         if ( '' !== $this->query_vars['parent'] ) {
    767             $where[] = $wpdb->prepare( 'comment_parent = %d', $this->query_vars['parent'] );
    768         }
    769 
    770         if ( is_array( $this->query_vars['user_id'] ) ) {
    771             $where[] = 'user_id IN (' . implode( ',', array_map( 'absint', $this->query_vars['user_id'] ) ) . ')';
    772         } elseif ( '' !== $this->query_vars['user_id'] ) {
    773             $where[] = $wpdb->prepare( 'user_id = %d', $this->query_vars['user_id'] );
    774         }
    775 
    776         if ( '' !== $this->query_vars['search'] ) {
    777             $search_sql = $this->get_search_sql(
    778                 $this->query_vars['search'],
    779                 array( 'comment_author', 'comment_author_email', 'comment_author_url', 'comment_author_IP', 'comment_content' )
    780             );
    781 
    782             // Strip leading 'AND'.
    783             $where[] = preg_replace( '/^\s*AND\s*/', '', $search_sql );
    784         }
    785 
    786         // If any post-related query vars are passed, join the posts table.
    787         $join_posts_table = false;
    788         $plucked = wp_array_slice_assoc( $this->query_vars, array( 'post_author', 'post_name', 'post_parent', 'post_status', 'post_type' ) );
    789         $post_fields = array_filter( $plucked );
    790 
    791         if ( ! empty( $post_fields ) ) {
    792             $join_posts_table = true;
    793             foreach ( $post_fields as $field_name => $field_value ) {
    794                 // $field_value may be an array.
    795                 $esses = array_fill( 0, count( (array) $field_value ), '%s' );
    796                 $where[] = $wpdb->prepare( " {$wpdb->posts}.{$field_name} IN (" . implode( ',', $esses ) . ')', $field_value );
    797             }
    798         }
    799 
    800         // Comment author IDs for an IN clause.
    801         if ( ! empty( $this->query_vars['author__in'] ) ) {
    802             $where[] = 'user_id IN ( ' . implode( ',', wp_parse_id_list( $this->query_vars['author__in'] ) ) . ' )';
    803         }
    804 
    805         // Comment author IDs for a NOT IN clause.
    806         if ( ! empty( $this->query_vars['author__not_in'] ) ) {
    807             $where[] = 'user_id NOT IN ( ' . implode( ',', wp_parse_id_list( $this->query_vars['author__not_in'] ) ) . ' )';
    808         }
    809 
    810         // Post author IDs for an IN clause.
    811         if ( ! empty( $this->query_vars['post_author__in'] ) ) {
    812             $join_posts_table = true;
    813             $where[] = 'post_author IN ( ' . implode( ',', wp_parse_id_list( $this->query_vars['post_author__in'] ) ) . ' )';
    814         }
    815 
    816         // Post author IDs for a NOT IN clause.
    817         if ( ! empty( $this->query_vars['post_author__not_in'] ) ) {
    818             $join_posts_table = true;
    819             $where[] = 'post_author NOT IN ( ' . implode( ',', wp_parse_id_list( $this->query_vars['post_author__not_in'] ) ) . ' )';
    820         }
    821 
    822         if ( $join_posts_table ) {
    823             $join = "JOIN $wpdb->posts ON $wpdb->posts.ID = $wpdb->comments.comment_post_ID";
    824         }
    825 
    826         if ( ! empty( $meta_query_clauses ) ) {
    827             $join .= $meta_query_clauses['join'];
    828 
    829             // Strip leading 'AND'.
    830             $where[] = preg_replace( '/^\s*AND\s*/', '', $meta_query_clauses['where'] );
    831 
    832             if ( ! $this->query_vars['count'] ) {
    833                 $groupby = "{$wpdb->comments}.comment_ID";
    834             }
    835         }
    836 
    837         $date_query = $this->query_vars['date_query'];
    838         if ( ! empty( $date_query ) && is_array( $date_query ) ) {
    839             $date_query_object = new WP_Date_Query( $date_query, 'comment_date' );
    840             $where[] = preg_replace( '/^\s*AND\s*/', '', $date_query_object->get_sql() );
    841         }
    842 
    843         $where = implode( ' AND ', $where );
    844 
    845         $pieces = array( 'fields', 'join', 'where', 'orderby', 'limits', 'groupby' );
    846         /**
    847          * Filter the comment query clauses.
    848          *
    849          * @since 3.1.0
    850          *
    851          * @param array            $pieces A compacted array of comment query clauses.
    852          * @param WP_Comment_Query &$this  Current instance of WP_Comment_Query, passed by reference.
    853          */
    854         $clauses = apply_filters_ref_array( 'comments_clauses', array( compact( $pieces ), &$this ) );
    855 
    856         $fields = isset( $clauses[ 'fields' ] ) ? $clauses[ 'fields' ] : '';
    857         $join = isset( $clauses[ 'join' ] ) ? $clauses[ 'join' ] : '';
    858         $where = isset( $clauses[ 'where' ] ) ? $clauses[ 'where' ] : '';
    859         $orderby = isset( $clauses[ 'orderby' ] ) ? $clauses[ 'orderby' ] : '';
    860         $limits = isset( $clauses[ 'limits' ] ) ? $clauses[ 'limits' ] : '';
    861         $groupby = isset( $clauses[ 'groupby' ] ) ? $clauses[ 'groupby' ] : '';
    862 
    863         if ( $where ) {
    864             $where = 'WHERE ' . $where;
    865         }
    866 
    867         if ( $groupby ) {
    868             $groupby = 'GROUP BY ' . $groupby;
    869         }
    870 
    871         if ( $orderby ) {
    872             $orderby = "ORDER BY $orderby";
    873         }
    874 
    875         $this->request = "SELECT $fields FROM $wpdb->comments $join $where $groupby $orderby $limits";
    876 
    877         if ( $this->query_vars['count'] ) {
    878             return $wpdb->get_var( $this->request );
    879         }
    880 
    881         if ( 'ids' == $this->query_vars['fields'] ) {
    882             $this->comments = $wpdb->get_col( $this->request );
    883             return array_map( 'intval', $this->comments );
    884         }
    885 
    886         $results = $wpdb->get_results( $this->request );
    887         /**
    888          * Filter the comment query results.
    889          *
    890          * @since 3.1.0
    891          *
    892          * @param array            $results  An array of comments.
    893          * @param WP_Comment_Query &$this    Current instance of WP_Comment_Query, passed by reference.
    894          */
    895         $comments = apply_filters_ref_array( 'the_comments', array( $results, &$this ) );
    896 
    897         wp_cache_add( $cache_key, $comments, 'comment' );
    898         if ( '*' === $fields ) {
    899             update_comment_cache( $comments );
    900         }
    901 
    902         $this->comments = $comments;
    903         return $this->comments;
    904     }
    905 
    906     /**
    907      * Used internally to generate an SQL string for searching across multiple columns
    908      *
    909      * @since 3.1.0
    910      * @access protected
    911      *
    912      * @global wpdb $wpdb
    913      *
    914      * @param string $string
    915      * @param array $cols
    916      * @return string
    917      */
    918     protected function get_search_sql( $string, $cols ) {
    919         global $wpdb;
    920 
    921         $like = '%' . $wpdb->esc_like( $string ) . '%';
    922 
    923         $searches = array();
    924         foreach ( $cols as $col ) {
    925             $searches[] = $wpdb->prepare( "$col LIKE %s", $like );
    926         }
    927 
    928         return ' AND (' . implode(' OR ', $searches) . ')';
    929     }
    930 
    931     /**
    932      * Parse and sanitize 'orderby' keys passed to the comment query.
    933      *
    934      * @since 4.2.0
    935      * @access protected
    936      *
    937      * @global wpdb $wpdb WordPress database abstraction object.
    938      *
    939      * @param string $orderby Alias for the field to order by.
    940      * @return string|false Value to used in the ORDER clause. False otherwise.
    941      */
    942     protected function parse_orderby( $orderby ) {
    943         global $wpdb;
    944 
    945         $allowed_keys = array(
    946             'comment_agent',
    947             'comment_approved',
    948             'comment_author',
    949             'comment_author_email',
    950             'comment_author_IP',
    951             'comment_author_url',
    952             'comment_content',
    953             'comment_date',
    954             'comment_date_gmt',
    955             'comment_ID',
    956             'comment_karma',
    957             'comment_parent',
    958             'comment_post_ID',
    959             'comment_type',
    960             'user_id',
    961         );
    962 
    963         if ( ! empty( $this->query_vars['meta_key'] ) ) {
    964             $allowed_keys[] = $this->query_vars['meta_key'];
    965             $allowed_keys[] = 'meta_value';
    966             $allowed_keys[] = 'meta_value_num';
    967         }
    968 
    969         $meta_query_clauses = $this->meta_query->get_clauses();
    970         if ( $meta_query_clauses ) {
    971             $allowed_keys = array_merge( $allowed_keys, array_keys( $meta_query_clauses ) );
    972         }
    973 
    974         $parsed = false;
    975         if ( $orderby == $this->query_vars['meta_key'] || $orderby == 'meta_value' ) {
    976             $parsed = "$wpdb->commentmeta.meta_value";
    977         } elseif ( $orderby == 'meta_value_num' ) {
    978             $parsed = "$wpdb->commentmeta.meta_value+0";
    979         } elseif ( in_array( $orderby, $allowed_keys ) ) {
    980 
    981             if ( isset( $meta_query_clauses[ $orderby ] ) ) {
    982                 $meta_clause = $meta_query_clauses[ $orderby ];
    983                 $parsed = sprintf( "CAST(%s.meta_value AS %s)", esc_sql( $meta_clause['alias'] ), esc_sql( $meta_clause['cast'] ) );
    984             } else {
    985                 $parsed = "$wpdb->comments.$orderby";
    986             }
    987         }
    988 
    989         return $parsed;
    990     }
    991 
    992     /**
    993      * Parse an 'order' query variable and cast it to ASC or DESC as necessary.
    994      *
    995      * @since 4.2.0
    996      * @access protected
    997      *
    998      * @param string $order The 'order' query variable.
    999      * @return string The sanitized 'order' query variable.
    1000      */
    1001     protected function parse_order( $order ) {
    1002         if ( ! is_string( $order ) || empty( $order ) ) {
    1003             return 'DESC';
    1004         }
    1005 
    1006         if ( 'ASC' === strtoupper( $order ) ) {
    1007             return 'ASC';
    1008         } else {
    1009             return 'DESC';
    1010         }
    1011     }
    1012 }
    1013 
    1014 /**
    1015  * Retrieve all of the WordPress supported comment statuses.
    1016  *
    1017  * Comments have a limited set of valid status values, this provides the comment
    1018  * status values and descriptions.
    1019  *
    1020  * @since 2.7.0
    1021  *
    1022  * @return array List of comment statuses.
    1023  */
    1024 function get_comment_statuses() {
    1025     $status = array(
    1026         'hold'      => __('Unapproved'),
    1027         /* translators: comment status  */
    1028         'approve'   => _x('Approved', 'adjective'),
    1029         /* translators: comment status */
    1030         'spam'      => _x('Spam', 'adjective'),
    1031     );
    1032 
    1033     return $status;
    1034 }
    1035 
    1036 /**
    1037  * Gets the default comment status for a post type.
    1038  *
    1039  * @since 4.3.0
    1040  *
    1041  * @param string $post_type    Optional. Post type. Default 'post'.
    1042  * @param string $comment_type Optional. Comment type. Default 'comment'.
    1043  * @return string Expected return value is 'open' or 'closed'.
    1044  */
    1045 function get_default_comment_status( $post_type = 'post', $comment_type = 'comment' ) {
    1046     switch ( $comment_type ) {
    1047         case 'pingback' :
    1048         case 'trackback' :
    1049             $supports = 'trackbacks';
    1050             $option = 'ping';
    1051             break;
    1052         default :
    1053             $supports = 'comments';
    1054             $option = 'comment';
    1055     }
    1056 
    1057     // Set the status.
    1058     if ( 'page' === $post_type ) {
    1059         $status = 'closed';
    1060     } elseif ( post_type_supports( $post_type, $supports ) ) {
    1061         $status = get_option( "default_{$option}_status" );
    1062     } else {
    1063         $status = 'closed';
    1064     }
    1065 
    1066     /**
    1067      * Filter the default comment status for the given post type.
    1068      *
    1069      * @since 4.3.0
    1070      *
    1071      * @param string $status       Default status for the given post type,
    1072      *                             either 'open' or 'closed'.
    1073      * @param string $post_type    Post type. Default is `post`.
    1074      * @param string $comment_type Type of comment. Default is `comment`.
    1075      */
    1076     return apply_filters( 'get_default_comment_status' , $status, $post_type, $comment_type );
    1077 }
    1078 
    1079 /**
    1080  * The date the last comment was modified.
    1081  *
    1082  * @since 1.5.0
    1083  *
    1084  * @global wpdb $wpdb WordPress database abstraction object.
    1085  * @staticvar array $cache_lastcommentmodified
    1086  *
    1087  * @param string $timezone Which timezone to use in reference to 'gmt', 'blog',
    1088  *      or 'server' locations.
    1089  * @return string Last comment modified date.
    1090  */
    1091 function get_lastcommentmodified($timezone = 'server') {
    1092     global $wpdb;
    1093     static $cache_lastcommentmodified = array();
    1094 
    1095     if ( isset($cache_lastcommentmodified[$timezone]) )
    1096         return $cache_lastcommentmodified[$timezone];
    1097 
    1098     $add_seconds_server = date('Z');
    1099 
    1100     switch ( strtolower($timezone)) {
    1101         case 'gmt':
    1102             $lastcommentmodified = $wpdb->get_var("SELECT comment_date_gmt FROM $wpdb->comments WHERE comment_approved = '1' ORDER BY comment_date_gmt DESC LIMIT 1");
    1103             break;
    1104         case 'blog':
    1105             $lastcommentmodified = $wpdb->get_var("SELECT comment_date FROM $wpdb->comments WHERE comment_approved = '1' ORDER BY comment_date_gmt DESC LIMIT 1");
    1106             break;
    1107         case 'server':
    1108             $lastcommentmodified = $wpdb->get_var($wpdb->prepare("SELECT DATE_ADD(comment_date_gmt, INTERVAL %s SECOND) FROM $wpdb->comments WHERE comment_approved = '1' ORDER BY comment_date_gmt DESC LIMIT 1", $add_seconds_server));
    1109             break;
    1110     }
    1111 
    1112     $cache_lastcommentmodified[$timezone] = $lastcommentmodified;
    1113 
    1114     return $lastcommentmodified;
    1115 }
    1116 
    1117 /**
    1118  * The amount of comments in a post or total comments.
    1119  *
    1120  * A lot like {@link wp_count_comments()}, in that they both return comment
    1121  * stats (albeit with different types). The {@link wp_count_comments()} actual
    1122  * caches, but this function does not.
    1123  *
    1124  * @since 2.0.0
    1125  *
    1126  * @global wpdb $wpdb WordPress database abstraction object.
    1127  *
    1128  * @param int $post_id Optional. Comment amount in post if > 0, else total comments blog wide.
    1129  * @return array The amount of spam, approved, awaiting moderation, and total comments.
    1130  */
    1131 function get_comment_count( $post_id = 0 ) {
    1132     global $wpdb;
    1133 
    1134     $post_id = (int) $post_id;
    1135 
    1136     $where = '';
    1137     if ( $post_id > 0 ) {
    1138         $where = $wpdb->prepare("WHERE comment_post_ID = %d", $post_id);
    1139     }
    1140 
    1141     $totals = (array) $wpdb->get_results("
    1142         SELECT comment_approved, COUNT( * ) AS total
    1143         FROM {$wpdb->comments}
    1144         {$where}
    1145         GROUP BY comment_approved
    1146     ", ARRAY_A);
    1147 
    1148     $comment_count = array(
    1149         "approved"              => 0,
    1150         "awaiting_moderation"   => 0,
    1151         "spam"                  => 0,
    1152         "total_comments"        => 0
    1153     );
    1154 
    1155     foreach ( $totals as $row ) {
    1156         switch ( $row['comment_approved'] ) {
    1157             case 'spam':
    1158                 $comment_count['spam'] = $row['total'];
    1159                 $comment_count["total_comments"] += $row['total'];
    1160                 break;
    1161             case 1:
    1162                 $comment_count['approved'] = $row['total'];
    1163                 $comment_count['total_comments'] += $row['total'];
    1164                 break;
    1165             case 0:
    1166                 $comment_count['awaiting_moderation'] = $row['total'];
    1167                 $comment_count['total_comments'] += $row['total'];
    1168                 break;
    1169             default:
    1170                 break;
    1171         }
    1172     }
    1173 
    1174     return $comment_count;
    1175 }
    1176 
    1177 //
    1178 // Comment meta functions
    1179 //
    1180 
    1181 /**
    1182  * Add meta data field to a comment.
    1183  *
    1184  * @since 2.9.0
    1185  * @link https://codex.wordpress.org/Function_Reference/add_comment_meta
    1186  *
    1187  * @param int $comment_id Comment ID.
    1188  * @param string $meta_key Metadata name.
    1189  * @param mixed $meta_value Metadata value.
    1190  * @param bool $unique Optional, default is false. Whether the same key should not be added.
    1191  * @return int|bool Meta ID on success, false on failure.
    1192  */
    1193 function add_comment_meta($comment_id, $meta_key, $meta_value, $unique = false) {
    1194     return add_metadata('comment', $comment_id, $meta_key, $meta_value, $unique);
    1195 }
    1196 
    1197 /**
    1198  * Remove metadata matching criteria from a comment.
    1199  *
    1200  * You can match based on the key, or key and value. Removing based on key and
    1201  * value, will keep from removing duplicate metadata with the same key. It also
    1202  * allows removing all metadata matching key, if needed.
    1203  *
    1204  * @since 2.9.0
    1205  * @link https://codex.wordpress.org/Function_Reference/delete_comment_meta
    1206  *
    1207  * @param int $comment_id comment ID
    1208  * @param string $meta_key Metadata name.
    1209  * @param mixed $meta_value Optional. Metadata value.
    1210  * @return bool True on success, false on failure.
    1211  */
    1212 function delete_comment_meta($comment_id, $meta_key, $meta_value = '') {
    1213     return delete_metadata('comment', $comment_id, $meta_key, $meta_value);
    1214 }
    1215 
    1216 /**
    1217  * Retrieve comment meta field for a comment.
    1218  *
    1219  * @since 2.9.0
    1220  * @link https://codex.wordpress.org/Function_Reference/get_comment_meta
    1221  *
    1222  * @param int $comment_id Comment ID.
    1223  * @param string $key Optional. The meta key to retrieve. By default, returns data for all keys.
    1224  * @param bool $single Whether to return a single value.
    1225  * @return mixed Will be an array if $single is false. Will be value of meta data field if $single
    1226  *  is true.
    1227  */
    1228 function get_comment_meta($comment_id, $key = '', $single = false) {
    1229     return get_metadata('comment', $comment_id, $key, $single);
    1230 }
    1231 
    1232 /**
    1233  * Update comment meta field based on comment ID.
    1234  *
    1235  * Use the $prev_value parameter to differentiate between meta fields with the
    1236  * same key and comment ID.
    1237  *
    1238  * If the meta field for the comment does not exist, it will be added.
    1239  *
    1240  * @since 2.9.0
    1241  * @link https://codex.wordpress.org/Function_Reference/update_comment_meta
    1242  *
    1243  * @param int $comment_id Comment ID.
    1244  * @param string $meta_key Metadata key.
    1245  * @param mixed $meta_value Metadata value.
    1246  * @param mixed $prev_value Optional. Previous value to check before removing.
    1247  * @return int|bool Meta ID if the key didn't exist, true on successful update, false on failure.
    1248  */
    1249 function update_comment_meta($comment_id, $meta_key, $meta_value, $prev_value = '') {
    1250     return update_metadata('comment', $comment_id, $meta_key, $meta_value, $prev_value);
    1251 }
    1252 
    1253 /**
    1254  * Sets the cookies used to store an unauthenticated commentator's identity. Typically used
    1255  * to recall previous comments by this commentator that are still held in moderation.
    1256  *
    1257  * @param object $comment Comment object.
    1258  * @param object $user Comment author's object.
    1259  *
    1260  * @since 3.4.0
    1261  */
    1262 function wp_set_comment_cookies($comment, $user) {
    1263     if ( $user->exists() )
    1264         return;
    1265 
    1266     /**
    1267      * Filter the lifetime of the comment cookie in seconds.
    1268      *
    1269      * @since 2.8.0
    1270      *
    1271      * @param int $seconds Comment cookie lifetime. Default 30000000.
    1272      */
    1273     $comment_cookie_lifetime = apply_filters( 'comment_cookie_lifetime', 30000000 );
    1274     $secure = ( 'https' === parse_url( home_url(), PHP_URL_SCHEME ) );
    1275     setcookie( 'comment_author_' . COOKIEHASH, $comment->comment_author, time() + $comment_cookie_lifetime, COOKIEPATH, COOKIE_DOMAIN, $secure );
    1276     setcookie( 'comment_author_email_' . COOKIEHASH, $comment->comment_author_email, time() + $comment_cookie_lifetime, COOKIEPATH, COOKIE_DOMAIN, $secure );
    1277     setcookie( 'comment_author_url_' . COOKIEHASH, esc_url($comment->comment_author_url), time() + $comment_cookie_lifetime, COOKIEPATH, COOKIE_DOMAIN, $secure );
    1278 }
    1279 
    1280 /**
    1281  * Sanitizes the cookies sent to the user already.
    1282  *
    1283  * Will only do anything if the cookies have already been created for the user.
    1284  * Mostly used after cookies had been sent to use elsewhere.
    1285  *
    1286  * @since 2.0.4
    1287  */
    1288 function sanitize_comment_cookies() {
    1289     if ( isset( $_COOKIE['comment_author_' . COOKIEHASH] ) ) {
    1290         /**
    1291          * Filter the comment author's name cookie before it is set.
    1292          *
    1293          * When this filter hook is evaluated in wp_filter_comment(),
    1294          * the comment author's name string is passed.
    1295          *
    1296          * @since 1.5.0
    1297          *
    1298          * @param string $author_cookie The comment author name cookie.
    1299          */
    1300         $comment_author = apply_filters( 'pre_comment_author_name', $_COOKIE['comment_author_' . COOKIEHASH] );
    1301         $comment_author = wp_unslash($comment_author);
    1302         $comment_author = esc_attr($comment_author);
    1303         $_COOKIE['comment_author_' . COOKIEHASH] = $comment_author;
    1304     }
    1305 
    1306     if ( isset( $_COOKIE['comment_author_email_' . COOKIEHASH] ) ) {
    1307         /**
    1308          * Filter the comment author's email cookie before it is set.
    1309          *
    1310          * When this filter hook is evaluated in wp_filter_comment(),
    1311          * the comment author's email string is passed.
    1312          *
    1313          * @since 1.5.0
    1314          *
    1315          * @param string $author_email_cookie The comment author email cookie.
    1316          */
    1317         $comment_author_email = apply_filters( 'pre_comment_author_email', $_COOKIE['comment_author_email_' . COOKIEHASH] );
    1318         $comment_author_email = wp_unslash($comment_author_email);
    1319         $comment_author_email = esc_attr($comment_author_email);
    1320         $_COOKIE['comment_author_email_'.COOKIEHASH] = $comment_author_email;
    1321     }
    1322 
    1323     if ( isset( $_COOKIE['comment_author_url_' . COOKIEHASH] ) ) {
    1324         /**
    1325          * Filter the comment author's URL cookie before it is set.
    1326          *
    1327          * When this filter hook is evaluated in wp_filter_comment(),
    1328          * the comment author's URL string is passed.
    1329          *
    1330          * @since 1.5.0
    1331          *
    1332          * @param string $author_url_cookie The comment author URL cookie.
    1333          */
    1334         $comment_author_url = apply_filters( 'pre_comment_author_url', $_COOKIE['comment_author_url_' . COOKIEHASH] );
    1335         $comment_author_url = wp_unslash($comment_author_url);
    1336         $_COOKIE['comment_author_url_'.COOKIEHASH] = $comment_author_url;
    1337     }
    1338 }
    1339 
    1340 /**
    1341  * Validates whether this comment is allowed to be made.
    1342  *
    1343  * @since 2.0.0
    1344  *
    1345  * @global wpdb $wpdb WordPress database abstraction object.
    1346  *
    1347  * @param array $commentdata Contains information on the comment
    1348  * @return int|string Signifies the approval status (0|1|'spam')
    1349  */
    1350 function wp_allow_comment( $commentdata ) {
    1351     global $wpdb;
    1352 
    1353     // Simple duplicate check
    1354     // expected_slashed ($comment_post_ID, $comment_author, $comment_author_email, $comment_content)
    1355     $dupe = $wpdb->prepare(
    1356         "SELECT comment_ID FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_parent = %s AND comment_approved != 'trash' AND ( comment_author = %s ",
    1357         wp_unslash( $commentdata['comment_post_ID'] ),
    1358         wp_unslash( $commentdata['comment_parent'] ),
    1359         wp_unslash( $commentdata['comment_author'] )
    1360     );
    1361     if ( $commentdata['comment_author_email'] ) {
    1362         $dupe .= $wpdb->prepare(
    1363             "OR comment_author_email = %s ",
    1364             wp_unslash( $commentdata['comment_author_email'] )
    1365         );
    1366     }
    1367     $dupe .= $wpdb->prepare(
    1368         ") AND comment_content = %s LIMIT 1",
    1369         wp_unslash( $commentdata['comment_content'] )
    1370     );
    1371     if ( $wpdb->get_var( $dupe ) ) {
    1372         /**
    1373          * Fires immediately after a duplicate comment is detected.
    1374          *
    1375          * @since 3.0.0
    1376          *
    1377          * @param array $commentdata Comment data.
    1378          */
    1379         do_action( 'comment_duplicate_trigger', $commentdata );
    1380         if ( defined( 'DOING_AJAX' ) ) {
    1381             die( __('Duplicate comment detected; it looks as though you&#8217;ve already said that!') );
    1382         }
    1383         wp_die( __( 'Duplicate comment detected; it looks as though you&#8217;ve already said that!' ), 409 );
    1384     }
    1385 
    1386     /**
    1387      * Fires immediately before a comment is marked approved.
    1388      *
    1389      * Allows checking for comment flooding.
    1390      *
    1391      * @since 2.3.0
    1392      *
    1393      * @param string $comment_author_IP    Comment author's IP address.
    1394      * @param string $comment_author_email Comment author's email.
    1395      * @param string $comment_date_gmt     GMT date the comment was posted.
    1396      */
    1397     do_action(
    1398         'check_comment_flood',
    1399         $commentdata['comment_author_IP'],
    1400         $commentdata['comment_author_email'],
    1401         $commentdata['comment_date_gmt']
    1402     );
    1403 
    1404     if ( ! empty( $commentdata['user_id'] ) ) {
    1405         $user = get_userdata( $commentdata['user_id'] );
    1406         $post_author = $wpdb->get_var( $wpdb->prepare(
    1407             "SELECT post_author FROM $wpdb->posts WHERE ID = %d LIMIT 1",
    1408             $commentdata['comment_post_ID']
    1409         ) );
    1410     }
    1411 
    1412     if ( isset( $user ) && ( $commentdata['user_id'] == $post_author || $user->has_cap( 'moderate_comments' ) ) ) {
    1413         // The author and the admins get respect.
    1414         $approved = 1;
    1415     } else {
    1416         // Everyone else's comments will be checked.
    1417         if ( check_comment(
    1418             $commentdata['comment_author'],
    1419             $commentdata['comment_author_email'],
    1420             $commentdata['comment_author_url'],
    1421             $commentdata['comment_content'],
    1422             $commentdata['comment_author_IP'],
    1423             $commentdata['comment_agent'],
    1424             $commentdata['comment_type']
    1425         ) ) {
    1426             $approved = 1;
    1427         } else {
    1428             $approved = 0;
    1429         }
    1430 
    1431         if ( wp_blacklist_check(
    1432             $commentdata['comment_author'],
    1433             $commentdata['comment_author_email'],
    1434             $commentdata['comment_author_url'],
    1435             $commentdata['comment_content'],
    1436             $commentdata['comment_author_IP'],
    1437             $commentdata['comment_agent']
    1438         ) ) {
    1439             $approved = 'spam';
    1440         }
    1441     }
    1442 
    1443     /**
    1444      * Filter a comment's approval status before it is set.
    1445      *
    1446      * @since 2.1.0
    1447      *
    1448      * @param bool|string $approved    The approval status. Accepts 1, 0, or 'spam'.
    1449      * @param array       $commentdata Comment data.
    1450      */
    1451     $approved = apply_filters( 'pre_comment_approved', $approved, $commentdata );
    1452     return $approved;
    1453 }
    1454 
    1455 /**
    1456  * Check whether comment flooding is occurring.
    1457  *
    1458  * Won't run, if current user can manage options, so to not block
    1459  * administrators.
    1460  *
    1461  * @since 2.3.0
    1462  *
    1463  * @global wpdb $wpdb WordPress database abstraction object.
    1464  *
    1465  * @param string $ip Comment IP.
    1466  * @param string $email Comment author email address.
    1467  * @param string $date MySQL time string.
    1468  */
    1469 function check_comment_flood_db( $ip, $email, $date ) {
    1470     global $wpdb;
    1471     if ( current_user_can( 'manage_options' ) )
    1472         return; // don't throttle admins
    1473     $hour_ago = gmdate( 'Y-m-d H:i:s', time() - HOUR_IN_SECONDS );
    1474     if ( $lasttime = $wpdb->get_var( $wpdb->prepare( "SELECT `comment_date_gmt` FROM `$wpdb->comments` WHERE `comment_date_gmt` >= %s AND ( `comment_author_IP` = %s OR `comment_author_email` = %s ) ORDER BY `comment_date_gmt` DESC LIMIT 1", $hour_ago, $ip, $email ) ) ) {
    1475         $time_lastcomment = mysql2date('U', $lasttime, false);
    1476         $time_newcomment  = mysql2date('U', $date, false);
    1477         /**
    1478          * Filter the comment flood status.
    1479          *
    1480          * @since 2.1.0
    1481          *
    1482          * @param bool $bool             Whether a comment flood is occurring. Default false.
    1483          * @param int  $time_lastcomment Timestamp of when the last comment was posted.
    1484          * @param int  $time_newcomment  Timestamp of when the new comment was posted.
    1485          */
    1486         $flood_die = apply_filters( 'comment_flood_filter', false, $time_lastcomment, $time_newcomment );
    1487         if ( $flood_die ) {
    1488             /**
    1489              * Fires before the comment flood message is triggered.
    1490              *
    1491              * @since 1.5.0
    1492              *
    1493              * @param int $time_lastcomment Timestamp of when the last comment was posted.
    1494              * @param int $time_newcomment  Timestamp of when the new comment was posted.
    1495              */
    1496             do_action( 'comment_flood_trigger', $time_lastcomment, $time_newcomment );
    1497 
    1498             if ( defined('DOING_AJAX') )
    1499                 die( __('You are posting comments too quickly. Slow down.') );
    1500 
    1501             wp_die( __( 'You are posting comments too quickly. Slow down.' ), 429 );
    1502         }
    1503     }
    1504 }
    1505 
    1506 /**
    1507  * Separates an array of comments into an array keyed by comment_type.
    1508  *
    1509  * @since 2.7.0
    1510  *
    1511  * @param array $comments Array of comments
    1512  * @return array Array of comments keyed by comment_type.
    1513  */
    1514 function separate_comments(&$comments) {
    1515     $comments_by_type = array('comment' => array(), 'trackback' => array(), 'pingback' => array(), 'pings' => array());
    1516     $count = count($comments);
    1517     for ( $i = 0; $i < $count; $i++ ) {
    1518         $type = $comments[$i]->comment_type;
    1519         if ( empty($type) )
    1520             $type = 'comment';
    1521         $comments_by_type[$type][] = &$comments[$i];
    1522         if ( 'trackback' == $type || 'pingback' == $type )
    1523             $comments_by_type['pings'][] = &$comments[$i];
    1524     }
    1525 
    1526     return $comments_by_type;
    1527 }
    1528 
    1529 /**
    1530  * Calculate the total number of comment pages.
    1531  *
    1532  * @since 2.7.0
    1533  *
    1534  * @uses Walker_Comment
    1535  *
    1536  * @global WP_Query $wp_query
    1537  *
    1538  * @param array $comments Optional array of comment objects. Defaults to $wp_query->comments
    1539  * @param int   $per_page Optional comments per page.
    1540  * @param bool  $threaded Optional control over flat or threaded comments.
    1541  * @return int Number of comment pages.
    1542  */
    1543 function get_comment_pages_count( $comments = null, $per_page = null, $threaded = null ) {
    1544     global $wp_query;
    1545 
    1546     if ( null === $comments && null === $per_page && null === $threaded && !empty($wp_query->max_num_comment_pages) )
    1547         return $wp_query->max_num_comment_pages;
    1548 
    1549     if ( ( ! $comments || ! is_array( $comments ) ) && ! empty( $wp_query->comments )  )
    1550         $comments = $wp_query->comments;
    1551 
    1552     if ( empty($comments) )
    1553         return 0;
    1554 
    1555     if ( ! get_option( 'page_comments' ) )
    1556         return 1;
    1557 
    1558     if ( !isset($per_page) )
    1559         $per_page = (int) get_query_var('comments_per_page');
    1560     if ( 0 === $per_page )
    1561         $per_page = (int) get_option('comments_per_page');
    1562     if ( 0 === $per_page )
    1563         return 1;
    1564 
    1565     if ( !isset($threaded) )
    1566         $threaded = get_option('thread_comments');
    1567 
    1568     if ( $threaded ) {
    1569         $walker = new Walker_Comment;
    1570         $count = ceil( $walker->get_number_of_root_elements( $comments ) / $per_page );
    1571     } else {
    1572         $count = ceil( count( $comments ) / $per_page );
    1573     }
    1574 
    1575     return $count;
    1576 }
    1577 
    1578 /**
    1579  * Calculate what page number a comment will appear on for comment paging.
    1580  *
    1581  * @since 2.7.0
    1582  *
    1583  * @global wpdb $wpdb
    1584  *
    1585  * @param int $comment_ID Comment ID.
    1586  * @param array $args Optional args.
    1587  * @return int|null Comment page number or null on error.
    1588  */
    1589 function get_page_of_comment( $comment_ID, $args = array() ) {
    1590     global $wpdb;
    1591 
    1592     if ( !$comment = get_comment( $comment_ID ) )
    1593         return;
    1594 
    1595     $defaults = array( 'type' => 'all', 'page' => '', 'per_page' => '', 'max_depth' => '' );
    1596     $args = wp_parse_args( $args, $defaults );
    1597 
    1598     if ( '' === $args['per_page'] && get_option('page_comments') )
    1599         $args['per_page'] = get_query_var('comments_per_page');
    1600     if ( empty($args['per_page']) ) {
    1601         $args['per_page'] = 0;
    1602         $args['page'] = 0;
    1603     }
    1604     if ( $args['per_page'] < 1 )
    1605         return 1;
    1606 
    1607     if ( '' === $args['max_depth'] ) {
    1608         if ( get_option('thread_comments') )
    1609             $args['max_depth'] = get_option('thread_comments_depth');
    1610         else
    1611             $args['max_depth'] = -1;
    1612     }
    1613 
    1614     // Find this comment's top level parent if threading is enabled
    1615     if ( $args['max_depth'] > 1 && 0 != $comment->comment_parent )
    1616         return get_page_of_comment( $comment->comment_parent, $args );
    1617 
    1618     $allowedtypes = array(
    1619         'comment' => '',
    1620         'pingback' => 'pingback',
    1621         'trackback' => 'trackback',
    1622     );
    1623 
    1624     $comtypewhere = ( 'all' != $args['type'] && isset($allowedtypes[$args['type']]) ) ? " AND comment_type = '" . $allowedtypes[$args['type']] . "'" : '';
    1625 
    1626     // Count comments older than this one
    1627     $oldercoms = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(comment_ID) FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_parent = 0 AND comment_approved = '1' AND comment_date_gmt < '%s'" . $comtypewhere, $comment->comment_post_ID, $comment->comment_date_gmt ) );
    1628 
    1629     // No older comments? Then it's page #1.
    1630     if ( 0 == $oldercoms )
    1631         return 1;
    1632 
    1633     // Divide comments older than this one by comments per page to get this comment's page number
    1634     return ceil( ( $oldercoms + 1 ) / $args['per_page'] );
    1635 }
    1636 
    1637 /**
    1638  * Does comment contain blacklisted characters or words.
    1639  *
    1640  * @since 1.5.0
    1641  *
    1642  * @param string $author The author of the comment
    1643  * @param string $email The email of the comment
    1644  * @param string $url The url used in the comment
    1645  * @param string $comment The comment content
    1646  * @param string $user_ip The comment author IP address
    1647  * @param string $user_agent The author's browser user agent
    1648  * @return bool True if comment contains blacklisted content, false if comment does not
    1649  */
    1650 function wp_blacklist_check($author, $email, $url, $comment, $user_ip, $user_agent) {
    1651     /**
    1652      * Fires before the comment is tested for blacklisted characters or words.
    1653      *
    1654      * @since 1.5.0
    1655      *
    1656      * @param string $author     Comment author.
    1657      * @param string $email      Comment author's email.
    1658      * @param string $url        Comment author's URL.
    1659      * @param string $comment    Comment content.
    1660      * @param string $user_ip    Comment author's IP address.
    1661      * @param string $user_agent Comment author's browser user agent.
    1662      */
    1663     do_action( 'wp_blacklist_check', $author, $email, $url, $comment, $user_ip, $user_agent );
    1664 
    1665     $mod_keys = trim( get_option('blacklist_keys') );
    1666     if ( '' == $mod_keys )
    1667         return false; // If moderation keys are empty
    1668     $words = explode("\n", $mod_keys );
    1669 
    1670     foreach ( (array) $words as $word ) {
    1671         $word = trim($word);
    1672 
    1673         // Skip empty lines
    1674         if ( empty($word) ) { continue; }
    1675 
    1676         // Do some escaping magic so that '#' chars in the
    1677         // spam words don't break things:
    1678         $word = preg_quote($word, '#');
    1679 
    1680         $pattern = "#$word#i";
    1681         if (
    1682                preg_match($pattern, $author)
    1683             || preg_match($pattern, $email)
    1684             || preg_match($pattern, $url)
    1685             || preg_match($pattern, $comment)
    1686             || preg_match($pattern, $user_ip)
    1687             || preg_match($pattern, $user_agent)
    1688          )
    1689             return true;
    1690     }
    1691     return false;
    1692 }
    1693 
    1694 /**
    1695  * Retrieve total comments for blog or single post.
    1696  *
    1697  * The properties of the returned object contain the 'moderated', 'approved',
    1698  * and spam comments for either the entire blog or single post. Those properties
    1699  * contain the amount of comments that match the status. The 'total_comments'
    1700  * property contains the integer of total comments.
    1701  *
    1702  * The comment stats are cached and then retrieved, if they already exist in the
    1703  * cache.
    1704  *
    1705  * @since 2.5.0
    1706  *
    1707  * @global wpdb $wpdb
    1708  *
    1709  * @param int $post_id Optional. Post ID.
    1710  * @return object|array Comment stats.
    1711  */
    1712 function wp_count_comments( $post_id = 0 ) {
    1713     global $wpdb;
    1714 
    1715     $post_id = (int) $post_id;
    1716 
    1717     /**
    1718      * Filter the comments count for a given post.
    1719      *
    1720      * @since 2.7.0
    1721      *
    1722      * @param array $count   An empty array.
    1723      * @param int   $post_id The post ID.
    1724      */
    1725     $stats = apply_filters( 'wp_count_comments', array(), $post_id );
    1726     if ( !empty($stats) )
    1727         return $stats;
    1728 
    1729     $count = wp_cache_get("comments-{$post_id}", 'counts');
    1730 
    1731     if ( false !== $count )
    1732         return $count;
    1733 
    1734     $where = '';
    1735     if ( $post_id > 0 )
    1736         $where = $wpdb->prepare( "WHERE comment_post_ID = %d", $post_id );
    1737 
    1738     $count = $wpdb->get_results( "SELECT comment_approved, COUNT( * ) AS num_comments FROM {$wpdb->comments} {$where} GROUP BY comment_approved", ARRAY_A );
    1739 
    1740     $total = 0;
    1741     $approved = array('0' => 'moderated', '1' => 'approved', 'spam' => 'spam', 'trash' => 'trash', 'post-trashed' => 'post-trashed');
    1742     foreach ( (array) $count as $row ) {
    1743         // Don't count post-trashed toward totals
    1744         if ( 'post-trashed' != $row['comment_approved'] && 'trash' != $row['comment_approved'] )
    1745             $total += $row['num_comments'];
    1746         if ( isset( $approved[$row['comment_approved']] ) )
    1747             $stats[$approved[$row['comment_approved']]] = $row['num_comments'];
    1748     }
    1749 
    1750     $stats['total_comments'] = $total;
    1751     foreach ( $approved as $key ) {
    1752         if ( empty($stats[$key]) )
    1753             $stats[$key] = 0;
    1754     }
    1755 
    1756     $stats = (object) $stats;
    1757     wp_cache_set("comments-{$post_id}", $stats, 'counts');
    1758 
    1759     return $stats;
    1760 }
    1761 
    1762 /**
    1763  * Trashes or deletes a comment.
    1764  *
    1765  * The comment is moved to trash instead of permanently deleted unless trash is
    1766  * disabled, item is already in the trash, or $force_delete is true.
    1767  *
    1768  * The post comment count will be updated if the comment was approved and has a
    1769  * post ID available.
    1770  *
    1771  * @since 2.0.0
    1772  *
    1773  * @global wpdb $wpdb WordPress database abstraction object.
    1774  *
    1775  * @param int $comment_id Comment ID
    1776  * @param bool $force_delete Whether to bypass trash and force deletion. Default is false.
    1777  * @return bool True on success, false on failure.
    1778  */
    1779 function wp_delete_comment($comment_id, $force_delete = false) {
    1780     global $wpdb;
    1781     if (!$comment = get_comment($comment_id))
    1782         return false;
    1783 
    1784     if ( !$force_delete && EMPTY_TRASH_DAYS && !in_array( wp_get_comment_status($comment_id), array( 'trash', 'spam' ) ) )
    1785         return wp_trash_comment($comment_id);
    1786 
    1787     /**
    1788      * Fires immediately before a comment is deleted from the database.
    1789      *
    1790      * @since 1.2.0
    1791      *
    1792      * @param int $comment_id The comment ID.
    1793      */
    1794     do_action( 'delete_comment', $comment_id );
    1795 
    1796     // Move children up a level.
    1797     $children = $wpdb->get_col( $wpdb->prepare("SELECT comment_ID FROM $wpdb->comments WHERE comment_parent = %d", $comment_id) );
    1798     if ( !empty($children) ) {
    1799         $wpdb->update($wpdb->comments, array('comment_parent' => $comment->comment_parent), array('comment_parent' => $comment_id));
    1800         clean_comment_cache($children);
    1801     }
    1802 
    1803     // Delete metadata
    1804     $meta_ids = $wpdb->get_col( $wpdb->prepare( "SELECT meta_id FROM $wpdb->commentmeta WHERE comment_id = %d", $comment_id ) );
    1805     foreach ( $meta_ids as $mid )
    1806         delete_metadata_by_mid( 'comment', $mid );
    1807 
    1808     if ( ! $wpdb->delete( $wpdb->comments, array( 'comment_ID' => $comment_id ) ) )
    1809         return false;
    1810 
    1811     /**
    1812      * Fires immediately after a comment is deleted from the database.
    1813      *
    1814      * @since 2.9.0
    1815      *
    1816      * @param int $comment_id The comment ID.
    1817      */
    1818     do_action( 'deleted_comment', $comment_id );
    1819 
    1820     $post_id = $comment->comment_post_ID;
    1821     if ( $post_id && $comment->comment_approved == 1 )
    1822         wp_update_comment_count($post_id);
    1823 
    1824     clean_comment_cache($comment_id);
    1825 
    1826     /** This action is documented in wp-includes/comment.php */
    1827     do_action( 'wp_set_comment_status', $comment_id, 'delete' );
    1828 
    1829     wp_transition_comment_status('delete', $comment->comment_approved, $comment);
    1830     return true;
    1831 }
    1832 
    1833 /**
    1834  * Moves a comment to the Trash
    1835  *
    1836  * If trash is disabled, comment is permanently deleted.
    1837  *
    1838  * @since 2.9.0
    1839  *
    1840  * @param int $comment_id Comment ID.
    1841  * @return bool True on success, false on failure.
    1842  */
    1843 function wp_trash_comment($comment_id) {
    1844     if ( !EMPTY_TRASH_DAYS )
    1845         return wp_delete_comment($comment_id, true);
    1846 
    1847     if ( !$comment = get_comment($comment_id) )
    1848         return false;
    1849 
    1850     /**
    1851      * Fires immediately before a comment is sent to the Trash.
    1852      *
    1853      * @since 2.9.0
    1854      *
    1855      * @param int $comment_id The comment ID.
    1856      */
    1857     do_action( 'trash_comment', $comment_id );
    1858 
    1859     if ( wp_set_comment_status($comment_id, 'trash') ) {
    1860         delete_comment_meta( $comment_id, '_wp_trash_meta_status' );
    1861         delete_comment_meta( $comment_id, '_wp_trash_meta_time' );
    1862         add_comment_meta( $comment_id, '_wp_trash_meta_status', $comment->comment_approved );
    1863         add_comment_meta( $comment_id, '_wp_trash_meta_time', time() );
    1864 
    1865         /**
    1866          * Fires immediately after a comment is sent to Trash.
    1867          *
    1868          * @since 2.9.0
    1869          *
    1870          * @param int $comment_id The comment ID.
    1871          */
    1872         do_action( 'trashed_comment', $comment_id );
    1873         return true;
    1874     }
    1875 
    1876     return false;
    1877 }
    1878 
    1879 /**
    1880  * Removes a comment from the Trash
    1881  *
    1882  * @since 2.9.0
    1883  *
    1884  * @param int $comment_id Comment ID.
    1885  * @return bool True on success, false on failure.
    1886  */
    1887 function wp_untrash_comment($comment_id) {
    1888     if ( ! (int)$comment_id )
    1889         return false;
    1890 
    1891     /**
    1892      * Fires immediately before a comment is restored from the Trash.
    1893      *
    1894      * @since 2.9.0
    1895      *
    1896      * @param int $comment_id The comment ID.
    1897      */
    1898     do_action( 'untrash_comment', $comment_id );
    1899 
    1900     $status = (string) get_comment_meta($comment_id, '_wp_trash_meta_status', true);
    1901     if ( empty($status) )
    1902         $status = '0';
    1903 
    1904     if ( wp_set_comment_status($comment_id, $status) ) {
    1905         delete_comment_meta($comment_id, '_wp_trash_meta_time');
    1906         delete_comment_meta($comment_id, '_wp_trash_meta_status');
    1907         /**
    1908          * Fires immediately after a comment is restored from the Trash.
    1909          *
    1910          * @since 2.9.0
    1911          *
    1912          * @param int $comment_id The comment ID.
    1913          */
    1914         do_action( 'untrashed_comment', $comment_id );
    1915         return true;
    1916     }
    1917 
    1918     return false;
    1919 }
    1920 
    1921 /**
    1922  * Marks a comment as Spam
    1923  *
    1924  * @since 2.9.0
    1925  *
    1926  * @param int $comment_id Comment ID.
    1927  * @return bool True on success, false on failure.
    1928  */
    1929 function wp_spam_comment($comment_id) {
    1930     if ( !$comment = get_comment($comment_id) )
    1931         return false;
    1932 
    1933     /**
    1934      * Fires immediately before a comment is marked as Spam.
    1935      *
    1936      * @since 2.9.0
    1937      *
    1938      * @param int $comment_id The comment ID.
    1939      */
    1940     do_action( 'spam_comment', $comment_id );
    1941 
    1942     if ( wp_set_comment_status($comment_id, 'spam') ) {
    1943         delete_comment_meta( $comment_id, '_wp_trash_meta_status' );
    1944         delete_comment_meta( $comment_id, '_wp_trash_meta_time' );
    1945         add_comment_meta( $comment_id, '_wp_trash_meta_status', $comment->comment_approved );
    1946         add_comment_meta( $comment_id, '_wp_trash_meta_time', time() );
    1947         /**
    1948          * Fires immediately after a comment is marked as Spam.
    1949          *
    1950          * @since 2.9.0
    1951          *
    1952          * @param int $comment_id The comment ID.
    1953          */
    1954         do_action( 'spammed_comment', $comment_id );
    1955         return true;
    1956     }
    1957 
    1958     return false;
    1959 }
    1960 
    1961 /**
    1962  * Removes a comment from the Spam
    1963  *
    1964  * @since 2.9.0
    1965  *
    1966  * @param int $comment_id Comment ID.
    1967  * @return bool True on success, false on failure.
    1968  */
    1969 function wp_unspam_comment($comment_id) {
    1970     if ( ! (int)$comment_id )
    1971         return false;
    1972 
    1973     /**
    1974      * Fires immediately before a comment is unmarked as Spam.
    1975      *
    1976      * @since 2.9.0
    1977      *
    1978      * @param int $comment_id The comment ID.
    1979      */
    1980     do_action( 'unspam_comment', $comment_id );
    1981 
    1982     $status = (string) get_comment_meta($comment_id, '_wp_trash_meta_status', true);
    1983     if ( empty($status) )
    1984         $status = '0';
    1985 
    1986     if ( wp_set_comment_status($comment_id, $status) ) {
    1987         delete_comment_meta( $comment_id, '_wp_trash_meta_status' );
    1988         delete_comment_meta( $comment_id, '_wp_trash_meta_time' );
    1989         /**
    1990          * Fires immediately after a comment is unmarked as Spam.
    1991          *
    1992          * @since 2.9.0
    1993          *
    1994          * @param int $comment_id The comment ID.
    1995          */
    1996         do_action( 'unspammed_comment', $comment_id );
    1997         return true;
    1998     }
    1999 
    2000     return false;
    2001 }
    2002 
    2003 /**
    2004  * The status of a comment by ID.
    2005  *
    2006  * @since 1.0.0
    2007  *
    2008  * @param int $comment_id Comment ID
    2009  * @return false|string Status might be 'trash', 'approved', 'unapproved', 'spam'. False on failure.
    2010  */
    2011 function wp_get_comment_status($comment_id) {
    2012     $comment = get_comment($comment_id);
    2013     if ( !$comment )
    2014         return false;
    2015 
    2016     $approved = $comment->comment_approved;
    2017 
    2018     if ( $approved == null )
    2019         return false;
    2020     elseif ( $approved == '1' )
    2021         return 'approved';
    2022     elseif ( $approved == '0' )
    2023         return 'unapproved';
    2024     elseif ( $approved == 'spam' )
    2025         return 'spam';
    2026     elseif ( $approved == 'trash' )
    2027         return 'trash';
    2028     else
    2029         return false;
    2030 }
    2031 
    2032 /**
    2033  * Call hooks for when a comment status transition occurs.
    2034  *
    2035  * Calls hooks for comment status transitions. If the new comment status is not the same
    2036  * as the previous comment status, then two hooks will be ran, the first is
    2037  * 'transition_comment_status' with new status, old status, and comment data. The
    2038  * next action called is 'comment_OLDSTATUS_to_NEWSTATUS' the NEWSTATUS is the
    2039  * $new_status parameter and the OLDSTATUS is $old_status parameter; it has the
    2040  * comment data.
    2041  *
    2042  * The final action will run whether or not the comment statuses are the same. The
    2043  * action is named 'comment_NEWSTATUS_COMMENTTYPE', NEWSTATUS is from the $new_status
    2044  * parameter and COMMENTTYPE is comment_type comment data.
    2045  *
    2046  * @since 2.7.0
    2047  *
    2048  * @param string $new_status New comment status.
    2049  * @param string $old_status Previous comment status.
    2050  * @param object $comment Comment data.
    2051  */
    2052 function wp_transition_comment_status($new_status, $old_status, $comment) {
    2053     /*
    2054      * Translate raw statuses to human readable formats for the hooks.
    2055      * This is not a complete list of comment status, it's only the ones
    2056      * that need to be renamed
    2057      */
    2058     $comment_statuses = array(
    2059         0         => 'unapproved',
    2060         'hold'    => 'unapproved', // wp_set_comment_status() uses "hold"
    2061         1         => 'approved',
    2062         'approve' => 'approved', // wp_set_comment_status() uses "approve"
    2063     );
    2064     if ( isset($comment_statuses[$new_status]) ) $new_status = $comment_statuses[$new_status];
    2065     if ( isset($comment_statuses[$old_status]) ) $old_status = $comment_statuses[$old_status];
    2066 
    2067     // Call the hooks
    2068     if ( $new_status != $old_status ) {
    2069         /**
    2070          * Fires when the comment status is in transition.
    2071          *
    2072          * @since 2.7.0
    2073          *
    2074          * @param int|string $new_status The new comment status.
    2075          * @param int|string $old_status The old comment status.
    2076          * @param object     $comment    The comment data.
    2077          */
    2078         do_action( 'transition_comment_status', $new_status, $old_status, $comment );
    2079         /**
    2080          * Fires when the comment status is in transition from one specific status to another.
    2081          *
    2082          * The dynamic portions of the hook name, `$old_status`, and `$new_status`,
    2083          * refer to the old and new comment statuses, respectively.
    2084          *
    2085          * @since 2.7.0
    2086          *
    2087          * @param object $comment Comment object.
    2088          */
    2089         do_action( "comment_{$old_status}_to_{$new_status}", $comment );
    2090     }
    2091     /**
    2092      * Fires when the status of a specific comment type is in transition.
    2093      *
    2094      * The dynamic portions of the hook name, `$new_status`, and `$comment->comment_type`,
    2095      * refer to the new comment status, and the type of comment, respectively.
    2096      *
    2097      * Typical comment types include an empty string (standard comment), 'pingback',
    2098      * or 'trackback'.
    2099      *
    2100      * @since 2.7.0
    2101      *
    2102      * @param int $comment_ID The comment ID.
    2103      * @param obj $comment    Comment object.
    2104      */
    2105     do_action( "comment_{$new_status}_{$comment->comment_type}", $comment->comment_ID, $comment );
    2106 }
    2107 
    2108 /**
    2109  * Get current commenter's name, email, and URL.
    2110  *
    2111  * Expects cookies content to already be sanitized. User of this function might
    2112  * wish to recheck the returned array for validity.
    2113  *
    2114  * @see sanitize_comment_cookies() Use to sanitize cookies
    2115  *
    2116  * @since 2.0.4
    2117  *
    2118  * @return array Comment author, email, url respectively.
    2119  */
    2120 function wp_get_current_commenter() {
    2121     // Cookies should already be sanitized.
    2122 
    2123     $comment_author = '';
    2124     if ( isset($_COOKIE['comment_author_'.COOKIEHASH]) )
    2125         $comment_author = $_COOKIE['comment_author_'.COOKIEHASH];
    2126 
    2127     $comment_author_email = '';
    2128     if ( isset($_COOKIE['comment_author_email_'.COOKIEHASH]) )
    2129         $comment_author_email = $_COOKIE['comment_author_email_'.COOKIEHASH];
    2130 
    2131     $comment_author_url = '';
    2132     if ( isset($_COOKIE['comment_author_url_'.COOKIEHASH]) )
    2133         $comment_author_url = $_COOKIE['comment_author_url_'.COOKIEHASH];
    2134 
    2135     /**
    2136      * Filter the current commenter's name, email, and URL.
    2137      *
    2138      * @since 3.1.0
    2139      *
    2140      * @param string $comment_author       Comment author's name.
    2141      * @param string $comment_author_email Comment author's email.
    2142      * @param string $comment_author_url   Comment author's URL.
    2143      */
    2144     return apply_filters( 'wp_get_current_commenter', compact('comment_author', 'comment_author_email', 'comment_author_url') );
    2145 }
    2146 
    2147 /**
    2148  * Inserts a comment into the database.
    2149  *
    2150  * @since 2.0.0
    2151  *
    2152  * @global wpdb $wpdb WordPress database abstraction object.
    2153  *
    2154  * @param array $commentdata {
    2155  *     Array of arguments for inserting a new comment.
    2156  *
    2157  *     @type string     $comment_agent        The HTTP user agent of the `$comment_author` when
    2158  *                                            the comment was submitted. Default empty.
    2159  *     @type int|string $comment_approved     Whether the comment has been approved. Default 1.
    2160  *     @type string     $comment_author       The name of the author of the comment. Default empty.
    2161  *     @type string     $comment_author_email The email address of the `$comment_author`. Default empty.
    2162  *     @type string     $comment_author_IP    The IP address of the `$comment_author`. Default empty.
    2163  *     @type string     $comment_author_url   The URL address of the `$comment_author`. Default empty.
    2164  *     @type string     $comment_content      The content of the comment. Default empty.
    2165  *     @type string     $comment_date         The date the comment was submitted. To set the date
    2166  *                                            manually, `$comment_date_gmt` must also be specified.
    2167  *                                            Default is the current time.
    2168  *     @type string     $comment_date_gmt     The date the comment was submitted in the GMT timezone.
    2169  *                                            Default is `$comment_date` in the site's GMT timezone.
    2170  *     @type int        $comment_karma        The karma of the comment. Default 0.
    2171  *     @type int        $comment_parent       ID of this comment's parent, if any. Default 0.
    2172  *     @type int        $comment_post_ID      ID of the post that relates to the comment, if any.
    2173  *                                            Default empty.
    2174  *     @type string     $comment_type         Comment type. Default empty.
    2175  *     @type int        $user_id              ID of the user who submitted the comment. Default 0.
    2176  * }
    2177  * @return int|false The new comment's ID on success, false on failure.
    2178  */
    2179 function wp_insert_comment( $commentdata ) {
    2180     global $wpdb;
    2181     $data = wp_unslash( $commentdata );
    2182 
    2183     $comment_author       = ! isset( $data['comment_author'] )       ? '' : $data['comment_author'];
    2184     $comment_author_email = ! isset( $data['comment_author_email'] ) ? '' : $data['comment_author_email'];
    2185     $comment_author_url   = ! isset( $data['comment_author_url'] )   ? '' : $data['comment_author_url'];
    2186     $comment_author_IP    = ! isset( $data['comment_author_IP'] )    ? '' : $data['comment_author_IP'];
    2187 
    2188     $comment_date     = ! isset( $data['comment_date'] )     ? current_time( 'mysql' )            : $data['comment_date'];
    2189     $comment_date_gmt = ! isset( $data['comment_date_gmt'] ) ? get_gmt_from_date( $comment_date ) : $data['comment_date_gmt'];
    2190 
    2191     $comment_post_ID  = ! isset( $data['comment_post_ID'] )  ? '' : $data['comment_post_ID'];
    2192     $comment_content  = ! isset( $data['comment_content'] )  ? '' : $data['comment_content'];
    2193     $comment_karma    = ! isset( $data['comment_karma'] )    ? 0  : $data['comment_karma'];
    2194     $comment_approved = ! isset( $data['comment_approved'] ) ? 1  : $data['comment_approved'];
    2195     $comment_agent    = ! isset( $data['comment_agent'] )    ? '' : $data['comment_agent'];
    2196     $comment_type     = ! isset( $data['comment_type'] )     ? '' : $data['comment_type'];
    2197     $comment_parent   = ! isset( $data['comment_parent'] )   ? 0  : $data['comment_parent'];
    2198 
    2199     $user_id  = ! isset( $data['user_id'] ) ? 0 : $data['user_id'];
    2200 
    2201     $compacted = compact( 'comment_post_ID', 'comment_author', 'comment_author_email', 'comment_author_url', 'comment_author_IP', 'comment_date', 'comment_date_gmt', 'comment_content', 'comment_karma', 'comment_approved', 'comment_agent', 'comment_type', 'comment_parent', 'user_id' );
    2202     if ( ! $wpdb->insert( $wpdb->comments, $compacted ) ) {
    2203         return false;
    2204     }
    2205 
    2206     $id = (int) $wpdb->insert_id;
    2207 
    2208     if ( $comment_approved == 1 ) {
    2209         wp_update_comment_count( $comment_post_ID );
    2210     }
    2211     $comment = get_comment( $id );
    2212 
    2213     /**
    2214      * Fires immediately after a comment is inserted into the database.
    2215      *
    2216      * @since 2.8.0
    2217      *
    2218      * @param int $id      The comment ID.
    2219      * @param obj $comment Comment object.
    2220      */
    2221     do_action( 'wp_insert_comment', $id, $comment );
    2222 
    2223     wp_cache_set( 'last_changed', microtime(), 'comment' );
    2224 
    2225     return $id;
    2226 }
    2227 
    2228 /**
    2229  * Filters and sanitizes comment data.
    2230  *
    2231  * Sets the comment data 'filtered' field to true when finished. This can be
    2232  * checked as to whether the comment should be filtered and to keep from
    2233  * filtering the same comment more than once.
    2234  *
    2235  * @since 2.0.0
    2236  *
    2237  * @param array $commentdata Contains information on the comment.
    2238  * @return array Parsed comment information.
    2239  */
    2240 function wp_filter_comment($commentdata) {
    2241     if ( isset( $commentdata['user_ID'] ) ) {
    2242         /**
    2243          * Filter the comment author's user id before it is set.
    2244          *
    2245          * The first time this filter is evaluated, 'user_ID' is checked
    2246          * (for back-compat), followed by the standard 'user_id' value.
    2247          *
    2248          * @since 1.5.0
    2249          *
    2250          * @param int $user_ID The comment author's user ID.
    2251          */
    2252         $commentdata['user_id'] = apply_filters( 'pre_user_id', $commentdata['user_ID'] );
    2253     } elseif ( isset( $commentdata['user_id'] ) ) {
    2254         /** This filter is documented in wp-includes/comment.php */
    2255         $commentdata['user_id'] = apply_filters( 'pre_user_id', $commentdata['user_id'] );
    2256     }
    2257 
    2258     /**
    2259      * Filter the comment author's browser user agent before it is set.
    2260      *
    2261      * @since 1.5.0
    2262      *
    2263      * @param int $comment_agent The comment author's browser user agent.
    2264      */
    2265     $commentdata['comment_agent'] = apply_filters( 'pre_comment_user_agent', ( isset( $commentdata['comment_agent'] ) ? $commentdata['comment_agent'] : '' ) );
    2266     /** This filter is documented in wp-includes/comment.php */
    2267     $commentdata['comment_author'] = apply_filters( 'pre_comment_author_name', $commentdata['comment_author'] );
    2268     /**
    2269      * Filter the comment content before it is set.
    2270      *
    2271      * @since 1.5.0
    2272      *
    2273      * @param int $comment_content The comment content.
    2274      */
    2275     $commentdata['comment_content'] = apply_filters( 'pre_comment_content', $commentdata['comment_content'] );
    2276     /**
    2277      * Filter the comment author's IP before it is set.
    2278      *
    2279      * @since 1.5.0
    2280      *
    2281      * @param int $comment_author_ip The comment author's IP.
    2282      */
    2283     $commentdata['comment_author_IP'] = apply_filters( 'pre_comment_user_ip', $commentdata['comment_author_IP'] );
    2284     /** This filter is documented in wp-includes/comment.php */
    2285     $commentdata['comment_author_url'] = apply_filters( 'pre_comment_author_url', $commentdata['comment_author_url'] );
    2286     /** This filter is documented in wp-includes/comment.php */
    2287     $commentdata['comment_author_email'] = apply_filters( 'pre_comment_author_email', $commentdata['comment_author_email'] );
    2288     $commentdata['filtered'] = true;
    2289     return $commentdata;
    2290 }
    2291 
    2292 /**
    2293  * Whether a comment should be blocked because of comment flood.
    2294  *
    2295  * @since 2.1.0
    2296  *
    2297  * @param bool $block Whether plugin has already blocked comment.
    2298  * @param int $time_lastcomment Timestamp for last comment.
    2299  * @param int $time_newcomment Timestamp for new comment.
    2300  * @return bool Whether comment should be blocked.
    2301  */
    2302 function wp_throttle_comment_flood($block, $time_lastcomment, $time_newcomment) {
    2303     if ( $block ) // a plugin has already blocked... we'll let that decision stand
    2304         return $block;
    2305     if ( ($time_newcomment - $time_lastcomment) < 15 )
    2306         return true;
    2307     return false;
    2308 }
    2309 
    2310 /**
    2311  * Adds a new comment to the database.
    2312  *
    2313  * Filters new comment to ensure that the fields are sanitized and valid before
    2314  * inserting comment into database. Calls 'comment_post' action with comment ID
    2315  * and whether comment is approved by WordPress. Also has 'preprocess_comment'
    2316  * filter for processing the comment data before the function handles it.
    2317  *
    2318  * We use REMOTE_ADDR here directly. If you are behind a proxy, you should ensure
    2319  * that it is properly set, such as in wp-config.php, for your environment.
    2320  * See {@link https://core.trac.wordpress.org/ticket/9235}
    2321  *
    2322  * @since 1.5.0
    2323  * @since 4.3.0 'comment_agent' and 'comment_author_IP' can be set via `$commentdata`.
    2324  *
    2325  * @see wp_insert_comment()
    2326  *
    2327  * @global wpdb $wpdb
    2328  *
    2329  * @param array $commentdata {
    2330  *     Comment data.
    2331  *
    2332  *     @type string $comment_author       The name of the comment author.
    2333  *     @type string $comment_author_email The comment author email address.
    2334  *     @type string $comment_author_url   The comment author URL.
    2335  *     @type string $comment_content      The content of the comment.
    2336  *     @type string $comment_date         The date the comment was submitted. Default is the current time.
    2337  *     @type string $comment_date_gmt     The date the comment was submitted in the GMT timezone.
    2338  *                                        Default is `$comment_date` in the GMT timezone.
    2339  *     @type int    $comment_parent       The ID of this comment's parent, if any. Default 0.
    2340  *     @type int    $comment_post_ID      The ID of the post that relates to the comment.
    2341  *     @type int    $user_id              The ID of the user who submitted the comment. Default 0.
    2342  *     @type int    $user_ID              Kept for backward-compatibility. Use `$user_id` instead.
    2343  *     @type string $comment_agent        Comment author user agent. Default is the value of 'HTTP_USER_AGENT'
    2344  *                                        in the `$_SERVER` superglobal sent in the original request.
    2345  *     @type string $comment_author_IP    Comment author IP address in IPv4 format. Default is the value of
    2346  *                                        'REMOTE_ADDR' in the `$_SERVER` superglobal sent in the original request.
    2347  * }
    2348  * @return int|false The ID of the comment on success, false on failure.
    2349  */
    2350 function wp_new_comment( $commentdata ) {
    2351     global $wpdb;
    2352 
    2353     if ( isset( $commentdata['user_ID'] ) ) {
    2354         $commentdata['user_id'] = $commentdata['user_ID'] = (int) $commentdata['user_ID'];
    2355     }
    2356 
    2357     $prefiltered_user_id = ( isset( $commentdata['user_id'] ) ) ? (int) $commentdata['user_id'] : 0;
    2358 
    2359     /**
    2360      * Filter a comment's data before it is sanitized and inserted into the database.
    2361      *
    2362      * @since 1.5.0
    2363      *
    2364      * @param array $commentdata Comment data.
    2365      */
    2366     $commentdata = apply_filters( 'preprocess_comment', $commentdata );
    2367 
    2368     $commentdata['comment_post_ID'] = (int) $commentdata['comment_post_ID'];
    2369     if ( isset( $commentdata['user_ID'] ) && $prefiltered_user_id !== (int) $commentdata['user_ID'] ) {
    2370         $commentdata['user_id'] = $commentdata['user_ID'] = (int) $commentdata['user_ID'];
    2371     } elseif ( isset( $commentdata['user_id'] ) ) {
    2372         $commentdata['user_id'] = (int) $commentdata['user_id'];
    2373     }
    2374 
    2375     $commentdata['comment_parent'] = isset($commentdata['comment_parent']) ? absint($commentdata['comment_parent']) : 0;
    2376     $parent_status = ( 0 < $commentdata['comment_parent'] ) ? wp_get_comment_status($commentdata['comment_parent']) : '';
    2377     $commentdata['comment_parent'] = ( 'approved' == $parent_status || 'unapproved' == $parent_status ) ? $commentdata['comment_parent'] : 0;
    2378 
    2379     if ( ! isset( $commentdata['comment_author_IP'] ) ) {
    2380         $commentdata['comment_author_IP'] = $_SERVER['REMOTE_ADDR'];
    2381     }
    2382     $commentdata['comment_author_IP'] = preg_replace( '/[^0-9a-fA-F:., ]/', '', $commentdata['comment_author_IP'] );
    2383 
    2384     if ( ! isset( $commentdata['comment_agent'] ) ) {
    2385         $commentdata['comment_agent'] = isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT']: '';
    2386     }
    2387     $commentdata['comment_agent'] = substr( $commentdata['comment_agent'], 0, 254 );
    2388 
    2389     if ( empty( $commentdata['comment_date'] ) ) {
    2390         $commentdata['comment_date'] = current_time('mysql');
    2391     }
    2392 
    2393     if ( empty( $commentdata['comment_date_gmt'] ) ) {
    2394         $commentdata['comment_date_gmt'] = current_time( 'mysql', 1 );
    2395     }
    2396 
    2397     $commentdata = wp_filter_comment($commentdata);
    2398 
    2399     $commentdata['comment_approved'] = wp_allow_comment($commentdata);
    2400 
    2401     $comment_ID = wp_insert_comment($commentdata);
    2402     if ( ! $comment_ID ) {
    2403         $fields = array( 'comment_author', 'comment_author_email', 'comment_author_url', 'comment_content' );
    2404 
    2405         foreach ( $fields as $field ) {
    2406             if ( isset( $commentdata[ $field ] ) ) {
    2407                 $commentdata[ $field ] = $wpdb->strip_invalid_text_for_column( $wpdb->comments, $field, $commentdata[ $field ] );
    2408             }
    2409         }
    2410 
    2411         $commentdata = wp_filter_comment( $commentdata );
    2412 
    2413         $commentdata['comment_approved'] = wp_allow_comment( $commentdata );
    2414 
    2415         $comment_ID = wp_insert_comment( $commentdata );
    2416         if ( ! $comment_ID ) {
    2417             return false;
    2418         }
    2419     }
    2420 
    2421     /**
    2422      * Fires immediately after a comment is inserted into the database.
    2423      *
    2424      * @since 1.2.0
    2425      *
    2426      * @param int $comment_ID       The comment ID.
    2427      * @param int $comment_approved 1 (true) if the comment is approved, 0 (false) if not.
    2428      */
    2429     do_action( 'comment_post', $comment_ID, $commentdata['comment_approved'] );
    2430 
    2431     if ( 'spam' !== $commentdata['comment_approved'] ) { // If it's spam save it silently for later crunching
    2432         if ( '0' == $commentdata['comment_approved'] ) {
    2433             wp_notify_moderator( $comment_ID );
    2434         }
    2435 
    2436         // wp_notify_postauthor() checks if notifying the author of their own comment.
    2437         // By default, it won't, but filters can override this.
    2438         if ( get_option( 'comments_notify' ) && $commentdata['comment_approved'] ) {
    2439             wp_notify_postauthor( $comment_ID );
    2440         }
    2441     }
    2442 
    2443     return $comment_ID;
    2444 }
    2445 
    2446 /**
    2447  * Sets the status of a comment.
    2448  *
    2449  * The 'wp_set_comment_status' action is called after the comment is handled.
    2450  * If the comment status is not in the list, then false is returned.
    2451  *
    2452  * @since 1.0.0
    2453  *
    2454  * global wpdb $wpdb
    2455  *
    2456  * @param int $comment_id Comment ID.
    2457  * @param string $comment_status New comment status, either 'hold', 'approve', 'spam', or 'trash'.
    2458  * @param bool $wp_error Whether to return a WP_Error object if there is a failure. Default is false.
    2459  * @return bool|WP_Error True on success, false or WP_Error on failure.
    2460  */
    2461 function wp_set_comment_status($comment_id, $comment_status, $wp_error = false) {
    2462     global $wpdb;
    2463 
    2464     switch ( $comment_status ) {
    2465         case 'hold':
    2466         case '0':
    2467             $status = '0';
    2468             break;
    2469         case 'approve':
    2470         case '1':
    2471             $status = '1';
    2472             if ( get_option('comments_notify') ) {
    2473                 wp_notify_postauthor( $comment_id );
    2474             }
    2475             break;
    2476         case 'spam':
    2477             $status = 'spam';
    2478             break;
    2479         case 'trash':
    2480             $status = 'trash';
    2481             break;
    2482         default:
    2483             return false;
    2484     }
    2485 
    2486     $comment_old = clone get_comment($comment_id);
    2487 
    2488     if ( !$wpdb->update( $wpdb->comments, array('comment_approved' => $status), array('comment_ID' => $comment_id) ) ) {
    2489         if ( $wp_error )
    2490             return new WP_Error('db_update_error', __('Could not update comment status'), $wpdb->last_error);
    2491         else
    2492             return false;
    2493     }
    2494 
    2495     clean_comment_cache($comment_id);
    2496 
    2497     $comment = get_comment($comment_id);
    2498 
    2499     /**
    2500      * Fires immediately before transitioning a comment's status from one to another
    2501      * in the database.
    2502      *
    2503      * @since 1.5.0
    2504      *
    2505      * @param int         $comment_id     Comment ID.
    2506      * @param string|bool $comment_status Current comment status. Possible values include
    2507      *                                    'hold', 'approve', 'spam', 'trash', or false.
    2508      */
    2509     do_action( 'wp_set_comment_status', $comment_id, $comment_status );
    2510 
    2511     wp_transition_comment_status($comment_status, $comment_old->comment_approved, $comment);
    2512 
    2513     wp_update_comment_count($comment->comment_post_ID);
    2514 
    2515     return true;
    2516 }
    2517 
    2518 /**
    2519  * Updates an existing comment in the database.
    2520  *
    2521  * Filters the comment and makes sure certain fields are valid before updating.
    2522  *
    2523  * @since 2.0.0
    2524  *
    2525  * @global wpdb $wpdb WordPress database abstraction object.
    2526  *
    2527  * @param array $commentarr Contains information on the comment.
    2528  * @return int Comment was updated if value is 1, or was not updated if value is 0.
    2529  */
    2530 function wp_update_comment($commentarr) {
    2531     global $wpdb;
    2532 
    2533     // First, get all of the original fields
    2534     $comment = get_comment($commentarr['comment_ID'], ARRAY_A);
    2535     if ( empty( $comment ) ) {
    2536         return 0;
    2537     }
    2538 
    2539     // Make sure that the comment post ID is valid (if specified).
    2540     if ( isset( $commentarr['comment_post_ID'] ) && ! get_post( $commentarr['comment_post_ID'] ) ) {
    2541         return 0;
    2542     }
    2543 
    2544     // Escape data pulled from DB.
    2545     $comment = wp_slash($comment);
    2546 
    2547     $old_status = $comment['comment_approved'];
    2548 
    2549     // Merge old and new fields with new fields overwriting old ones.
    2550     $commentarr = array_merge($comment, $commentarr);
    2551 
    2552     $commentarr = wp_filter_comment( $commentarr );
    2553 
    2554     // Now extract the merged array.
    2555     $data = wp_unslash( $commentarr );
    2556 
    2557     /**
    2558      * Filter the comment content before it is updated in the database.
    2559      *
    2560      * @since 1.5.0
    2561      *
    2562      * @param string $comment_content The comment data.
    2563      */
    2564     $data['comment_content'] = apply_filters( 'comment_save_pre', $data['comment_content'] );