小林静華
小林の小站

WordPress用QQ头像代替没有Gravatar的评论

bookmark_border开发 | access_time2021年4月11日 | library_books1548字

写在前面 · 契机

小站诞生的第二天就收到了好多评论,超开心的。但由于WorPress的评论头像是直接取自邮箱对应的gravatar,在国内不仅gravatar官网上不去,我在开始做网站之前连听说都没听说过,结果就是大家的头像都是默认头像。但在评论的时候大部分都会有填写自己的QQ邮箱,于是便想把大家的QQ头像放在评论中。想的是挺好,做起来蛮麻烦的。

对于刚刚开站的我来说完全没有技术力干啥啥不行,抄袭第一名,只好去百度上搜,最后终于找到了多少算是能看懂的一篇。

当我摸爬滚打了几个小时实装之后,出现了天大的BUG,和并没有那么大但很烦的BUG,总之就是各种问题。毕竟已经是去年的文章了(什么?去年的文章现在就不管用了吗),听上去很离谱,但事实就是这样。于是小林以php零基础仔细看了一遍代码,稍微改动了一下,最终实装在了自己网站上。希望不会再出BUG了。

先上代码

下面的代码改自盒子萌WordPress用QQ头像代替没有Gravatar头像

代码需要被加在主题里的functions.php后,用于重写get_avatar函数。

原代码

<?php
 
//首先要创建一个缓存文件夹ABSPATH . 'avatar/' 
//
function my_get_avatar($avatar, $id_or_email, $size) {
	
	// Get the author's email.
	$email = '';
	if ( is_numeric($id_or_email) ) {
		$id = (int) $id_or_email;
		$user = get_userdata($id);
		if ( $user )
			$email = $user->user_email;
	} elseif ( is_object($id_or_email) ) {
		if ( !empty($id_or_email->user_id) ) {
			$id = (int) $id_or_email->user_id;
			$user = get_userdata($id);
			if ( $user)
				$email = $user->user_email;
		} elseif ( !empty($id_or_email->comment_author_email) ) {
			$email = $id_or_email->comment_author_email;
		}
	} else {
		$email = $id_or_email;
	}
	
	// Get the url of avatar
	$pattern = '/(?=http)[-\w:\/\.?&#;=]+/';
	$url_count = preg_match_all($pattern, $avatar, $original_avatar_url);
	$avatar_size = array();
	$avatar_url = array();
	$pattern = '/(?<=s=)\d+/';
	for ( $i = 0; $i < $url_count; ++$i) {		
		preg_match($pattern, $original_avatar_url[0][$i], $size_array);	
		$avatar_url[$i] = $original_avatar_url[0][$i];
		$avatar_size[$i] = $size_array[0];
	}
	
	// Check if the author has a gravatar.
	$hashkey = md5(strtolower(trim($email)));
	$test_url = 'http://www.gravatar.com/avatar/' . $hashkey . '?d=404';
	$data = wp_cache_get($hashkey);
	if ( false === $data ) {
		$response = wp_remote_head($test_url);
		if( is_wp_error($response) ) {
			$data = 'not200';
		} else {
			$data = $response['response']['code'];
		}
	    wp_cache_set($hashkey, $data, $group = '', $expire = 60*5);
	}
	
	if ( $data != '200' ) {		
		// The author doesn't have a gravatar.
		if ( stripos($email,"@qq.com") ) {
			// If he uses QQ mail, let WordPress use his QQ avatar instead.
			// Set the size of QQ avatar.
			for ( $i = 0; $i < $url_count; ++$i) {					
				if ( $avatar_size[$i] <= 100 ) $qq_avatar_size = 100;
				elseif ( $size <= 140 ) $qq_avatar_size = 140;
				elseif ( $size <= 240 ) $qq_avatar_size = 240;
				else $qq_avatar_size = 640;
				// q1.qlogo.cn, q3.qlogo.cn, q4.qlogo.cn also work.
				$avatar_url[$i] = 'http://q2.qlogo.cn/g?b=qq&nk=' . $email . '&s=' . $qq_avatar_size;
			}
		}
	}
 
	// Unfortunately I don't know what encrypt method the QQ avatar interface accepts.
	// So to protect the author's privacy, I have to cache the avatars.
	$wp_url = get_bloginfo('wpurl');
	// Caching.
	for ( $i = 0; $i < $url_count; ++$i) {
		$file_path = ABSPATH . 'avatar/' . $hashkey . '-' . $avatar_size[$i] . '.jpg';
		// 1209600s = 14d, the avatars will be cached for 2 weeks. You can change the period.
		$lifetime = 1209600; 
		if ( !is_file($file_path) || (time() - filemtime($file_path) ) > $lifetime) { 
			// If the file doesn't exist or it has been out of date, then update it.
			// It's necessary to use "-N --no-use-server-timestamps".
			exec("wget -N --no-use-server-timestamps -O '" . $file_path . "' '" . $avatar_url[$i] . "'  > /dev/null &");						
		}
		else $avatar = str_replace($original_avatar_url[0][$i], $wp_url . '/avatar/' . $hashkey . '-' . $avatar_size[$i] . '.jpg', $avatar);
		if ( filesize($file_path) < 500 ) copy($wp_url . '/avatar/default.jpg', $file_path);
	}
	return $avatar;	
}
add_filter( 'get_avatar', 'my_get_avatar', 10, 3);
 
?>

我的代码

<?php

