话说鄙人超级喜欢跟风,谁写出一部技巧集,我就能山寨再加以修改写出第二部
。把 Gravatar 头像缓存到本地服务器是很多博客会做的事情,那是因为国内糟糕的网路环境访问 Gravatar 网站特别慢,某些地区甚至与 Gravatar 绝缘。
不少人用的是 Willin Kan 的缓存 Gravatar 头像之方法,我昨天在本地调试主题的时候加上了这段代码,但发现了一个比较严重的问题:评论列表要等很长的时间才能显示,甚至直接 30 秒超时。原因是原代码中这样一句(第 13 行):
copy($g, $e);
copy 函数起到了把 Gravatar 头像复制到本地的作用,此时 PHP 解释器只能等待复制完成才能继续运行,反而成为拖慢速度的隐患。
本文适用于以下情况:
- 使用国内服务器但想缓存 Gravatar 头像;
- Gravatar 服务器出现问题时。
国外服务器由于访问 Gravatar 服务器很快,因此不会有明显的延迟感,但当以上情况时延迟就非常明显了。
该怎么解决这个问题呢?某个 Gravatar 头像第一次被显示时,实际上仍然是调用 Gravatar 服务器的资源,与此同时博客的服务器再从 Gravatar 服务器将头像下载下来。博客服务器下载头像的过程对用户访问是没有任何意义的,应该将 copy 过程放在另一个线程执行,评论列表输出不受影响,问题就解决了。
PHP 本身是不支持多线程的,我们可以将下载头像的代码写在一个单独的 PHP 文件中,然后由处理评论列表的函数发起一个到此文件的请求,并立即关闭连接,继续向下执行。被请求的文件进行下载工作就相当于异步执行了。
下面给出具体实行方法。
一、建立异步执行的 PHP 文件
在 WordPress 根目录或者其它文件夹新建 gravatar.php(也可以改成您喜欢的名字),内容如下:
if ( $_SERVER['REQUEST_METHOD'] != 'POST' ) {
header( 'Allow: POST' );
header( 'HTTP/1.1 405 Method Not Allowed' );
header( 'Content-Type: text/plain' );
exit;
}
$key = '123456'; // $key 为防止恶意提交的验证码
if ( !isset( $_POST['hash'], $_POST['s'], $_POST['key'] ) || $_POST['key'] != $key ) {
header( 'HTTP/1.1 400 Bad Request' );
header( 'Content-Type: text/plain' );
exit;
}
$hash = $_POST['hash'];
if ( !ctype_alnum( $hash ) || strlen( $hash ) != 32 )
return;
$s = strtoupper($_POST['s']);
if ( !ctype_digit( $s ) ) return;
$r = isset( $_POST['r'] ) ? $_POST['r'] : '';
$rating = array( '', 'G', 'PG', 'R', 'X' );
if ( !in_array( $r, $rating ) )
return;
if ( defined( 'ABSPATH' ) )
require_once( ABSPATH . 'wp-load.php' );
else
require_once( './wp-load.php' ); // 要用相对路径指向 WordPress 根目录,如果放在主题目录下,则改为 ../../../wp-load.php
$d = get_bloginfo( 'wpurl' ) . '/wp-content/avatar/default.png'; // 缺省头像的位置
$gravatarpath = "http://www.gravatar.com/avatar/";
$gravatarpath .= $hash;
$gravatarpath .= '?s=' . $s;
$gravatarpath .= '&d=' . urlencode( $d );
if ( !empty( $r ) )
$gravatarpath .= '&r=' . $r;
$cachepath = ABSPATH . 'wp-content/avatar/' . $hash; // 缓存头像保存的位置
copy( $gravatarpath, $cachepath );
if ( filesize( $cachepath ) < 500) copy( $d, $cachepath );
几点说明:
- 第 8 行中 $key 为防止恶意提交的验证码,请改成不容易破解的字符组合,也不要告诉他人;
- 如果文件不是放在根目录,需要修改第 24 行的相对路径,距离根目录多少层就打入多少
../,主题目录为/wp-content/themes/theme,共有 3 层则为../../../wp-load.php; - 没有为缓存的头像指定扩展名,因为头像可能有多种格式。实际上也不需要扩展名。
二、建立 Gravatar 头像存放的文件夹
根据刚才设定的缓存头像保存位置新建文件夹,例如代码中将 Gravatar 头像存放在 wp-content/avatar 文件夹下,因为我认为 wp-content 文件夹是用来存放用户生成内容的。
请将此文件夹的权限(chmod)设定为 755。
将无 Gravatar 头像时显示的缺省头像放在新建的文件夹中,文件名要与上面代码中“缺省头像位置”一行一致。
三、写入 functions.php
打开主题目录下的 functions.php,在 <?php 与 ?> 之间插入以下代码,如果你已经用了 Willin Kan 的代码,请替换:
function my_avatar( $email, $size = '40', $default = '', $alt = false ) {
$alt = ( false === $alt ) ? '' : esc_attr( $alt );
$hash = md5( strtolower( $email ) );
$wpurl = get_bloginfo( 'wpurl' );
$out = $wpurl . '/wp-content/avatar/' . $hash; // 缓存头像的 URL
$cachepath = ABSPATH . 'wp-content/avatar/' . $hash; // 缓存头像位于硬盘的位置
$t = 1209600; // 设定 14 天, 单位: 秒
$default = $wpurl . '/wp-content/avatar/default.png'; // 缺省头像的位置
if ( !is_file( $cachepath ) || ( time() - filemtime( $cachepath ) ) > $t ){ // 当头像不存在或文件超过 14 天才更新
$rating = get_option( 'avatar_rating' );
$gravatarpath = "http://www.gravatar.com/avatar/$hash?s=$size&d=$default&r=$rating";
$key = '123456'; // $key 为刚刚用于异步下载的 PHP 文件中设定的验证码
$fp = fsockopen( $_SERVER['HTTP_HOST'], 80, &$errno, &$errstr, 5 ); // 建立 Socket 连接
if ( $fp ) {
$encoded = "hash={$hash}&s={$size}&r={$rating}&key=" . urlencode( $key );
fputs( $fp, "POST ".get_bloginfo('wpurl')."/wp-content/themes/beam/gravatar.php HTTP/1.0\n" ); // 用于异步下载的 PHP 文件 URL
fputs( $fp, "Host: ".$_SERVER['HTTP_HOST']."\n" );
fputs( $fp, "Content-type: application/x-www-form-urlencoded\n" );
fputs( $fp, "Content-length: " . strlen($encoded)."\n" );
fputs( $fp, "Connection: close\n\n" );
fputs( $fp, "$encoded\n" );
fclose( $fp );
}
$out = esc_attr( $gravatarpath );
}
$avatar = "<img title='{$alt}' alt='{$alt}' src='{$out}' class='avatar avatar-{$size} photo' height='{$size}' width='{$size}' />";
return apply_filters( 'my_avatar', $avatar, $email, $size, $default, $alt );
}
几点说明:
- 请将 $key 改为与第一步中指定的验证码一致,否则程序不能工作;
- 该函数调用方式与 WordPress 自带 get_avatar 已不相同;
- get_avatar 语法:
get_avatar( $id_or_email, $size = '<size>', $default = '<path_to_url>' , $alt = '<alt>' );
my_avatar 语法:
my_avatar( $comment->comment_author_email, $size = '<size>', $default = '<path_to_url>' , $alt = '<alt>' );
您也可以将 WordPress 自带 get_avatar 复制并改造以保持原有功能;
- 按照以上变更的调用方式,将所有主题模板中出现的 get_avatar() 修改为 my_avatar(),“大概是 functions.php,comments.php,sidebar.php,comments-ajax.php 会有头像的地方有 get_avatar() 函数。”
四、测试
如果把文件放在与本文给出的位置不一样的地方,要修改的地方会比较多,最好在本地测试完成后上传到服务器。
P.s. 我知道代码花眼了点……想办法找个好用的代码高亮插件



