剖析 WordPress `wp_insert_comment()` 函数源码:评论插入与相关 `meta` 数据处理。

嘿,各位代码探险家们,今天咱们来深潜一下 WordPress 的 wp_insert_comment() 这个函数,看看它到底是怎么把评论给塞进数据库里,以及它是怎么处理那些跟评论相关的“小秘密”——也就是评论的 meta 数据。

准备好了吗?Let’s dive in!

第一幕:wp_insert_comment() 的结构解剖

首先,咱们得看看 wp_insert_comment() 到底长什么样。这玩意儿可是 WordPress 评论系统的核心引擎之一。

/**
 * Inserts a comment into the database.
 *
 * @since 2.0.0
 *
 * @param array|object $commentarr Comment data. Must pass balanceTags(). Accepts an array or an object.
 * @param bool         $wp_error   Optional. Whether to return a WP_Error object on failure. Default false.
 * @return int|WP_Error The ID of the comment on success. WP_Error object on error if `$wp_error` is true.
 *                      0 on failure if `$wp_error` is false.
 */
function wp_insert_comment( $commentarr = array(), $wp_error = false ) {
  global $wpdb;

  // Sanitize comment values.
  $commentarr = wp_unslash( $commentarr );

  $commentdata = wp_parse_args( $commentarr );

  // Typical usage is as array, but someone might pass in a comment object.
  if ( is_object( $commentdata ) ) {
    $commentdata = get_object_vars( $commentdata );
  }

  // expected_slashed (everything!)
  $commentdata = wp_slash( $commentdata );

  // Are we updating or creating?
  if ( ! empty( $commentdata['comment_ID'] ) ) {
    $comment_id = $commentdata['comment_ID'];
    $update     = true;
  } else {
    $update     = false;
  }

  $comment_author_IP = $_SERVER['REMOTE_ADDR'];
  $comment_date      = current_time( 'mysql' );
  $comment_date_gmt  = current_time( 'mysql', 1 );
  $comment_post_ID   = 0;

  if ( ! empty( $commentdata['comment_post_ID'] ) ) {
    $comment_post_ID = absint( $commentdata['comment_post_ID'] );
  }

  if ( $update ) {
    $old_comment = get_comment( $comment_id );

    if ( empty( $old_comment ) ) {
      if ( $wp_error ) {
        return new WP_Error( 'comment_not_exists', __( 'Specified comment does not exist.' ) );
      } else {
        return 0;
      }
    }

    $comment_post_ID = $old_comment->comment_post_ID;
  }

  $status = wp_allow_comment( $commentdata );
  if ( is_wp_error( $status ) ) {
    if ( $wp_error ) {
      return $status;
    } else {
      return 0;
    }
  }

  if ( ! empty( $commentdata['comment_author_IP'] ) ) {
    $comment_author_IP = $commentdata['comment_author_IP'];
  }

  if ( ! empty( $commentdata['comment_date'] ) ) {
    $comment_date = $commentdata['comment_date'];
  }

  if ( ! empty( $commentdata['comment_date_gmt'] ) ) {
    $comment_date_gmt = $commentdata['comment_date_gmt'];
  }

  $comment_parent = 0;
  if ( isset( $commentdata['comment_parent'] ) ) {
    $comment_parent = absint( $commentdata['comment_parent'] );
  }

  $comment_author       = '';
  $comment_author_email = '';
  $comment_author_url   = '';
  $comment_content      = '';
  $comment_karma        = 0;
  $comment_approved     = 1;
  $comment_agent        = '';
  $comment_type         = '';

  if ( isset( $commentdata['comment_author'] ) ) {
    $comment_author = trim( $commentdata['comment_author'] );
  }

  if ( isset( $commentdata['comment_author_email'] ) ) {
    $comment_author_email = trim( $commentdata['comment_author_email'] );
  }

  if ( isset( $commentdata['comment_author_url'] ) ) {
    $comment_author_url = trim( $commentdata['comment_author_url'] );
  }

  if ( isset( $commentdata['comment_content'] ) ) {
    $comment_content = trim( $commentdata['comment_content'] );
  }

  if ( isset( $commentdata['comment_karma'] ) ) {
    $comment_karma = absint( $commentdata['comment_karma'] );
  }

  if ( isset( $commentdata['comment_approved'] ) ) {
    $comment_approved = $commentdata['comment_approved'];
  }

  if ( isset( $commentdata['comment_agent'] ) ) {
    $comment_agent = trim( $commentdata['comment_agent'] );
  }

  if ( isset( $commentdata['comment_type'] ) ) {
    $comment_type = trim( $commentdata['comment_type'] );
  }

  $comment_author       = substr( $comment_author, 0, 255 );
  $comment_author_email = substr( $comment_author_email, 0, 100 );
  $comment_author_url   = substr( $comment_author_url, 0, 200 );
  $comment_agent        = substr( $comment_agent, 0, 255 );
  $comment_type         = substr( $comment_type, 0, 20 );
  $comment_content      = trim( $comment_content );

  $data = 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'
  );

  $data  = wp_filter_comment( $data );
  $where = '';

  if ( $update ) {
    $where = array( 'comment_ID' => $comment_id );
    /**
     * Fires before a comment is updated in the database.
     *
     * @since 2.7.0
     *
     * @param int   $comment_id The ID of the comment to be updated.
     * @param array $commentarr Array of unslashed comment data.
     */
    do_action( 'pre_comment_update', $comment_id, $commentarr );

    $result = $wpdb->update( $wpdb->comments, $data, $where );

    /**
     * Fires after a comment is updated in the database.
     *
     * @since 2.7.0
     *
     * @param int   $comment_id The ID of the comment that was updated.
     * @param array $commentarr Array of unslashed comment data.
     */
    do_action( 'edit_comment', $comment_id, $commentarr );
  } else {
    /**
     * Fires before a comment is created in the database.
     *
     * @since 2.7.0
     *
     * @param array $commentarr Array of unslashed comment data.
     */
    do_action( 'pre_comment_on_post', $comment_post_ID );
    do_action( 'preprocess_comment', $commentarr );

    $result = $wpdb->insert( $wpdb->comments, $data );
    $comment_id = (int) $wpdb->insert_id;

    /**
     * Fires after a comment is created in the database.
     *
     * @since 2.7.0
     *
     * @param int   $comment_id The ID of the comment that was created.
     * @param array $commentarr Array of unslashed comment data.
     */
    do_action( 'comment_post', $comment_id, $commentarr );
  }

  if ( $result ) {
    clean_comment_cache( $comment_id );

    $comment = get_comment( $comment_id );

    wp_update_comment_count( $comment_post_ID );

    /**
     * Fires after a comment is successfully inserted or updated in the database.
     *
     * @since 2.0.0
     *
     * @param int     $comment_id The ID of the comment.
     * @param WP_Comment $comment    Comment object.
     */
    do_action( 'wp_insert_comment', $comment_id, $comment );

    return $comment_id;
  } else {
    if ( $wp_error ) {
      return new WP_Error( 'db_insert_error', __( 'Could not insert comment into the database.' ), $wpdb->last_error );
    } else {
      return 0;
    }
  }
}

