关于nikbobo

Nikbobo,本名刘永强,记忆空间站长,男,1998 年出生于广东茂名,至今(2020 年)21 岁,目前(2020 年)就读于广州大学华软软件学院,常以“nikbobo”这个网名混迹互联网。如无特殊注明,Nikbobo 在本站发表的文章,遵循 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议。详情请参阅关于页面的作者介绍。

回到了 BAE 2.0

很多人说 BAE 3.0 怎么好,怎么方便,对我这种用惯 SDK 的人来说,实在没觉得。

反而觉得 BAE 3.0 各种慢,各种 502,各种讨厌。

也许 BAE 3.0 最大的优势就是所有服务都免费吧,在这里 Cache 要收费,不过你学会优化,那这些问题都不怕了。

搜狐 Send Cloud WordPress 插件:提高 WordPress 邮件送达效率

Send Cloud 是由搜狐武汉中心研发的,提供多种接口来协助开发者和创业者完成邮件的发送与追踪任务的第三方邮件发送服务平台。

Send Cloud 致力于为用户提供便捷的邮件的发送服务,满足用户个性化需求,并提供实时数据分析,从技术上有效防止垃圾邮件的误判,使邮件能够高效到达,以专业的技术和服务解决用户邮件发送的难题。

Send Cloud 主要特点

  • 提供多种 API 接入方式供您自由选择
  • 配置好 DKIM 签名、SPF、MX 记录保证邮件送达
  • ESP 白名单确认,保证邮件进收件箱而不是垃圾箱
  • 策略化的邮件发送,有效控制发信频率
  • 可配置使用发信域名,并对收信数据进行统计分析
  • 邮件被退原因状态码与中文双重解释,清楚易懂
  • 邮件请求后代码返回及时,明确知晓邮件情况
  • 有发送子账号与标签功能,方便不同邮件数据分类追踪
  • 实时的邮件发送日志,统计发送情况和追踪情况
  • 将各种数据给予多种图表呈现,让数据直观可视化
  • 阶梯计价,灵活的账号升降级方式,有效控制成本
  • 系统提供退订链接,可选在邮件底部生成以追踪邮件订阅状态

搜狐 Send Cloud WordPress 插件

为了方便集成进入 WordPress,我开发了搜狐 Send Cloud WordPress 插件,仅需通过简单的设置,就可以集成 Send Cloud 到 WordPress。

下面就详细说一下我开发的 搜狐 Send Cloud WordPress 插件的使用方法:

  1. 打开 Sendcloud 30000封免费额度优惠链接 ,根据提示注册成功,并通过审核。
  2. 参考 SendCloud 如何配置自有域名 配置好自有域名,然后分别创建用于触发邮、群发邮的子账号。
  3. 填写插件页面内容,保存更改。

注意:

  1. 根据搜狐官方推荐,使用触发邮替代 WordPress 自带邮件系统;另开专页,可手动使用群发邮群发邮件。
  2. 由于使用的是 Web API,所以需要服务器(虚拟主机)支持并开启了 CURL,否则无法正常使用插件。
  3. 目前未实现检查输入是否正确功能,请自行检查输入是否正确,由不正确使用造成的后果自负。
  4. 请留意每一个选项的解释和页面最底部的解释,注意其中的细节问题,也许很多问题,就是你不注重细节产生的。

下载:搜狐 Send Cloud WordPress 插件

BAE 3.0 上的 WordPress(五):巧用 BCS 缓存,让速度飞

BAE 3.0 上的 WordPress(四):新的缓存设想 中,我提到了使用 BCS 缓存,目前我就已经实现了!

但是,问题还是有的,就是:有时候,文章发布、评论发表要自己手动更新缓存,无法自动更新。

很简单,在根目录创建一个 index-with-bcs.php,写入:

<?php

// 模式设置
$debug = 1; //开关 DeBug 模式(0:Off;1:On)
$display_cached_by_bcs = 1;  //显示 Cache By BCS(0:Off;1:On)

$start = microtime(); // 计算页面加载时间

// BAE 获取真实 IP
$RealIP = str_replace(", ", "", str_replace(getenv('REMOTE_ADDR'), "", getenv('HTTP_X_FORWARDED_FOR')));
if (false === getenv('HTTP_X_FORWARDED_FOR')) {
	$_SERVER['REMOTE_ADDR'] = $_SERVER['REMOTE_ADDR'];
} else {
	$_SERVER['REMOTE_ADDR'] = $RealIP;
}

