深入解读 WordPress `get_comment_meta()` 函数源码:评论元数据的获取流程。

各位观众老爷,大家好!今天咱们来聊聊WordPress里面一个挺不起眼,但关键时候能救命的函数——get_comment_meta()。这玩意儿,专门负责从数据库里捞出评论的元数据,就像从评论的口袋里掏宝贝一样。

咱们今天就来扒一扒它的源码,看看它到底是怎么运作的,顺便也学点写代码的小技巧。准备好了吗?咱们开始!

一、Meta,何方神圣?评论为何需要Meta?

在深入代码之前,先得弄清楚“meta”是个什么东西。简单来说,meta就是“元数据”,是对数据进行描述的数据。对于评论来说,除了评论内容、作者、时间这些基本信息,我们还可能需要一些额外的、自定义的信息。

举个例子:

  • 用户评分: 假设你想让用户对评论进行评分,这个评分数据就可以作为评论的meta。
  • 管理员审核标记: 管理员可以给某些评论打上“已审核”、“待处理”之类的标记,这些标记也是meta。
  • 评论来源: 标记评论来自哪个渠道(比如邮件回复、社交平台),这也能当meta。

如果没有 meta,这些额外的信息就没地方放,或者只能塞到评论内容里,想想都觉得乱。

二、get_comment_meta():你的取款机

get_comment_meta()函数的作用,就是根据你提供的评论ID和meta key,从数据库里把对应的meta value取出来。它的基本用法是这样的:

<?php
$comment_id = 123; // 评论ID
$meta_key = 'rating'; // meta key,比如 'rating'(评分)
$single = true; // 是否只返回一个值,默认是 false 返回数组

$rating = get_comment_meta( $comment_id, $meta_key, $single );

if ( $rating ) {
    echo '评论评分:' . $rating;
} else {
    echo '该评论没有评分';
}
?>
  • $comment_id 不用多说,就是你要取meta的评论的ID。
  • $meta_key 你要取的meta的名称。
  • $single 决定返回单个值还是数组。如果$singletrue,且找到了值,就直接返回这个值;如果是false,或者有多个相同$meta_key的值,就返回一个包含所有值的数组。

三、源码剖析:一步一步揭开它的面纱

好了,重头戏来了,咱们打开WordPress的源码,找到wp-includes/comment.php文件,找到get_comment_meta()函数。

function get_comment_meta( $comment_id, $key = '', $single = false ) {
    return get_metadata( 'comment', $comment_id, $key, $single );
}

WTF?! 就这么一行? 没错,get_comment_meta() 自己啥也不干,直接把任务丢给了 get_metadata()。 这是一种典型的“门面模式”,get_comment_meta() 只是一个门面,负责把请求转发给真正干活的 get_metadata()

这样做的好处是,代码更简洁,也更统一。WordPress用一套 *_metadata() 函数体系来处理各种类型的meta数据,比如文章meta、用户meta、评论meta等等。

接下来,我们继续追踪 get_metadata() 函数,它位于 wp-includes/meta.php 文件中。

function get_metadata( $meta_type, $object_id, $meta_key = '', $single = false ) {
    global $wpdb;

    $object_id = absint( $object_id );
    if ( ! $object_id ) {
        return false;
    }

    $meta_type = sanitize_key( $meta_type );
    if ( ! in_array( $meta_type, array( 'post', 'comment', 'term', 'user' ), true ) ) {
        return false;
    }

    $table_name = _get_meta_table( $meta_type );

    if ( ! $table_name ) {
        return false;
    }

    $cache_key = sanitize_key( $meta_type ) . '_' . $object_id;

    $cache = wp_cache_get( $cache_key, 'meta' );

    if ( ! isset( $cache[ $meta_key ] ) && '' !== $meta_key ) {
        $cache = update_meta_cache( $meta_type, array( $object_id ) );
        if ( isset( $cache[ $meta_key ] ) ) {
            $cache = $cache[ $meta_key ];
        } else {
            $cache = false;
        }
    }

    if ( isset( $cache[ $meta_key ] ) ) {
        $values = array_map( 'maybe_unserialize', (array) $cache[ $meta_key ] );

        if ( $single ) {
            return reset( $values );
        } else {
            return $values;
        }
    }

    if ( ! $meta_key ) {
        $sql = "SELECT meta_key, meta_value FROM $table_name WHERE {$meta_type}_id = %d ORDER BY meta_key";
        $values = $wpdb->get_results( $wpdb->prepare( $sql, $object_id ), OBJECT_K );

        if ( empty( $values ) ) {
            return false;
        }

        $cache = array();
        foreach ( $values as $meta_key => $value ) {
            $cache[ $meta_key ] = array( maybe_unserialize( $value->meta_value ) );
        }

        wp_cache_set( $cache_key, $cache, 'meta' );

        if ( $single ) {
            return reset( $cache[ $meta_key ] );
        } else {
            return $cache;
        }
    }

    $sql = $wpdb->prepare( "SELECT meta_value FROM $table_name WHERE {$meta_type}_id = %d AND meta_key = %s", $object_id, $meta_key );

    $values = $wpdb->get_col( $sql );

    if ( empty( $values ) ) {
        return false;
    }

    $values = array_map( 'maybe_unserialize', $values );

    if ( $single ) {
        return reset( $values );
    } else {
        return $values;
    }
}

