$comment_date_gmt = ! isset( $data['comment_date_gmt'] ) ? get_gmt_from_date( $comment_date ) : $data['comment_date_gmt'];
$comment_post_ID = ! isset( $data['comment_post_ID'] ) ? 0 : $data['comment_post_ID'];
$comment_content = ! isset( $data['comment_content'] ) ? '' : $data['comment_content'];
$comment_karma = ! isset( $data['comment_karma'] ) ? 0 : $data['comment_karma'];
$comment_approved = ! isset( $data['comment_approved'] ) ? 1 : $data['comment_approved'];
$comment_agent = ! isset( $data['comment_agent'] ) ? '' : $data['comment_agent'];
$comment_type = empty( $data['comment_type'] ) ? 'comment' : $data['comment_type'];
$comment_parent = ! isset( $data['comment_parent'] ) ? 0 : $data['comment_parent'];
$user_id = ! isset( $data['user_id'] ) ? 0 : $data['user_id'];
$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' );
if ( ! $wpdb->insert( $wpdb->comments, $compacted ) ) {
$id = (int) $wpdb->insert_id;
if ( 1 == $comment_approved ) {
wp_update_comment_count( $comment_post_ID );
foreach ( array( 'server', 'gmt', 'blog' ) as $timezone ) {
wp_cache_delete( "lastcommentmodified:$timezone", 'timeinfo' );
clean_comment_cache( $id );
$comment = get_comment( $id );
// If metadata is provided, store it.
if ( isset( $commentdata['comment_meta'] ) && is_array( $commentdata['comment_meta'] ) ) {
foreach ( $commentdata['comment_meta'] as $meta_key => $meta_value ) {
add_comment_meta( $comment->comment_ID, $meta_key, $meta_value, true );
* Fires immediately after a comment is inserted into the database.
* @param int $id The comment ID.
* @param WP_Comment $comment Comment object.
do_action( 'wp_insert_comment', $id, $comment );
* Filters and sanitizes comment data.
* Sets the comment data 'filtered' field to true when finished. This can be
* checked as to whether the comment should be filtered and to keep from
* filtering the same comment more than once.
* @param array $commentdata Contains information on the comment.
* @return array Parsed comment information.
function wp_filter_comment( $commentdata ) {
if ( isset( $commentdata['user_ID'] ) ) {
* Filters the comment author's user ID before it is set.
* The first time this filter is evaluated, 'user_ID' is checked
* (for back-compat), followed by the standard 'user_id' value.
* @param int $user_ID The comment author's user ID.
$commentdata['user_id'] = apply_filters( 'pre_user_id', $commentdata['user_ID'] );
} elseif ( isset( $commentdata['user_id'] ) ) {
/** This filter is documented in wp-includes/comment.php */
$commentdata['user_id'] = apply_filters( 'pre_user_id', $commentdata['user_id'] );
* Filters the comment author's browser user agent before it is set.
* @param string $comment_agent The comment author's browser user agent.
$commentdata['comment_agent'] = apply_filters( 'pre_comment_user_agent', ( isset( $commentdata['comment_agent'] ) ? $commentdata['comment_agent'] : '' ) );
/** This filter is documented in wp-includes/comment.php */
$commentdata['comment_author'] = apply_filters( 'pre_comment_author_name', $commentdata['comment_author'] );
* Filters the comment content before it is set.
* @param string $comment_content The comment content.
$commentdata['comment_content'] = apply_filters( 'pre_comment_content', $commentdata['comment_content'] );
* Filters the comment author's IP address before it is set.
* @param string $comment_author_ip The comment author's IP address.
$commentdata['comment_author_IP'] = apply_filters( 'pre_comment_user_ip', $commentdata['comment_author_IP'] );
/** This filter is documented in wp-includes/comment.php */
$commentdata['comment_author_url'] = apply_filters( 'pre_comment_author_url', $commentdata['comment_author_url'] );
/** This filter is documented in wp-includes/comment.php */
$commentdata['comment_author_email'] = apply_filters( 'pre_comment_author_email', $commentdata['comment_author_email'] );
$commentdata['filtered'] = true;
* Whether a comment should be blocked because of comment flood.
* @param bool $block Whether plugin has already blocked comment.
* @param int $time_lastcomment Timestamp for last comment.
* @param int $time_newcomment Timestamp for new comment.
* @return bool Whether comment should be blocked.
function wp_throttle_comment_flood( $block, $time_lastcomment, $time_newcomment ) {
if ( $block ) { // A plugin has already blocked... we'll let that decision stand.
if ( ( $time_newcomment - $time_lastcomment ) < 15 ) {
* Adds a new comment to the database.
* Filters new comment to ensure that the fields are sanitized and valid before
* inserting comment into database. Calls {@see 'comment_post'} action with comment ID
* and whether comment is approved by WordPress. Also has {@see 'preprocess_comment'}
* filter for processing the comment data before the function handles it.
* We use `REMOTE_ADDR` here directly. If you are behind a proxy, you should ensure
* that it is properly set, such as in wp-config.php, for your environment.
* See {@link https://core.trac.wordpress.org/ticket/9235}
* @since 4.3.0 Introduced the `comment_agent` and `comment_author_IP` arguments.
* @since 4.7.0 The `$avoid_die` parameter was added, allowing the function
* to return a WP_Error object instead of dying.
* @since 5.5.0 The `$avoid_die` parameter was renamed to `$wp_error`.
* @since 5.5.0 Introduced the `comment_type` argument.
* @see wp_insert_comment()
* @global wpdb $wpdb WordPress database abstraction object.
* @param array $commentdata {
* @type string $comment_author The name of the comment author.
* @type string $comment_author_email The comment author email address.
* @type string $comment_author_url The comment author URL.
* @type string $comment_content The content of the comment.
* @type string $comment_date The date the comment was submitted. Default is the current time.
* @type string $comment_date_gmt The date the comment was submitted in the GMT timezone.
* Default is `$comment_date` in the GMT timezone.
* @type string $comment_type Comment type. Default 'comment'.
* @type int $comment_parent The ID of this comment's parent, if any. Default 0.
* @type int $comment_post_ID The ID of the post that relates to the comment.
* @type int $user_id The ID of the user who submitted the comment. Default 0.
* @type int $user_ID Kept for backward-compatibility. Use `$user_id` instead.
* @type string $comment_agent Comment author user agent. Default is the value of 'HTTP_USER_AGENT'
* in the `$_SERVER` superglobal sent in the original request.
* @type string $comment_author_IP Comment author IP address in IPv4 format. Default is the value of
* 'REMOTE_ADDR' in the `$_SERVER` superglobal sent in the original request.
* @param bool $wp_error Should errors be returned as WP_Error objects instead of
* executing wp_die()? Default false.
* @return int|false|WP_Error The ID of the comment on success, false or WP_Error on failure.
function wp_new_comment( $commentdata, $wp_error = false ) {
if ( isset( $commentdata['user_ID'] ) ) {
$commentdata['user_ID'] = (int) $commentdata['user_ID'];
$commentdata['user_id'] = $commentdata['user_ID'];
$prefiltered_user_id = ( isset( $commentdata['user_id'] ) ) ? (int) $commentdata['user_id'] : 0;
if ( ! isset( $commentdata['comment_author_IP'] ) ) {
$commentdata['comment_author_IP'] = $_SERVER['REMOTE_ADDR'];
if ( ! isset( $commentdata['comment_agent'] ) ) {
$commentdata['comment_agent'] = isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : '';
* Filters a comment's data before it is sanitized and inserted into the database.
* @since 5.6.0 Comment data includes the `comment_agent` and `comment_author_IP` values.
* @param array $commentdata Comment data.
$commentdata = apply_filters( 'preprocess_comment', $commentdata );
$commentdata['comment_post_ID'] = (int) $commentdata['comment_post_ID'];
if ( isset( $commentdata['user_ID'] ) && $prefiltered_user_id !== (int) $commentdata['user_ID'] ) {
$commentdata['user_ID'] = (int) $commentdata['user_ID'];
$commentdata['user_id'] = $commentdata['user_ID'];
} elseif ( isset( $commentdata['user_id'] ) ) {
$commentdata['user_id'] = (int) $commentdata['user_id'];
$commentdata['comment_parent'] = isset( $commentdata['comment_parent'] ) ? absint( $commentdata['comment_parent'] ) : 0;
$parent_status = ( $commentdata['comment_parent'] > 0 ) ? wp_get_comment_status( $commentdata['comment_parent'] ) : '';
$commentdata['comment_parent'] = ( 'approved' === $parent_status || 'unapproved' === $parent_status ) ? $commentdata['comment_parent'] : 0;
$commentdata['comment_author_IP'] = preg_replace( '/[^0-9a-fA-F:., ]/', '', $commentdata['comment_author_IP'] );
$commentdata['comment_agent'] = substr( $commentdata['comment_agent'], 0, 254 );
if ( empty( $commentdata['comment_date'] ) ) {
$commentdata['comment_date'] = current_time( 'mysql' );
if ( empty( $commentdata['comment_date_gmt'] ) ) {
$commentdata['comment_date_gmt'] = current_time( 'mysql', 1 );
if ( empty( $commentdata['comment_type'] ) ) {
$commentdata['comment_type'] = 'comment';
$commentdata = wp_filter_comment( $commentdata );
$commentdata['comment_approved'] = wp_allow_comment( $commentdata, $wp_error );
if ( is_wp_error( $commentdata['comment_approved'] ) ) {
return $commentdata['comment_approved'];
$comment_ID = wp_insert_comment( $commentdata );
$fields = array( 'comment_author', 'comment_author_email', 'comment_author_url', 'comment_content' );
foreach ( $fields as $field ) {
if ( isset( $commentdata[ $field ] ) ) {
$commentdata[ $field ] = $wpdb->strip_invalid_text_for_column( $wpdb->comments, $field, $commentdata[ $field ] );
$commentdata = wp_filter_comment( $commentdata );
$commentdata['comment_approved'] = wp_allow_comment( $commentdata, $wp_error );
if ( is_wp_error( $commentdata['comment_approved'] ) ) {
return $commentdata['comment_approved'];
$comment_ID = wp_insert_comment( $commentdata );
* Fires immediately after a comment is inserted into the database.
* @since 4.5.0 The `$commentdata` parameter was added.
* @param int $comment_ID The comment ID.
* @param int|string $comment_approved 1 if the comment is approved, 0 if not, 'spam' if spam.
* @param array $commentdata Comment data.
do_action( 'comment_post', $comment_ID, $commentdata['comment_approved'], $commentdata );
* Send a comment moderation notification to the comment moderator.
* @param int $comment_ID ID of the comment.
* @return bool True on success, false on failure.
function wp_new_comment_notify_moderator( $comment_ID ) {
$comment = get_comment( $comment_ID );
// Only send notifications for pending comments.
$maybe_notify = ( '0' == $comment->comment_approved );
/** This filter is documented in wp-includes/comment.php */
$maybe_notify = apply_filters( 'notify_moderator', $maybe_notify, $comment_ID );
return wp_notify_moderator( $comment_ID );
* Send a notification of a new comment to the post author.
* Uses the {@see 'notify_post_author'} filter to determine whether the post author
* should be notified when a new comment is added, overriding site setting.
* @param int $comment_ID Comment ID.
* @return bool True on success, false on failure.
function wp_new_comment_notify_postauthor( $comment_ID ) {
$comment = get_comment( $comment_ID );
$maybe_notify = get_option( 'comments_notify' );
* Filters whether to send the post author new comment notification emails,
* overriding the site setting.
* @param bool $maybe_notify Whether to notify the post author about the new comment.
* @param int $comment_ID The ID of the comment for the notification.
$maybe_notify = apply_filters( 'notify_post_author', $maybe_notify, $comment_ID );
* wp_notify_postauthor() checks if notifying the author of their own comment.
* By default, it won't, but filters can override this.
// Only send notifications for approved comments.
if ( ! isset( $comment->comment_approved ) || '1' != $comment->comment_approved ) {
return wp_notify_postauthor( $comment_ID );
* Sets the status of a comment.
* The {@see 'wp_set_comment_status'} action is called after the comment is handled.
* If the comment status is not in the list, then false is returned.
* @global wpdb $wpdb WordPress database abstraction object.
* @param int|WP_Comment $comment_id Comment ID or WP_Comment object.
* @param string $comment_status New comment status, either 'hold', 'approve', 'spam', or 'trash'.
* @param bool $wp_error Whether to return a WP_Error object if there is a failure. Default false.
* @return bool|WP_Error True on success, false or WP_Error on failure.
function wp_set_comment_status( $comment_id, $comment_status, $wp_error = false ) {
switch ( $comment_status ) {
add_action( 'wp_set_comment_status', 'wp_new_comment_notify_postauthor' );
$comment_old = clone get_comment( $comment_id );
if ( ! $wpdb->update( $wpdb->comments, array( 'comment_approved' => $status ), array( 'comment_ID' => $comment_old->comment_ID ) ) ) {
return new WP_Error( 'db_update_error', __( 'Could not update comment status.' ), $wpdb->last_error );
clean_comment_cache( $comment_old->comment_ID );
$comment = get_comment( $comment_old->comment_ID );
* Fires immediately after transitioning a comment's status from one to another in the database
* and removing the comment from the object cache, but prior to all status transition hooks.
* @param int $comment_id Comment ID.
* @param string $comment_status Current comment status. Possible values include
* 'hold', '0', 'approve', '1', 'spam', and 'trash'.
do_action( 'wp_set_comment_status', $comment->comment_ID, $comment_status );
wp_transition_comment_status( $comment_status, $comment_old->comment_approved, $comment );
wp_update_comment_count( $comment->comment_post_ID );
* Updates an existing comment in the database.
* Filters the comment and makes sure certain fields are valid before updating.
* @since 4.9.0 Add updating comment meta during comment update.
* @since 5.5.0 The `$wp_error` parameter was added.
* @since 5.5.0 The return values for an invalid comment or post ID
* were changed to false instead of 0.
* @global wpdb $wpdb WordPress database abstraction object.
* @param array $commentarr Contains information on the comment.
* @param bool $wp_error Optional. Whether to return a WP_Error on failure. Default false.
* @return int|false|WP_Error The value 1 if the comment was updated, 0 if not updated.
* False or a WP_Error object on failure.
function wp_update_comment( $commentarr, $wp_error = false ) {
// First, get all of the original fields.
$comment = get_comment( $commentarr['comment_ID'], ARRAY_A );
if ( empty( $comment ) ) {
return new WP_Error( 'invalid_comment_id', __( 'Invalid comment ID.' ) );
// Make sure that the comment post ID is valid (if specified).
if ( ! empty( $commentarr['comment_post_ID'] ) && ! get_post( $commentarr['comment_post_ID'] ) ) {
return new WP_Error( 'invalid_post_id', __( 'Invalid post ID.' ) );
if ( ! has_filter( 'pre_comment_content', 'wp_filter_kses' ) ) {
$filter_comment = ! user_can( isset( $comment['user_id'] ) ? $comment['user_id'] : 0, 'unfiltered_html' );
add_filter( 'pre_comment_content', 'wp_filter_kses' );
// Escape data pulled from DB.
$comment = wp_slash( $comment );
$old_status = $comment['comment_approved'];
// Merge old and new fields with new fields overwriting old ones.
$commentarr = array_merge( $comment, $commentarr );
$commentarr = wp_filter_comment( $commentarr );
remove_filter( 'pre_comment_content', 'wp_filter_kses' );
// Now extract the merged array.
$data = wp_unslash( $commentarr );
* Filters the comment content before it is updated in the database.
* @param string $comment_content The comment data.
$data['comment_content'] = apply_filters( 'comment_save_pre', $data['comment_content'] );
$data['comment_date_gmt'] = get_gmt_from_date( $data['comment_date'] );