这个标签云页面虽然很漂亮,但是生成起来却非常麻烦。首先需要获取到网站所有的标签,然后再循环输出标签,循环到每个标签的时候需要单独去查询这个标签的最新文章,也就是页面所展示的每一个标签都需要经历一次单独的 SQL 查询。

标签比较少的时候还好说,但日后内容慢慢变多以后,生成这个标签云集需要大量的时间,而且也要耗费非常多的服务器资源。

这时候可以把标签云集的内容缓存起来。生成好 HTML 代码之后先保存到 Transients 里,然后再输出,下次再加载时直接从 Transients 里获取 HTML 代码,只需要一条甚至零条 SQL 就能获取到整个标签云集的代码:


//获取标签云集
function Bing_page_tags(){
    if( ( $cache = get_transient( 'page_tags_list' ) ) !== false ) return $cache;//如果有 Transients 缓存则直接返回
    //如果没有缓存则开始生成 HTML 代码
    $code = '';
    if( $tags = get_tags( 'orderby=count&order=DESC' ) ){
        foreach( $tags as $tag ){
            $code .= '<li class="tag-box">';
                $post = current( get_posts( array(
                    'tag_id'         => $tag->term_id,
                    'posts_per_page' => 1
                ) ) );
                $code .= "<p class='tag-name'>$tag->name</p>";
                $code .= sprintf( '<a href="%s">%s</a>', esc_url( get_permalink( $post ) ), get_the_title( $post ) );
            $code .= '</li>';
        }
        $code .= "<ul id='tags_list'>$code</ul>";
    }
    //建立 Transients 缓存并返回代码
    set_transient( 'page_tags_list', $code, DAY_IN_SECONDS );//缓存有效 24 小时
    return $code;
}

上面的代码用来生成标签云集的 HTML 代码,并且缓存 24 小时(DAY_IN_SECONDS 为时间常量,表示 24 小时的秒数),如果存在缓存则直接返回,不用再次生成,避免大量 SQL 查询。

不过仅仅是上边的代码还是不够完美的,虽然一次缓存最多只会存在 24 小时,但是这期间修改标签或文章却无法立即显示,所以还需要在特定事件清除缓存:


//清除标签云集缓存
function Bing_clear_page_tags_cache(){
    delete_transient( 'page_tags_list' );//删除 Transients 缓存
}
add_action( 'save_post', 'Bing_clear_page_tags_cache' );//创建和编辑文章
add_action( 'deleted_post', 'Bing_clear_page_tags_cache' );//删除文章
add_action( 'created_post_tag', 'Bing_clear_page_tags_cache' );//创建标签
add_action( 'edited_post_tag', 'Bing_clear_page_tags_cache' );//编辑标签
add_action( 'delete_post_tag', 'Bing_clear_page_tags_cache' );//删除标签

缓存标签云集的用法主要适用于将需要耗费大量时间和服务器资源来生成的内容缓存到 Transients 里,下一次调用时从 Transients 里获取,大大节省时间和服务器资源。

缓存天气

Transients 还经常被用作缓存从网络 API 获取的内容,比如赛事排行榜和天气预报等等。因为服务器发送网络链接需要耗费一定的时间,如果每次加载页面都请求一次,会大大拖慢加载速度,这时就可以缓存到 Transients 里,每隔一段时间再重新请求一次。

下面的例子从中国气象局的 API 获取北京现在的天气,并缓存下来:


//获取北京现在的天气
function Bing_weather_Beijing(){
    if( ( $cache = get_transient( 'weather_Beijing' ) ) !== false ) return $cache;//先从缓存获取,如果有则直接返回
 
    //从 API 获取数据
    $api = 'http://www.weather.com.cn/adat/cityinfo/101010100.html';
    $response = wp_remote_get( $api );
    if( is_wp_error( $response ) || wp_remote_retrieve_response_code( $response ) !== 200 ) return;
 
    //解析数据
    $result = json_decode( wp_remote_retrieve_body( $response ) );
    if( empty( $result->weatherinfo ) ) return;
 
    //生成数据
    $weather = "北京现在 {$result->weatherinfo->temp1} 到 {$result->weatherinfo->temp2},{$result->weatherinfo->weather}。\n";
    $weather .= '最后更新:' . $result->weatherinfo->ptime;
 
    //缓存数据并返回
    set_transient( 'weather_Beijing', $weather, HOUR_IN_SECONDS );//缓存一小时,到期重新获取
    return $weather;
}

因为天气并不是实时更新,所以在 Transients 里缓存一小时,缓存过期之后再去重新获取数据。

邮件登录

除了能缓存之外,Transients 还能保存一些有时间限制的 Key、授权代码之类的信息。

下面这个例子是用发送邮件的形式登录账号。原理是提交邮箱时生成一个用于登录账号的 Key,通过这个 Key 就能登录使用该邮箱的账号,下面只展示生成和校验 Key 部分的代码:


//生成邮件登录 key
function Bing_set_mail_login_key( $user_id ){
    $user_id = (int) $user_id;
    if( !( $user = get_userdata( $user_id ) ) ) return;
    $key = md5( wp_generate_password() );//生成 Key
    $result = set_transient( 'mail_login_key_user_' . $user_id, $key, MINUTE_IN_SECONDS * 15 );//保存 Key;15 分钟后过期无效
    if( $result ) return $key;//保存成功则返回 Key,用于发邮件
}
 
//使用邮件 key 登录
function Bing_mail_key_login( $user_id, $key ){
    $user_id = (int) $user_id;
    $user = get_userdata( $user_id );
    if( !$user || empty( $key ) ) return;
    $transient_key = get_transient( 'mail_login_key_user_' . $user_id );//获取保存的 Key
    if( $transient_key === false ) wp_die( 'Key 无效或者已过期' );//Key 无效或者已过期
    if( $transient_key == $key ){
        delete_transient( 'mail_login_key_user_' . $user_id );//Key 有效,登录之前先删除 Key
        //登录并跳转到后台
        wp_set_current_user( $user_id, $user->user_login );
        wp_set_auth_cookie( $user_id );
        do_action( 'wp_login', $user->user_login );
        wp_safe_redirect( admin_url() );
        die;
    }
}

上面的代码有两个函数,一个用来生成并保存登录 Key,一个用来校验 Key 和登录,其中 Key 保存在可以设置期限的 Transients 里,非常方便。

其它

关于临时选项(Transients)就介绍到之类,结尾最后强调一下 Transients 的特点:

Transients 并不是永久储存,它的数据可能随时消失,即使还没有到期;但是在到期之后它不会再继续“存在”
Transients 可能存储在数据库里,也可能存储在外部对象缓存里
如果需要缓存一些很轻松就能获取到的内容,请不要使用 Transients,而是缓存
Transients 会在一些时候自动消失(例如数据库升级和启用外部对象缓存),所以不用担心它会永远留在服务器中,成为垃圾
Transients 的名字是独一无二的