define('WP_USE_THEMES', true); // 不这样,主题就没了。

require_once ('bcs.class.php'); // 载入 BCS SDK
$cache_bucket = 'bucket'; // 缓存所用的 Bucket,建议使用“私有”模式和单独 Bucket,不要混用
$cache_ak = 'AK'; // API Key,BAE 3.0 环境需要手动填写,2.0 可用 getenv('HTTP_BAE_ENV_AK')
$cache_sk = 'SK'; // Scret Key,BAE 3.0 环境需要手动填写,2.0 可用 getenv('HTTP_BAE_ENV_SK')
$baidu_bcs = new BaiduBCS($cache_ak, $cache_sk);

$refresh_key = 'r'; // 单页缓存刷新 Key,默认 r,刷新方式就是访问你要刷新的页面 + ?r=y,强烈建议更改此选项
$clean_key = 'c'; // 缓存清空 Key,默认 c,刷新方式就是访问站点任意页面 + ?c=y,强烈建议更改此选项

$url = "http://".$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
$url = str_replace('?' . $refresh_key . '=y', '', $url);
$url = str_replace('?' . $clean_key . '=y', '', $url);
$key = md5($url);
$object = '/' . md5($url);

// 检查 WordPress 是否处于登录状态下
$cookie = var_export($_COOKIE, true);
$loggedin = preg_match("/wordpress_logged_in/", $cookie);

// 如果已经缓存,且为游客访问(非登陆状态下),就直接加载缓存内容
if ($baidu_bcs->is_object_exist($cache_bucket, $object) && !$loggedin && !strpos($url, '/feed/') && !isset($_GET[$refresh_key]) && !isset($_GET[$clean_key])) {

	echo $baidu_bcs->get_object($cache_bucket, $object)->body;
	$cached = 1;
	$msg = '<!-- Page cached -->';
// 如果检测到刷新指令,就清空该页缓存
} elseif ($_GET[$refresh_key] == 'y') {

	require('./wp-blog-header.php');
	$baidu_bcs->delete_object($cache_bucket, $object);
	$msg = '<!-- Page cache deleted -->';

// 如果检测到清空指令,就清空所有缓存
} elseif ($_GET[$clean_key] == 'y') {

	require('./wp-blog-header.php');
	$list_opt = array('start' => 0);
	$list_cache = json_decode($baidu_bcs->list_object($cache_bucket, $list_opt)->body, true);
	$i = 0;
	while($i < 1000){
		$object= $list_cache["object_list"][$i]["object"];
		$baidu_bcs->delete_object($cache_bucket, $object);
		$i++;
	}
	$msg = '<!-- Cache flushed -->';
// 如果登录的情况下,就不缓存
} elseif ($loggedin) {

	require('./wp-blog-header.php');
	$msg = '<!-- Page not cached -->';
// 否则,缓存该页面
} else {

	ob_start();

	require('./wp-blog-header.php');

	$html = ob_get_contents();

	ob_end_clean();
	echo $html;

	// 如果是 404 页面 或 搜索结果 页面就不缓存
	if (!is_404() && !is_search()) {
		// 缓存 7 天,但不知过期是否会自动处理
		$cache_opt = array('header' => array(
			'Expires' => 'modify plus 7 days',
			'Content-Type' => 'text/plain'
			)
		);
		$baidu_bcs->create_object_by_content($cache_bucket, $object, $html, $cache_opt);
		$msg = '<!-- Page is cached -->';
	}
}

$end = microtime(); // 得到加载完成时间

if ($cached && $display_cached_by_bcs) {
	echo "\n" . '<!-- Dynamic page generated in ' . t_exec($start, $end) . ' seconds -->' . "\n";
	echo '<!-- Cached page generated by Nikbobo BCS Cache -->' . "\n";
	echo '<!-- BCS Cache: ' . $key . ' -->' . "\n";
}

// 如果 DeBug 模式开启,就显示 DeBug 信息
if ($debug) {
    echo "\n" . $msg . "\n";
}

// 时间比对
function t_exec($start, $end) {
	$t = (getmicrotime($end) - getmicrotime($start));
	return round($t,5);
}

// 获取页面加载时间
function getmicrotime($t) {
	list($usec, $sec) = explode(" ",$t);
	return ((float)$usec + (float)$sec);
}

?>

就可以了。

但是,记住 BAE 3.0 服务的 SDK 需要开发者自己安装到环境中,所以自己下载 BCS SDK 扔到根目录。