这段代码有点长,咱们分段来解读:

1. 参数校验和表名获取

    global $wpdb;

    $object_id = absint( $object_id );
    if ( ! $object_id ) {
        return false;
    }

    $meta_type = sanitize_key( $meta_type );
    if ( ! in_array( $meta_type, array( 'post', 'comment', 'term', 'user' ), true ) ) {
        return false;
    }

    $table_name = _get_meta_table( $meta_type );

    if ( ! $table_name ) {
        return false;
    }
  • 首先,引入全局的 $wpdb 对象,它是WordPress用来操作数据库的核心类。
  • 然后,对传入的参数进行校验:
    • $object_id 必须是整数,并且大于0。
    • $meta_type 必须是 postcommenttermuser 中的一个。
  • 接着,通过 _get_meta_table() 函数获取存储meta数据的表名。对于评论meta,表名是 wp_commentmeta

2. 缓存机制:能用缓存就不用数据库

    $cache_key = sanitize_key( $meta_type ) . '_' . $object_id;

    $cache = wp_cache_get( $cache_key, 'meta' );

    if ( ! isset( $cache[ $meta_key ] ) && '' !== $meta_key ) {
        $cache = update_meta_cache( $meta_type, array( $object_id ) );
        if ( isset( $cache[ $meta_key ] ) ) {
            $cache = $cache[ $meta_key ];
        } else {
            $cache = false;
        }
    }

    if ( isset( $cache[ $meta_key ] ) ) {
        $values = array_map( 'maybe_unserialize', (array) $cache[ $meta_key ] );

        if ( $single ) {
            return reset( $values );
        } else {
            return $values;
        }
    }
  • WordPress使用了缓存来提高性能。它首先尝试从缓存中获取meta数据。
  • wp_cache_get( $cache_key, 'meta' ) 从名为 ‘meta’ 的缓存组中获取键为 $cache_key 的缓存数据。 $cache_key 是由meta类型和对象ID组成的,比如 comment_123
  • 如果缓存中没有找到指定 $meta_key 的数据,并且 $meta_key 不为空,那么会调用 update_meta_cache() 函数来更新缓存。update_meta_cache() 会批量从数据库中读取指定对象的所有meta数据,并存入缓存。
  • 如果缓存中找到了数据,就直接返回,并根据 $single 参数决定返回单个值还是数组。 maybe_unserialize() 函数用于反序列化从数据库中取出的数据。

3. 从数据库中读取数据

    if ( ! $meta_key ) {
        $sql = "SELECT meta_key, meta_value FROM $table_name WHERE {$meta_type}_id = %d ORDER BY meta_key";
        $values = $wpdb->get_results( $wpdb->prepare( $sql, $object_id ), OBJECT_K );

        if ( empty( $values ) ) {
            return false;
        }

        $cache = array();
        foreach ( $values as $meta_key => $value ) {
            $cache[ $meta_key ] = array( maybe_unserialize( $value->meta_value ) );
        }

        wp_cache_set( $cache_key, $cache, 'meta' );

        if ( $single ) {
            return reset( $cache[ $meta_key ] );
        } else {
            return $cache;
        }
    }

    $sql = $wpdb->prepare( "SELECT meta_value FROM $table_name WHERE {$meta_type}_id = %d AND meta_key = %s", $object_id, $meta_key );

    $values = $wpdb->get_col( $sql );

    if ( empty( $values ) ) {
        return false;
    }

    $values = array_map( 'maybe_unserialize', $values );

    if ( $single ) {
        return reset( $values );
    } else {
        return $values;
    }
  • 如果缓存中没有找到数据,或者 $meta_key 为空(表示要获取所有meta数据),那么就需要从数据库中读取数据。
  • 如果 $meta_key 为空,构造SQL语句,查询指定 object_id 的所有 meta_keymeta_value,并按照 meta_key 排序。 然后将结果存入缓存,并根据 $single 参数决定返回单个值还是数组。
  • 如果 $meta_key 不为空,构造SQL语句,查询指定 object_idmeta_keymeta_value。然后根据 $single 参数决定返回单个值还是数组。
  • $wpdb->prepare() 函数用于防止SQL注入。
  • $wpdb->get_results()$wpdb->get_col() 是WordPress提供的数据库查询函数。
  • maybe_unserialize() 函数用于反序列化从数据库中取出的数据。

四、wp_commentmeta 表结构

咱们来简单看看 wp_commentmeta 表的结构,这对理解meta数据的存储方式很有帮助。

字段名 数据类型 说明
meta_id bigint(20) UNSIGNED 自增ID,主键
comment_id bigint(20) UNSIGNED 评论ID,外键,关联wp_comments表的comment_ID字段
meta_key varchar(255) meta的名称,比如 ‘rating’
meta_value longtext meta的值,可以存储字符串、数字、数组、对象等。 注意,数组和对象会先被序列化成字符串,再存入数据库。