看起来有点吓人?别怕,咱们一步步拆解。

  1. 输入参数

    • $commentarr:评论数据的数组或者对象。必须经过 balanceTags() 处理(用于平衡 HTML 标签,防止恶意代码)。
    • $wp_error:可选参数,如果为 true,出错时返回 WP_Error 对象,否则返回 0。
  2. 数据清洗与准备

    • wp_unslash()wp_slash():这两个函数分别用于移除和添加反斜杠,以确保数据安全。别忘了,WordPress 喜欢在数据入库前加斜杠,出来的时候再去掉。
    • wp_parse_args():将传入的数组转换为一个标准的数组格式,方便后续处理。
    • 确定是更新还是创建评论。如果 $commentdata['comment_ID'] 存在,那就是更新,否则就是新建。
  3. 数据校验

    • wp_allow_comment():这个函数会检查评论是否允许被插入,例如是否是垃圾评论,是否需要审核等等。
  4. 数据组装

    • $commentdata 数组中提取各种评论相关的字段,例如作者、邮箱、URL、内容等等。
    • 使用 compact() 函数将这些字段打包成一个 $data 数组,准备插入数据库。
  5. 数据库操作

    • 如果是更新评论,使用 $wpdb->update() 函数。
    • 如果是插入新评论,使用 $wpdb->insert() 函数。
    • $wpdb 是 WordPress 的数据库操作对象,负责与数据库进行交互。
  6. 缓存清理与计数更新

    • clean_comment_cache():清理评论缓存,确保显示最新的评论数据。
    • wp_update_comment_count():更新文章的评论计数。
  7. Action Hooks

    • pre_comment_on_post (before insert)
    • preprocess_comment (before insert)
    • comment_post (after insert)
    • pre_comment_update (before update)
    • edit_comment (after update)
    • wp_insert_comment (after insert or update)

    这些 Actions 允许开发者在评论插入或更新的不同阶段执行自定义代码。它们是 WordPress 插件和主题扩展功能的重要入口。

  8. 返回值

    • 成功时返回评论 ID。
    • 出错时,如果 $wp_errortrue,返回 WP_Error 对象,否则返回 0。

第二幕:评论 meta 数据的奥秘

评论 meta 数据就像是评论的“附加属性”,可以用来存储一些额外的信息,比如用户的评分、评论的自定义状态等等。WordPress 提供了一系列函数来管理评论 meta 数据。

  • add_comment_meta( int $comment_id, string $meta_key, mixed $meta_value, bool $unique = false ):添加评论 meta 数据。
  • get_comment_meta( int $comment_id, string $meta_key = '', bool $single = false ):获取评论 meta 数据。
  • update_comment_meta( int $comment_id, string $meta_key, mixed $meta_value, mixed $prev_value = '' ):更新评论 meta 数据。
  • delete_comment_meta( int $comment_id, string $meta_key, mixed $meta_value = '' ):删除评论 meta 数据。