我们还要做的是修改 App.Conf 将伪静态修改到 index-with-bcs.php,具体步骤是:

编辑 App.Conf,将:

 - url : /
    script : index.php
  - url : (.*)
    script : /index.php

替换为:

 - url : /
    script : index-with-bcs.php
  - url : (.*)
    script : /index-with-bcs.php

然后,为了尽可能避免发布文章不自动更新、发表评论不自动更新,我们需要在主题的 Functions.php 中添加如下代码:

/*
 * BCS 缓存清空 (当发布文章、评论时)
 */
function bcs_cache_refresh() {
	global $batcache;

	$post = get_post($post_id);
	if ( $post->post_type == 'revision' || get_post_status($post_id) != 'publish' )
		return;

	bcs_cache_refresh_url( get_option('home') );
	bcs_cache_refresh_url( trailingslashit( get_option('home') ) );
	bcs_cache_refresh_url( get_permalink($post_id) );
}
add_action( 'wp_trash_post', 'bcs_cache_refresh', 0 );
add_action( 'publish_post', 'bcs_cache_refresh', 0 );
add_action( 'edit_post', 'bcs_cache_refresh', 0 ); // leaving a comment called edit_post
add_action( 'delete_post', 'bcs_cache_refresh', 0 );
add_action( 'publish_phone', 'bcs_cache_refresh', 0 );
add_action( 'clean_post_cache', 'bcs_cache_refresh' );

function bcs_cache_refresh_url($url) {
	if ( empty( $url ) )
		return false;
	require_once ('bcs.class.php');
	$cache_bucket = 'Bucket'; // 缓存所用的 Bucket,建议使用“私有”模式和单独 Bucket,不要混用
	$cache_ak = 'AK'; // API Key,BAE 3.0 环境需要手动填写,2.0 可用 getenv('HTTP_BAE_ENV_AK')
	$cache_sk = 'SK'; // Scret Key,BAE 3.0 环境需要手动填写,2.0 可用 getenv('HTTP_BAE_ENV_SK')
	$baidu_bcs = new BaiduBCS($cache_ak, $cache_sk);
	$cache_object = '/' . md5( $url );
	$baidu_bcs->delete_object($cache_bucket, $cache_object);
	return true;
}

话说,SiteMap.XML 改一改,也可以缓存一下。敬请期待。

有人问这个有什么用,不是有 Cache 么?但是我想说,等到 Cache 收费时,用处就来了。

Twenty Twelve 主题添加 WordPress PageNavi 教程

Twenty Twelve 作为 WordPress 官方主题,小清新。但是,有一点我不喜欢,没有 Page Navi,而是采用传统方式。这也许是为了保证响应式设计不错位。但是,其实可以微调一下,我拿我有限的设备测试了一下,貌似不错位。

1)修改主题 Style.CSS,加入:

.page-numbers {
	padding: 0 5px 5px;
}

2)修改主题的 Functions.PHP,加入:

<?php
function nikbobo_pagenavi(){
	global $wp_query;   

	$big = 999999999; // need an unlikely integer

	$pagination = array(
		'base' => str_replace( $big, '%#%', get_pagenum_link( $big ) ),
		'format' => '',
		'total' => $wp_query->max_num_pages,
		'current' => max( 1, get_query_var('paged') ),
		'prev_text' => '<span class="meta-nav">←</span>',
		'next_text' => '<span class="meta-nav">→</span>',
		'show_all'     => False,
		'end_size'     => 1,
		'mid_size'     => 1
	);

	echo paginate_links($pagination); 
}
?>

3)在主题的 Functions.php,查找:

function twentytwelve_content_nav( $html_id ) {
	global $wp_query;

	$html_id = esc_attr( $html_id );

	if ( $wp_query->max_num_pages > 1 ) : ?>
		<nav id="<?php echo $html_id; ?>" class="navigation" role="navigation">
			<h3 class="assistive-text"><?php _e( 'Post navigation', 'twentytwelve' ); ?></h3>
			<div class="nav-previous"><?php next_posts_link( __( '<span class="meta-nav">←</span> Older posts', 'twentytwelve' ) ); ?></div>
			<div class="nav-next"><?php previous_posts_link( __( 'Newer posts <span class="meta-nav">→</span>', 'twentytwelve' ) ); ?></div>
		</nav><!-- #<?php echo $html_id; ?> .navigation -->
	<?php endif;
}

替换为:

function twentytwelve_content_nav( $html_id ) {
	global $wp_query;

	$html_id = esc_attr( $html_id );

	if ( $wp_query->max_num_pages > 1 ) : ?>
		<nav id="<?php echo $html_id; ?>" class="navigation" role="navigation">
			<h3 class="assistive-text"><?php _e( 'Post navigation', 'twentytwelve' ); ?></h3>
			<?php nikbobo_pagenavi(); ?>
		</nav><!-- #<?php echo $html_id; ?> .navigation -->
	<?php endif;
}

大功告成!

有没有发现?这其实和我的 ProwerV6 主题添加 WordPress PageNavi 教程是差不多的,唯一的区别就是 CSS。

话说,Twenty Thirteen 的 CSS,我也弄出来了,有时间再发吧!

BAE 发邮件需要更改 WordPress 核心代码?你 Out 了!

网络上 80% 的教程告诉我们 BAE 发邮件需要更改核心代码,其实不然,你只要把代码丢在 Functions.php 就行了:

if ( !function_exists( 'wp_mail' ) ) :
function wp_mail( $to, $subject, $message, $headers = '', $attachments = array() ) {
	$queue = '云消息名称';
	require_once ('Bcms.class.php');
	$bcms = new Bcms () ;
	$form = '发件人地址';
	$ret = $bcms->mail ( $queue, '<!--HTML-->' . $message, array( $to ), array( Bcms::FROM=>$from_email, Bcms::MAIL_SUBJECT => $subject) ) ;
	if ( false === $ret ) {
		return false;
	} else {
		return true;
	}
}
endif;

因为 WordPress 是强大的。

用 BAE 是否有助于百度收录?

忽然查了一下自己的收录数据,原来近乎为 0 的站点,不知不觉有了收录。
闲来无事,再查查监控,百度蜘蛛勤快的爬。
但戏剧性的是,开始勤快爬的那一天真好是我搬回 BAE 的那一天。
就引发了一个设想:用 BAE 是否有助于百度收录?
猜测的答案:BAE 跟百度友好
再观察几天吧,也有可能是我这段时间更新较为频繁,被蜘蛛看上了。

该文章发自手机

WordPress 子主题制作出现 Cannot redeclare 错误的原因及解决方法

前段时间,我尝试以子主题的模式制作了一个主题。原以为制作起来 So Easy,可是,却没有想象的那么容易。首当其冲的是函数替换问题。

WordPress 官方给出的替换方法

<?php
if (!function_exists('theme_special_nav')) {
    function theme_special_nav() {
        //  Do something.
    }
}
?>

事实上却没有那么简单。有些函数替换会报 Cannot redeclare 错误,怎么回事呢?

出错的原因

大家翻开支持子主题的主题的 Functions.php(这句话真绕口),会发现,那些可以替换的函数,在 Functions.php 中也是使用:

<?php
if (!function_exists('theme_special_nav')) {
    function theme_special_nav() {
        //  Do something.
    }
}
?>

写的,这样就是可以替换的原因,然而那些不能替换的函数呢?一般就是这样的写法:

<?php
function theme_special_nav() {
    //  Do something.
}
add_action('xxx', 'theme_special_nav');
?>

那么不能替换的函数怎么替换呢?其实也可以巧妙解决的。本文就以 theme_special_nav 这个函数为例,说一下怎么解决的。

解决方法

假设父主题的 Functions.php 是这样写的:

<?php
function theme_special_nav() {
    //  Do something.
}
add_action('xxx', 'theme_special_nav');
?>

其实已经隐含着替换方法了,我们可以在子主题加入以下代码:

<?php
function remove_parent_theme_hook() {
	remove_action('xxx', 'theme_special_nav');
}
add_action('after_setup_theme', 'remove_parent_theme_hook');
function nikbobo_theme_special_nav() {
	// Do Something.
}
add_action('xxx', 'theme_special_nav');
?>

原理就是利用 WordPress 的 Hook 机制,在 after_setup_theme 即主题设置完成这个时机删除掉原来的 Hook,然后添加自己的 Hook,为什么一定要在 after_setup_theme 这个时机呢?直接一句:

<?php
remove_action('xxx', 'theme_special_nav');
?>

不就行了么?

大家有无留意到,WordPress 的子主题介绍上有一句话:

其实它会在父主题文件加载之前先载入

也就意味着,你 remove_action 的时候,这个 Hook 还没有建立,结果你自然知道了。

升级到 WordPress 3.7