五、update_meta_cache() 函数

前面提到,update_meta_cache() 函数用于批量更新缓存。 咱们简单看看这个函数。

function update_meta_cache( $meta_type, $object_ids ) {
    global $wpdb;

    if ( empty( $object_ids ) ) {
        return false;
    }

    $meta_type = sanitize_key( $meta_type );
    if ( ! in_array( $meta_type, array( 'post', 'comment', 'term', 'user' ), true ) ) {
        return false;
    }

    $table_name = _get_meta_table( $meta_type );

    if ( ! $table_name ) {
        return false;
    }

    $object_ids = array_map( 'absint', (array) $object_ids );
    $object_ids = array_unique( $object_ids );

    $cache = array();
    $non_cached_ids = array();
    foreach ( $object_ids as $id ) {
        $cache_key = sanitize_key( $meta_type ) . '_' . $id;
        $cached_object = wp_cache_get( $cache_key, 'meta' );
        if ( false === $cached_object ) {
            $non_cached_ids[] = $id;
        } else {
            $cache[ $id ] = $cached_object;
        }
    }

    if ( empty( $non_cached_ids ) ) {
        return $cache;
    }

    $object_ids_string = implode( ',', $non_cached_ids );

    $sql = "SELECT {$meta_type}_id, meta_key, meta_value FROM $table_name WHERE {$meta_type}_id IN ($object_ids_string) ORDER BY meta_key";

    $meta_list = $wpdb->get_results( $sql, OBJECT );

    if ( ! empty( $meta_list ) ) {
        foreach ( $meta_list as $meta ) {
            $id = (int) $meta->{$meta_type . '_id'};
            $meta_key = $meta->meta_key;
            $meta_value = maybe_unserialize( $meta->meta_value );

            if ( ! isset( $cache[ $id ] ) ) {
                $cache[ $id ] = array();
            }

            if ( ! isset( $cache[ $id ][ $meta_key ] ) ) {
                $cache[ $id ][ $meta_key ] = array();
            }

            $cache[ $id ][ $meta_key ][] = $meta_value;
        }

        foreach ( $cache as $id => $object_cache ) {
            $cache_key = sanitize_key( $meta_type ) . '_' . $id;
            wp_cache_set( $cache_key, $object_cache, 'meta' );
        }
    }

    return $cache;
}
  • 这个函数接受一个 meta 类型和一个对象 ID 数组作为参数。
  • 它首先检查哪些对象 ID 已经存在于缓存中,哪些不在。
  • 然后,它从数据库中批量读取不在缓存中的对象 ID 的所有 meta 数据。
  • 最后,它将读取到的 meta 数据存入缓存。

六、总结:get_comment_meta() 的工作流程

现在,我们来总结一下 get_comment_meta() 函数的工作流程:

  1. 接收评论ID、meta key和single参数。
  2. 调用 get_metadata() 函数。
  3. get_metadata() 函数进行参数校验。
  4. get_metadata() 函数尝试从缓存中获取数据。
  5. 如果缓存中没有数据,get_metadata() 函数调用 update_meta_cache() 函数更新缓存。
  6. 如果缓存中仍然没有数据,get_metadata() 函数从数据库中读取数据。
  7. get_metadata() 函数根据single参数决定返回单个值还是数组。
  8. get_metadata() 函数返回数据。
  9. get_comment_meta() 函数返回 get_metadata() 函数返回的数据。

可以用表格来更清晰地表示:

步骤 函数 操作
1 get_comment_meta() 接收参数,调用get_metadata()
2 get_metadata() 参数校验,获取表名
3 get_metadata() 尝试从缓存中获取数据 (wp_cache_get())
4 get_metadata() 如果缓存未命中,调用 update_meta_cache() 更新缓存
5 update_meta_cache() 批量查询数据库,将结果存入缓存 (wp_cache_set())
6 get_metadata() 如果缓存仍然未命中(或者需要获取所有meta),则直接查询数据库 ($wpdb->get_results()$wpdb->get_col())
7 get_metadata() 根据 $single 参数,决定返回单个值还是数组
8 get_comment_meta() 返回结果

七、优化建议:用好缓存,避免滥用

  • 批量获取: 如果你需要获取多个评论的meta数据,最好使用 update_meta_cache() 函数批量更新缓存,然后再从缓存中获取数据,这样可以减少数据库查询次数。
  • 避免频繁更新: 频繁更新meta数据会导致缓存失效,影响性能。尽量减少不必要的更新操作。
  • 考虑使用 Transients API: 对于一些不经常变化,但又需要持久存储的数据,可以考虑使用 WordPress 的 Transients API,它提供了更灵活的缓存机制。

八、总结:Meta,让评论更强大

get_comment_meta() 函数虽然简单,但它是WordPress评论系统的重要组成部分。它允许我们为评论添加自定义的元数据,从而扩展评论的功能,满足各种各样的需求。理解它的工作原理,可以帮助我们更好地使用它,并优化我们的WordPress站点。

好了,今天的讲座就到这里。希望大家有所收获!下次再见!

发表回复

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