图一是去年栽的平枝栒子,特点是果子大,心型的叶子入秋后变红。
春季·栒子·开花

这盆栒子今天刚刚开花,初花时间比去年提前了一天。
春季·阳光·栒子·开花

珍珠黄杨,买花得的赠品,买的花没活赠品却活下来了。今年是来我这的第五个年头了,今年第一次开花结果,之前以为它不会开花呢。
春季·阳光·珍珠黄杨

小草不知其名,繁殖能力极强,去年首次栽种就泛滥了,大规模清除后,仅剩此两棵,差点被灭族。
春季·阳光·花盆·小草·苔藓·石头

木香,香味很淡。
春季·木香·花朵·绿叶

WordPress save_post 这个钩子有三个参数分别是:$post_ID$post$update,本篇对第三个参数 $update 总结一下。

$update 的定义

$update 是一个布尔型参数,它要么是 true,要么是 false,WordPress 开发文档对 $update 参数的定义是:

"Whether this is an existing post being updated."

字面上理解理解这句话:是否是对现有日志的更新。

上述定义带有一定的歧义和误导,事实上在日志编辑界面,并不是点更新按钮 $update 就是 true,点保存或发布按钮 $update 就是 false 。 WordPress 是根据编辑界面的标题或内容字段(仅限这两个字段)较上一次保存的版本是否发生变更来确定 $update 其值的:不管点什么按钮,只要标题或内容这两个字段有变更,$update 就是 false,否则,如果未发生变更,不管点什么按钮,点多少次,$update 的值都是 true 。

有趣的是当 $update 为 false 时,会生成修订版本,否则不生成修订版本。因此,推断 $update 这个字段可能是用来判断是否生成修订版本的。

简单一句话总结 $update 参数: 标题或内容无变动时, 点操作按钮(保存草稿/发布/更新)$update 为 true; 反之 $update 为 false 。

save_post 的触发时机

关于 save_post 这个钩子在什么情况下会触发,WordPress 开发文档给的解释如下:

save_post is an action triggered whenever a post or page is created or updated, which could be from an import, post/page edit form, xmlrpc, or post by email

大意是当我们通过编辑界面、 xmlrpc(如 wordpress APP)、 email 、导入工具,创建或更新日志或页面时,就会触发 save_post 钩子。

通过实验总结,以上解释也不是很具体。以发布一篇日志为例,从创建到保存、发布、更新,期间会多次触发 save_post 这个钩子,在此总结一下(可能不全,甚至有误)。

  1. 点 “写文章”,生成 $post_id,并立即触发 save_post 钩子,$update 值是 false
  2. 撰写新文章界面,不填任何内容, 直接点保存草稿/发布,均不触发 save_post, 发布不会成功
  3. 自动保存不触发 save_post
  4. 除以上情况,手动点保存草稿/发布/更新,都将触发 save_post

总结

如果希望借助 save_post 钩子做很细的控制操作,仅仅凭借 $update 一个参数是不够的,可能还需要通过 wp_get_post_parent_id() 借助 $post_ID 判断是否为修订版本,甚至是 save_post 第二个参数 $post 中的 post_datepost_statuspost_modified 来辅助。

wp_get_post_parent_id()会返回当前日志的父 ID,如果当前日志是修订版本,wp_get_post_parent_id() 返回它所附属日志的 post_id(大于 0);否则,返回 0 。下面结合编辑界面的按钮动作将 $update 和 wp_get_post_parent_id() 的对应值做一下总结,简短起见且将 wp_get_post_parent_id() 的返回结果命名为 parent_id

有变更 动作 $update parent_id
/ 新建 false 0
保存 true 0
发布 true 0
更新 true 0
保存 false >0
发布 false >0
更新 false >0

一直想将自己在用的主题分享出来,只因有一些私人信息不好处理,一直作罢。最近一段时间,将主题里相关的配置信息剥离出来,统一放到配置文件中,使得分享得以实现,等于现在我只需要维护一个私有的配置文件即可。现在将主题托管在了 GitHub 上,点这里直接下载。

功能介绍

  • 响应式布局;
  • 自动暗黑模式;
  • 支持 schemas 标记;
  • 支持部分 web app 特性;
  • 日志和评论支持无限滚动加载;
  • 标准(standard)格式支持视频背景;
  • 状态(status)格式支持喜欢按钮、 支持地理位置坐标;
  • 日志、评论自动记录发布者终端设备信息;
  • 上传附件附加随机字符串的安全机制;
  • 相对完善的垃圾评论抑制机制;
  • 丰富多样的参数化配置;
  • 原生 JavaScript 支持;
  • 支持代码高亮。