你的也不能显示头像呐。。。。这个方法思路不错
囧…因为我也懒得缓存了。
[...] 异步执行的 Gravatar 缓存 » 转载请注明来源 » IM路人 » 博客开始缓存 Gravatar 头像» [...]
[...] 但如果你是国内主机会很杯具,Ray Chow给出了对于国内空间的另一个解决办法 异步执行的 Gravatar 缓存 [...]
[...] 另国内主机可参考:异步执行的 Gravatar 缓存 2010.10.17 13:39:11 / 标签: gravatar,wordpress相关,头像 / 分类: 学习WP Bein [...]
[...] 改获取地址很方便,但是要去动原程序,一升级就完蛋了。我承认我是一个懒人,能不动原程序坚决不动,所以我采用的是缓存法,只折腾主题一劳永逸。搜一下发现最流行的缓存法是 Willin Kan 的 缓存 Gravatar 头像方法,看着代码中的 copy($g, $e) 发呆不爽了半天,万一 gravatar 抽抽了怎么办?难道就一直和他扯皮么。又埋头继续努力发现了 光线志 的 异步执行的 Gravatar 缓存。 [...]
虽然我也用了Willin Kan的头像缓存方法,但是我还没遇到过等待30秒的情况。如果说copy()函数将图片下载至本地耗费时间,你可以预先读取数据库中所有留言者的Email,执行一遍my_avatar(),这样原先的评论者头像就一次性下载至本地了,不会再执行copy()函数了,也给访客节约了一点时间。
不知道你有没有比较过Willin Kan和你的方法,在时间效率上有何差异吗?
因为在本地调试的时候我是遇到了这问题,头像死活刷不出来,结果评论就一直 loading,这也可能是我们当地网络环境不太好的关系。你也是使用国内的服务器,可以试试删掉一个评论页面的所有缓存头像,看看一次性全部下载需要多长时间。
Willin Kan 的方法实际上是从万戈当初那方法修改来的,原来是异步的,只是被他改成了同步的……
這做法是沒錯, 因為國內情況特殊, 使用國內服務器可以加個雙線程的方法. 但也不能說我自創的方法是改自那個方法啊. 你應該仔細看過那文件, 兩個方法完全不一樣.
我不使用異步的原因是, request 已有時間限制, 連不上 gravatar 的話會自動掉線, 然後 copy 默認頭像, 再次刷新是默認頭像, 已緩存, 所以慢只有第一次, 不是每次都慢. 下次更新頭像是 14 天之後, 而且每個頭像更新時間是錯開的, 這慢的程度應該可接受吧, 總比直接連 gravatar 快多了. 而且一半博客都往國外跑, 我想他們並不需要.
感謝你能修改我程式不足的地方, 學習就是要這樣, 能舉一返三才能發輝最大功效. 思考過後, 才是你自己的財富, 繼續加油, 盼能奉獻更多代碼.
我剛發現你也用了我的 comment-ajax, 那個 loading 可改淺色的 wpspin_light.gif
歡迎大師光臨。因為之前這一系列改來改去各式各樣的緩存看到眼花繚亂可能弄錯對象,說錯了別介意唷。
因為我是在我的電腦中試驗,第一次請求肯定很多頭像要下載,結果評論一直空白不顯示,還以為是程式當掉了,後來才弄清楚是真的是卡在 copy 了,所以才有了加線程的想法~
國外的主機的確是不需要,開頭也說明了喔。
我的头像文件缓存后就成99了,不晓得你这个能不能解决这个问题。
99 是什么意思?这只是改成了异步,本质上仍是远端复制。
用了Willin的代码,99权限的文件就是此文件不归任何人,ftp不能做任何操作,要到主机账号里才能改回来,每到更新头像的期限14天就得提前这么做,不然页面就会被error慢慢占据。
我对 Linux 了解不深,但你确定有 99 权限这一说法?权限应该是属于创建者 www 之类的用户吧,我刚刚在 FTP 里试过了,是可以修改的,你试着用 chown 改变所有者呢?
不好意思,不懂代码这些,是99所有者/组。FTP能改权限,但99这样的文件改不了权限。
是不是你的主机设定有误啊……按说创建文件后所有者就应该是你啦!
如果真的是“99”所有者,不是你的话,如果是虚拟主机估计也没权限改所有者的。
以前卸载有些外来插件的时候会出现一些99文件,在主机账户用 fix_ownership 按钮是可以改回来之后,我可以删掉。可现在是willin这个“插件”正用着,头像文件却成了99。我想可能是.htacess有问题,但自己不懂,都是网上找来的。问题不是很致命的,将就这样吧。