各位观众,晚上好!今天咱们来聊聊 WordPress 里一对神奇的函数:get_transient()
和 set_transient()
。 别看名字高深,其实它们就是 WordPress 的“小抄本”,专门用来缓存那些“一会儿有用,一会儿没用”的数据。想象一下,你辛辛苦苦从数据库里捞出一堆数据,恨不得立刻写在手心,下次用的时候直接看,省得再去数据库里折腾。 这俩函数就扮演了你手心的角色,只不过它们写的是数字化的“小抄”。
为什么要用 Transient?
在深入源码之前,先说说为什么要用这玩意儿。 想象一下,你的网站上有一个功能,需要频繁调用一个外部 API 获取数据。 每次用户访问都去调 API,你的服务器和外部 API 估计都要崩溃。 这时候,Transient 就派上用场了。 它可以把 API 返回的数据缓存起来,在一定时间内直接返回缓存数据,减轻服务器压力,提高网站速度。
举几个例子:
- 第三方 API 数据: 比如天气预报、汇率信息等,这些数据一般不会实时变化,缓存一段时间是没问题的。
- 复杂的数据库查询结果: 比如计算热门文章、统计用户行为等,这些查询消耗资源较多,缓存可以减少数据库压力。
- 页面片段: 缓存一些不常变化的页面片段,比如侧边栏的广告、热门评论等。
Transient 的基本用法
先来个简单的例子:
<?php
// 1. 设置 Transient
$transient_name = 'my_super_data';
$data = array(
'name' => '张三',
'age' => 30,
'city' => '北京'
);
$expiration = 3600; // 缓存 1 小时 (秒)
set_transient( $transient_name, $data, $expiration );
// 2. 获取 Transient
$cached_data = get_transient( $transient_name );
if ( false === $cached_data ) {
// Transient 不存在或已过期,重新获取数据
$data = array(
'name' => '张三',
'age' => 30,
'city' => '北京'
); // 假设这里是从数据库或者 API 获取数据
set_transient( $transient_name, $data, $expiration );
$cached_data = $data;
echo '从数据库获取数据!';
} else {
echo '从缓存获取数据!';
}
// 3. 使用缓存数据
echo '<pre>';
print_r( $cached_data );
echo '</pre>';
// 4. 删除 Transient (可选)
// delete_transient( $transient_name );
?>
这个例子演示了 Transient 的基本用法:
set_transient( $transient_name, $value, $expiration )
: 设置一个 Transient,参数分别是 Transient 的名称、要缓存的数据、以及过期时间 (秒)。get_transient( $transient_name )
: 获取一个 Transient,如果 Transient 存在且未过期,则返回缓存的数据;否则返回false
。delete_transient( $transient_name )
: 删除一个 Transient (可选)。
源码剖析:set_transient()
现在,让我们深入 set_transient()
的源码,看看它是如何实现的。 set_transient()
函数位于 wp-includes/functions.php
文件中。
function set_transient( $transient, $value, $expiration = 0 ) {
/**
* Fires before a transient is set.
*
* @since 3.0.0
*
* @param string $transient Transient name.
* @param mixed $value Transient value.
* @param int $expiration Time until expiration in seconds.
*/
do_action( 'set_transient', $transient, $value, $expiration );
/**
* Filters the value of an existing transient.
*
* The dynamic portion of the hook name, `$transient`, refers to the transient name.
*
* @since 4.4.0
*
* @param mixed $value The value to be stored, might be pre-filtered.
* @param string $transient Transient name.
* @param int $expiration Time until expiration in seconds.
*/
$value = apply_filters( 'pre_set_transient_' . $transient, $value, $transient, $expiration );
if ( is_serialized( $value ) ) {
$value = @unserialize( $value );
}
/**
* Filters the expiration of an existing transient.
*
* The dynamic portion of the hook name, `$transient`, refers to the transient name.
*
* @since 4.4.0
*
* @param int $expiration Time until expiration in seconds.
* @param string $transient Transient name.
* @param mixed $value Transient value.
*/
$expiration = apply_filters( 'transient_expiration_' . $transient, $expiration, $transient, $value );
if ( empty( $expiration ) ) {
$expiration = 0;
} else {
$expiration = time() + (int) $expiration;
}
$transient_timeout = '_transient_timeout_' . $transient;
$transient = '_transient_' . $transient;
// If the transient does not already exist, then we need to insert it.
if ( false === get_option( $transient ) ) {
if ( add_option( $transient, $value, '', 'no' ) ) {
add_option( $transient_timeout, $expiration, '', 'no' );
/**
* Fires after a transient is set.
*
* @since 3.0.0
*
* @param string $transient Transient name.
* @param mixed $value Transient value.
* @param int $expiration Time until expiration in seconds.
*/
do_action( 'setted_transient', $transient, $value, $expiration );
return true;
} else {
return false;
}
} else {
// If the transient already exists, then we need to update it.
if ( update_option( $transient, $value ) ) {
update_option( $transient_timeout, $expiration );
/**
* Fires after an existing transient is updated.
*
* @since 3.0.0
*
* @param string $transient Transient name.
* @param mixed $value Transient value.
* @param int $expiration Time until expiration in seconds.
*/
do_action( 'updated_transient', $transient, $value, $expiration );
return true;
} else {
return false;
}
}
}
代码有点长,咱们一步步分析:
-
Action 和 Filter:
set_transient()
函数开头和结尾都使用了do_action()
函数,允许开发者在 Transient 设置前后执行自定义操作。中间还使用了apply_filters()
函数,允许开发者修改 Transient 的值和过期时间。 这体现了 WordPress 强大的扩展性。 -
is_serialized()
和unserialize()
: 这部分代码看起来有点奇怪。 实际上,WordPress 选项 API 默认会自动序列化和反序列化数据。 为了保持一致性,set_transient()
也做了类似的处理。 如果$value
已经是序列化的字符串,它会先反序列化,然后再保存。 -
处理过期时间:
$expiration
参数是秒数,set_transient()
会将其转换为 Unix 时间戳,并保存在_transient_timeout_{$transient}
选项中。 -
Transient 名称:
set_transient()
会给传入的$transient
名称加上_transient_
前缀,然后作为选项名称存储在数据库中。 过期时间也类似,会加上_transient_timeout_
前缀。 这样做是为了避免 Transient 名称与现有的选项名称冲突。 -
使用
add_option()
和update_option()
:set_transient()
使用add_option()
和update_option()
函数将 Transient 数据和过期时间存储在wp_options
表中。- 如果 Transient 不存在,则使用
add_option()
创建新的选项。 - 如果 Transient 已经存在,则使用
update_option()
更新选项的值。
- 如果 Transient 不存在,则使用
关键点总结:set_transient()
步骤 | 说明 |
---|---|
1. Action/Filter | 提供钩子,允许开发者在 Transient 设置前后和修改 Transient 的值和过期时间时执行自定义操作。 |
2. 序列化处理 | 如果 Transient 的值已经是序列化的字符串,先进行反序列化。 |
3. 计算过期时间 | 将传入的秒数 $expiration 转换为 Unix 时间戳。 |
4. 添加前缀 | 给 Transient 名称和过期时间名称添加 _transient_ 和 _transient_timeout_ 前缀,避免与现有选项冲突。 |
5. 存储到数据库 | 使用 add_option() 或 update_option() 函数将 Transient 数据和过期时间存储到 wp_options 表中。 |
源码剖析:get_transient()
接下来,我们看看 get_transient()
的源码,了解它是如何获取 Transient 数据的。 get_transient()
函数也位于 wp-includes/functions.php
文件中。
function get_transient( $transient ) {
/**
* Filters the value of an existing transient.
*
* The dynamic portion of the hook name, `$transient`, refers to the transient name.
*
* @since 4.4.0
*
* @param mixed $pre_transient The default value to return if the transient does not exist.
* Any value other than false will short-circuit the retrieval
* and return the returned value.
* @param string $transient Transient name.
*/
$pre = apply_filters( 'pre_transient_' . $transient, false, $transient );
if ( false !== $pre ) {
return $pre;
}
$transient = '_transient_' . $transient;
$value = get_option( $transient );
if ( false === $value ) {
return false;
}
$transient_timeout = '_transient_timeout_' . $transient;
$expiration = get_option( $transient_timeout );
if ( false !== $expiration && time() > (int) $expiration ) {
delete_transient( substr( $transient, 11 ) );
return false;
}
/**
* Filters an existing transient's value.
*
* The dynamic portion of the hook name, `$transient`, refers to the transient name.
*
* @since 3.0.0
*
* @param mixed $value Value of transient.
* @param string $transient Transient name.
*/
return apply_filters( 'transient_' . substr( $transient, 11 ), $value, substr( $transient, 11 ) );
}
代码同样有点长,咱们分解一下:
-
Filter:
get_transient()
首先使用apply_filters()
函数,允许开发者在获取 Transient 之前自定义返回值。 如果pre_transient_{$transient}
过滤器返回的值不是false
,则直接返回该值,不再执行后续的代码。 这提供了一种提前返回缓存数据的机制。 -
Transient 名称:
get_transient()
同样会给传入的$transient
名称加上_transient_
前缀,以便从数据库中获取正确的选项。 -
使用
get_option()
:get_transient()
使用get_option()
函数从wp_options
表中获取 Transient 数据。 如果选项不存在,get_option()
会返回false
,get_transient()
也随之返回false
。 -
检查过期时间:
get_transient()
会获取_transient_timeout_{$transient}
选项,并判断当前时间是否超过了过期时间。 如果已过期,get_transient()
会使用delete_transient()
函数删除 Transient,并返回false
。 -
Filter:
get_transient()
最后使用apply_filters()
函数,允许开发者在返回 Transient 数据之前对其进行修改。 这里使用substr( $transient, 11 )
去掉了_transient_
前缀,以便过滤器接收到原始的 Transient 名称。
关键点总结:get_transient()
步骤 | 说明 |
---|---|
1. Filter | 提供钩子,允许开发者在获取 Transient 之前自定义返回值,提前返回缓存数据。 |
2. 添加前缀 | 给 Transient 名称添加 _transient_ 前缀,从数据库中获取对应的选项。 |
3. 从数据库获取数据 | 使用 get_option() 函数从 wp_options 表中获取 Transient 数据。 |
4. 检查过期时间 | 获取 _transient_timeout_{$transient} 选项,判断当前时间是否超过了过期时间。如果已过期,则删除 Transient 并返回 false 。 |
5. Filter | 提供钩子,允许开发者在返回 Transient 数据之前对其进行修改。 |
delete_transient()
函数
顺便提一下 delete_transient()
函数,它用于删除 Transient。 源码如下:
function delete_transient( $transient ) {
/**
* Fires before a transient is deleted.
*
* @since 3.0.0
*
* @param string $transient Transient name.
*/
do_action( 'delete_transient', $transient );
$transient_timeout = '_transient_timeout_' . $transient;
$transient = '_transient_' . $transient;
$deleted = delete_option( $transient );
if ( $deleted ) {
delete_option( $transient_timeout );
/**
* Fires after a transient is deleted.
*
* @since 3.0.0
*
* @param string $transient Transient name.
*/
do_action( 'deleted_transient', $transient );
return true;
} else {
return false;
}
}
delete_transient()
函数也很简单:
- Action: 提供钩子,允许开发者在 Transient 删除前后执行自定义操作。
- 添加前缀: 给 Transient 名称添加
_transient_
前缀。 - 使用
delete_option()
: 使用delete_option()
函数从wp_options
表中删除 Transient 数据和过期时间。
Transient 的存储位置
Transient 数据存储在 wp_options
表中。 wp_options
表是 WordPress 用来存储各种配置选项的表。 Transient 数据以选项的形式存储,选项名称以 _transient_
开头,过期时间以 _transient_timeout_
开头。
Transient 的优缺点
优点:
- 简单易用:
get_transient()
和set_transient()
函数使用起来非常简单,只需要几行代码就可以实现数据缓存。 - 自动过期: Transient 可以设置过期时间,过期后会自动删除,避免缓存数据过期。
- 可扩展性: Transient 提供了丰富的钩子,允许开发者自定义缓存行为。
缺点:
- 存储在数据库: Transient 数据存储在数据库中,如果缓存数据量过大,可能会影响数据库性能。
- 不适合缓存大型数据: 由于存储在数据库中,Transient 不适合缓存大型数据,比如大型图片或视频。
最佳实践
- 选择合适的过期时间: 根据数据的变化频率,选择合适的过期时间。 如果数据变化频繁,过期时间可以设置短一些;如果数据变化较慢,过期时间可以设置长一些。
- 不要缓存敏感数据: Transient 数据存储在数据库中,不适合缓存敏感数据,比如用户密码、信用卡信息等。
- 避免缓存大型数据: Transient 不适合缓存大型数据,如果需要缓存大型数据,可以考虑使用其他缓存机制,比如 Memcached 或 Redis。
- 清理过期数据: 虽然 Transient 会自动过期,但如果缓存数据量过大,可能会占用大量数据库空间。 可以定期清理过期的 Transient 数据。 可以使用 WordPress 的 WP-Cron 功能来定期执行清理任务。
更高级的用法:对象缓存 (Object Cache)
Transient 实际上是一种比较基础的缓存机制。 WordPress 还提供了更高级的对象缓存机制,可以将数据存储在内存中,而不是存储在数据库中。 对象缓存可以显著提高网站性能,尤其是在高流量的网站上。
常见的对象缓存方案包括:
- Memcached: 一个高性能的分布式内存对象缓存系统。
- Redis: 一个开源的内存数据结构存储系统,可以用作数据库、缓存和消息代理。
使用对象缓存需要安装相应的 WordPress 插件,比如 Memcached Object Cache 或 Redis Object Cache。 安装插件后,WordPress 会自动使用对象缓存来存储 Transient 数据和其他缓存数据。
总结
Transient 是 WordPress 中一种简单易用的缓存机制,可以有效地提高网站性能。 通过 get_transient()
和 set_transient()
函数,你可以轻松地缓存那些“一会儿有用,一会儿没用”的数据。 但是,Transient 也有一些缺点,比如存储在数据库中,不适合缓存大型数据。 在实际开发中,需要根据具体情况选择合适的缓存方案。 如果你的网站流量较大,可以考虑使用更高级的对象缓存机制,比如 Memcached 或 Redis。
希望今天的讲解对大家有所帮助! 感谢大家的收看! 下次有机会再跟大家分享更多 WordPress 的技术知识。