前天晚间博客遭受了一波垃圾评论攻击,攻击者大概是看了我之前的一篇介绍在用的防垃圾评论的日志,知道我这里评论文本框的名字叫 little_star,因此哥们一夜一间就制造了几百条垃圾评论。真是出乎意料,我一直觉得没人会对我的无名小站感兴趣,没想到这一天还是来了。

因此,这两天对在用的防垃圾评论机制进行了一些改进,总体思路是生成动态的评论文本框名称。方法大概入如下:
在主题 function.php 文件中加入以下几个函数

// 生成随机字符串
function get_brave_hash($hash_length = NULL) {
	$hash_length = !is_int($hash_length) ? rand(8,14) : abs($hash_length);
	return substr(str_shuffle("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890"), -$hash_length);
}

session_start(); // 开启 session

// 生成评论框名称
function get_comment_text_name() {
	$comment_text_name = 'real_comment';
	$comment_text_name_hash = get_array_key('_SESSION', $comment_text_name);
	if ( isset($comment_text_name_hash) ) {
		// session 里存在时直接返回 $_SESSION 中存储的值
		return $comment_text_name_hash;
	}
	$sep = '_';
	$hash_length = 8; // 8 位长度
	$comment_text_name_hash = $comment_text_name . $sep . get_brave_hash($hash_length);
	// 加入到 session
	$_SESSION[$comment_text_name] = $comment_text_name_hash;
	return $comment_text_name_hash;
}

// 获取数组键值
function get_array_key($array, $key) {
	if (is_string($array)) {
		global $$array;
		return array_key_exists($key, $$array) ? $$array[$key] : null;
	}
	return array_key_exists($key, $array) ? $array[$key] : null;
}

在主题评论文件中(我这里是 comments.php)调用 get_comment_text_name() 函数,生成动态的名称,用作评论文本框名称:

<?php
$comment_text_name = get_comment_text_name();
comment_form( array(
'fields' => array(
'author' => '<p class="commentform"><input type="text" name="author" id="author" class="inp cmt-input" placeholder="昵称 [必填]" aria-required="true" size="30" value="' . esc_attr( $commenter['comment_author'] ) . '"></p>',
'email' => '<p class="commentform"><input type="text" name="email" id="email" class="inp cmt-input" placeholder="邮箱 [必填·保密]" aria-required="true" size="30" value="' . esc_attr( $commenter['comment_author_email'] ) . '"></p>',
'url' => '<p class="commentform"><input type="text" name="url" id="url" class="inp cmt-input" placeholder="网址 [选填]" size="30" value="'.$comment_author_url.'"></p>',
'cookies' => '<p class="comment-form-cookies-consent"><input id="wp-comment-cookies-consent" name="wp-comment-cookies-consent" type="checkbox" value="yes"' . $consent . ' />' .
'<label for="wp-comment-cookies-consent" class="c4"> 记住我的个人信息</label></p>'
),
'comment_field' => '<textarea id="comment" name="comment"></textarea>
<textarea name= ' . '"' .$comment_text_name . '"' . ' class="inp text-bg comment-textarea" placeholder="添加评论..." aria-required="true" cols="45" rows="8"></textarea>',
'title_reply' => '发表评论',
'cancel_reply_link' => '取消回复',
'label_submit' => '发表评论',
'comment_notes_before' => '',
'comment_notes_after' => '',
'class_submit' => 'btn submit',
'action' => '/wp-stop-spam.php'
));
?>

在 wp-stop-spam.php 文件中通过 session 获取评论框名称,并进行置换

<?php
/** Sets up the WordPress Environment. */
require_once __DIR__ . '/wp-load.php';
nocache_headers();

$comment = trim($_POST['comment']);
if ( empty( $comment ) ) {
// 置换评论内容
$comment_text_name = get_comment_text_name();
$_POST['comment'] = trim($_POST[$comment_text_name]);
require_once( __DIR__ . '/wp-comments-post.php' );
}
if ( ! empty( $comment ) ) {
	// 返回错误
	wp_die('放过我吧!');
}

2021年年底买的三苗余蝴蝶,2022年发了几苗并来了两个花苞,临近春节这段时间花苞生长迅速,明显可见其中一个花苞出现了分裂,分出主副两个花苞,几乎可以肯定这是一杆双花,主花苞已经开裂肉眼可见花瓣了。
春兰·余蝴蝶

2022年2月25日,晴,花开了,状态还不错,香味较浓。
春兰·余蝴蝶

2023年3月3日,晴,今年这盆花来了一杆双花的。
春兰·余蝴蝶·一杆双花

2023年3月9日,晴,同一杆上的另一朵花也开了。
春兰·余蝴蝶·一杆双花