function my_get_avatar($avatar, $id_or_email, $size) {
	
	// Get the author's email.
	$email = '';
	if ( is_numeric($id_or_email) ) {
		$id = (int) $id_or_email;
		$user = get_userdata($id);
		if ( $user )
			$email = $user->user_email;
	} elseif ( is_object($id_or_email) ) {
		if ( !empty($id_or_email->user_id) ) {
			$id = (int) $id_or_email->user_id;
			$user = get_userdata($id);
			if ( $user)
				$email = $user->user_email;
		} elseif ( !empty($id_or_email->comment_author_email) ) {
			$email = $id_or_email->comment_author_email;
		}
	} else {
		$email = $id_or_email;
	}
	
	// Get the url of avatar
	$pattern = '/(?=http)[-\w:\/\.?&#;=]+/';
	$url_count = preg_match_all($pattern, $avatar, $original_avatar_url);
	$avatar_size = array();
	$avatar_url = array();
	$pattern = '/(?<=s=)\d+/';
	for ( $i = 0; $i < $url_count; ++$i) {		
		preg_match($pattern, $original_avatar_url[0][$i], $size_array);	
		$avatar_url[$i] = $original_avatar_url[0][$i];
		$avatar_size[$i] = $size_array[0];
	}
	
	$hashkey = md5(strtolower(trim($email)));
	
	// Check if the author has a QQ number.
	if ( stripos($email,"@qq.com") && is_numeric(explode('@', $email)[0]) ) {
		// q1.qlogo.cn, q3.qlogo.cn, q4.qlogo.cn also work.
		$avatar_url = 'http://q1.qlogo.cn/g?b=qq&nk=' . $email . '&s=140';
		$wp_url = get_bloginfo('wpurl');
		$file_path = ABSPATH . 'avatar/' . $hashkey . '-140' . '.jpg';
		// 1209600s = 14d, the avatars will be cached for 2 weeks. You can change the period.
		$lifetime = 1209600; 
		if ( !is_file($file_path) || (time() - filemtime($file_path) ) > $lifetime) { 
			// If the file doesn't exist or it has been out of date, then update it.
			// It's necessary to use "-N --no-use-server-timestamps".
			exec("wget -N --no-use-server-timestamps -O '" . $file_path . "' '" . $avatar_url . "'  > /dev/null &");						
		}
		for ( $i = 0; $i < $url_count; ++$i) {
		    $avatar = str_replace($original_avatar_url[0][$i], $wp_url . '/avatar/' . $hashkey . '-140' . '.jpg', $avatar);
		}
	    return $avatar;	
	}
	
	// Check if the author has a gravatar.
	$test_url = 'http://www.gravatar.com/avatar/' . $hashkey . '?d=404';
	$data = wp_cache_get($hashkey);
	if ( false === $data ) {
		$response = wp_remote_head($test_url);
		if( is_wp_error($response) ) {
			$data = 'not200';
		} else {
			$data = $response['response']['code'];
		}
	    wp_cache_set($hashkey, $data, $group = '', $expire = 60*5);
	}
	return $avatar;	
}
add_filter( 'get_avatar', 'my_get_avatar', 10, 3);

?>

为什么这么麻烦

用户评论填写的邮箱应该只能由博主看到才对。由于qq的头像服务器并没有怎么加密,只要访问http://q1.qlogo.cn/g?b=qq&nk=你的QQ号&s=640就可以看到头像。如果网站直接向该url发送request大概可以直接用f12抓包下来。或者干脆右键头像在新页面打开,就直接暴露qq号了。

所以需要一种安全的方法,将qq头像先下载到网站的服务器中,赋予文件一个加密的名字保存下来,再把这个文件分发给访客,你就看不出来大家的qq号啦。

这段代码干了什么

原作者的注释写的还是蛮清楚的。

先获取用户的邮箱,检测该邮箱有没有gravatar,如果没有(即对test_url的request的response为200),则检测邮箱是否为QQ邮箱。如果是,则将邮箱对应的QQ头像cache下来,并把地址指向cache的文件。

我改动了什么

首先,现在QQ邮箱甚至有了非QQ号的用户名,这个输进头像服务器是只会拿到胖企鹅的,让我实在头疼,于是曾经在判断条件中加了一个is_numeric(explode('@', $email)[0]),来确定是QQ号没错。

经过测试瞎猜,QQ头像服务器偶尔会随机给你返回胖企鹅,只有140大小和640大小的头像是比较稳定的。于是将原作者根据头像大小划分的100/140/240/640大小改成了只有140大小。对头像文件的保存,原本是每一个大小都会保存一张,我在测试的时候实在是删烦了,最后变成了不管什么大小只保存一张,删除了头像大小的文件名后缀。

在测试的时候,有gravatar的用户的头像总是会离奇消失,反复测试瞎猜之后得到的结论是它们也被指向了一个cache下来的文件,然而我并没有cache来自gravatar的文件,于是我先判断是不是QQ号,剩下的都交给gravatar。

最后面有一行if ( filesize($file_path) < 500 ) copy($wp_url . '/avatar/default.jpg', $file_path);被我给删掉了。这个原本是把空文件替换成一个默认图片的,但WordPress自己本来就有默认的头像,在服务器里留下一堆默认图片也实在不好辨认,最后就给删去了。


等于说给mdx加了个QQ头像

如果技术力高的话甚至还能贡献到开源库里

——Magma

对,就这样。然而我的回复是

没有技术力

php今天刚入门

我能把bug找出来之后再优化一番已经很厉害了好吧

——小林

写blog路还远哟。

小林 静華

文章作者

发表评论

textsms
account_circle
email

小林の小站

WordPress用QQ头像代替没有Gravatar的评论
在评论的时候大家会有填写自己的QQ邮箱,于是便想把大家的QQ头像放在评论中。
扫描二维码继续阅读
2021-04-11