手动升级到了 WordPress 3.7,感觉貌似不错。唯一不满意的就是 BAE 的多灾多难,安全与方便就如同鱼和熊掌,两者不可兼得。

升级也没有像 BAE 官方的 WordPress For BAE 3.0 教程那样容易,不知是不是我的问题。还要费心去跟,看看升级升级了什么。

Twenty Twelve 也升级了,跟进官方版本。

有时间会写写教程,目前暂定:

  • Twenty Twelve 免插件添加 PageNavi,包含 CSS
  • WordPress 如何完全手动升级(应对 BAE 特殊情况)

BAE 3.0 上的 WordPress(四):新的缓存设想

刚刚在 V2EX 看到有人问为什么 BAE 免费 Redis 没人用来做缓存,不免费 Cache,大家却都想用。

这不仅仅是因为 Cache 方便移植,而是 BAE 干了一件好事:

目前每条 redis 命令的有效长度最大为2048个字节。

2048 字节是什么情况?2K,这缓存,如果确实要用,必须分片写入、分片读写,那样的话,查询量也少不了多少,效率我个人感觉和直接不用缓存差不多。
知道这种情况,我也有心查了一下 Cache:

Key 最大长度:180Bytes
Value 最大长度:1MB

所以性能自然不如自己弄的 Memcached,但是架不住 BAE 的服务器好,所以还是比我们自己的快!

由此,我突然产生了一个新想法:利用 BCS 云存储也是 Key->Value 的特性,换存到云存储。目前我已经在尝试了。。

BAE 版 WordPress Gravatar 头像缓存

因为近期 Gravatar 头像加载速度又慢如蜗牛,所以需要缓存一下。百度了一下,没有 BAE 版的,于是参照 SAE 版的,自己写了个。

什么是Gravatar?

Gravatar 是 Globally Recognized Avatar 的缩写,是 Gravatar 推出的一项服务,意为“全球通用头像”。如果在 Gravatar 的服务器上放置了你自己的头像,那么在任何支持 Gravatar 的站点上留言时,只要提供你与这个头像关联的 E-Mail 地址,就能够显示出你的Gravatar头像来。

为什么要缓存Gravatar头像?

因为 Gravatar 的服务器在国外,在国内访问使用该功能的网站或博客,可能因为载入速度过慢而导致页面一直在载入中甚至头像无法显示,这是一个非常糟糕的用户体验。将头像缓存下来,定时更新,而头像图片的文件一般不会很大,不需要暂用很大的服务器空间,因此缓存 Gravatar 头像是一个加快网站访问速度的高性价比选择。

如何在 BAE 中缓存Gravatar头像?

很简单,在你的主题的 Function.php 文件中加入以下代码:

<?php
function nikbobo_get_avatar_cache ( $avatar ) {
	if( $_SERVER['PHP_SELF'] == '/wp-admin/options-discussion.php' )
		return $avatar;
	$bucket = '这里更改为你的Bucket名称';
	$cache_dir = '/avatar/';
	$baidu_bcs = new BaiduBCS();
	$tmp = strpos( $avatar, 'avatar/' ) + 7;
	$avatar_id = substr( $avatar, $tmp, strpos( $avatar, '?' ) - $tmp );
	$tmp = strpos( $avatar, 'avatar/' ) + 7;
	$pattern = "/(<img.* src=\')([^\']*)(\'.*)/";
	$avatar_url = preg_replace( $pattern, "$2", $avatar );
	$avatar_url = str_replace( "&", "&", $avatar_url );
	$avatar_file = $cache_dir . $avatar_id . '.png';
	if( ! $baidu_bcs->is_object_exist( $bucket, $avatar_file ) ) {
		$content = @file_get_contents( $avatar_url );
		if( ! $content )
			return $avatar;

		$opt['acl'] = "public-read";
		$opt['headers']['Expires'] = 'access plus 15 days';
		$opt['headers']['Content-Type'] = 'image/png';

		$result = $baidu_bcs->create_object_by_content( $bucket, $avatar_file, $content, $opt );
	}
	$avatar_cache_url = 'http://bcs.duapp.com/' . $bucket . $avatar_file;
	$avatar = preg_replace( $pattern, "$1" . $avatar_cache_url . "$3", $avatar );
	return $avatar;
}

add_filter('get_avatar', 'nikbobo_get_avatar_cache');
?>

以上代码在 BAE 环境中测试通过,无法确定能否用于非 BAE 环境。

大功告成!