这些函数底层操作的是 wp_commentmeta 表,这个表存储了所有评论的 meta 数据。

第三幕:代码实战:插入评论并添加 meta 数据

现在,咱们来写一段代码,演示如何使用 wp_insert_comment() 插入一条评论,并添加一些 meta 数据。

<?php
// 评论数据
$commentdata = array(
  'comment_post_ID' => 1, // 文章 ID
  'comment_author' => '张三',
  'comment_author_email' => '[email protected]',
  'comment_author_url' => 'https://example.com',
  'comment_content' => '这是一条测试评论。',
  'comment_type' => '', // 评论类型,默认为空
  'comment_parent' => 0, // 父评论 ID,默认为 0
  'comment_author_IP' => '127.0.0.1',
  'comment_agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36',
  'comment_date' => current_time('mysql'),
  'comment_date_gmt' => current_time('mysql', 1),
  'comment_approved' => 1,
);

// 插入评论
$comment_id = wp_insert_comment($commentdata);

if ($comment_id) {
  echo '评论插入成功,评论 ID:' . $comment_id . '<br>';

  // 添加 meta 数据
  $rating = 5; // 评分
  $custom_status = 'featured'; // 自定义状态

  add_comment_meta($comment_id, 'rating', $rating);
  add_comment_meta($comment_id, 'custom_status', $custom_status);

  echo '成功添加 meta 数据:rating = ' . $rating . ', custom_status = ' . $custom_status . '<br>';

  // 获取 meta 数据
  $retrieved_rating = get_comment_meta($comment_id, 'rating', true);
  $retrieved_status = get_comment_meta($comment_id, 'custom_status', true);

  echo '获取到的 meta 数据:rating = ' . $retrieved_rating . ', custom_status = ' . $retrieved_status . '<br>';

  // 更新 meta 数据
  $new_rating = 4;
  update_comment_meta($comment_id, 'rating', $new_rating);

  echo '成功更新 meta 数据:rating = ' . $new_rating . '<br>';

  // 删除 meta 数据
  delete_comment_meta($comment_id, 'custom_status');

  echo '成功删除 meta 数据:custom_status<br>';
} else {
  echo '评论插入失败。';
}
?>

这段代码演示了如何插入一条评论,并添加、获取、更新和删除评论的 meta 数据。

第四幕:深入理解 wp_commentmeta

wp_commentmeta 表是存储评论 meta 数据的关键。它的结构通常如下:

列名 数据类型 说明
meta_id bigint(20) UNSIGNED 主键,自增长
comment_id bigint(20) UNSIGNED 评论 ID,关联 wp_comments 表的 comment_ID
meta_key varchar(255) meta 键名
meta_value longtext meta

第五幕:使用 Action Hooks 扩展功能

咱们可以利用 wp_insert_comment 这个 action hook,在评论插入后执行一些自定义操作,比如发送邮件通知管理员。

<?php
/**
 * 在评论插入后发送邮件通知管理员
 *
 * @param int     $comment_id 评论 ID
 * @param WP_Comment $comment    评论对象
 */
function my_custom_comment_notification($comment_id, $comment) {
  $to = get_option('admin_email');
  $subject = '有新的评论需要审核!';
  $message = '文章:' . get_the_title($comment->comment_post_ID) . "n" .
             '作者:' . $comment->comment_author . "n" .
             '内容:' . $comment->comment_content . "n" .
             '请登录后台审核:' . admin_url('comment.php?action=editcomment&id=' . $comment_id);

  wp_mail($to, $subject, $message);
}
add_action('wp_insert_comment', 'my_custom_comment_notification', 10, 2);
?>

这段代码会在每次成功插入评论后,给管理员发送一封邮件,提醒他们去审核评论。

第六幕:性能优化与安全注意事项

  • 性能优化:避免频繁读取和写入 comment meta 数据,特别是在循环中。可以考虑使用缓存来提高性能。
  • 安全注意事项:对 meta_keymeta_value 进行严格的过滤和验证,防止 SQL 注入和 XSS 攻击。永远不要信任用户的输入。

第七幕:高级技巧

  • 自定义评论类型:可以通过设置 comment_type 字段来创建自定义的评论类型,例如“回复”、“引用”等等。
  • 使用 WP_Comment_Query 查询评论:可以使用 WP_Comment_Query 类来执行复杂的评论查询,包括根据 meta 数据进行筛选。

总结

wp_insert_comment() 函数是 WordPress 评论系统的核心,它负责将评论数据插入到数据库中,并处理相关的 meta 数据。理解这个函数的原理和用法,可以帮助你更好地定制和扩展 WordPress 的评论功能。 记住,安全第一,性能至上!

希望这次探险之旅对你有所帮助!下次咱们再一起探索 WordPress 的其他奥秘!

Good luck, and happy coding!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注