WP DEVELOP

EP128. “核心功能实现:字数统计与预估阅读时间”

首页 WordPress 开发课程 插件开发 CH1:PHP 基础 · EP128
约 11 分钟· #EP128#插件开发 CH1:PHP 基础
🔒 登录后可标记已读

把之前搭好的设置页面真正用起来:给博客文章正文挂一个 the_content 过滤器,只在「文章详情页 + 主查询 + 至少勾选了一项统计」时才触发;具体拼 HTML 的逻辑拆到独立的 createHTML() 方法里,依次判断字数、字符数、预估阅读时间三个开关,各自拼接对应文字,最后根据「显示位置」设置决定是拼在正文前面还是后面。字数只算一次、字数和阅读时间共用同一个变量,避免重复计算。


涉及文件

  • wp-content/plugins/our-first-unique-plugin/our-first-unique-plugin.php (修改)

代码实现

__construct():新增 the_content 过滤器

add_filter('the_content', array($this, 'ifWrap'));

ifWrap():判断是否需要处理,是则转给 createHTML()

function ifWrap($content) {
  if (is_main_query() AND is_single() AND
  (
    get_option('wcp_wordcount', '1') OR
    get_option('wcp_charactercount', '1') OR
    get_option('wcp_readtime', '1')
  )) {
    return $this->createHTML($content);
  }
  return $content;
}

createHTML():真正拼出统计信息 HTML,按设置决定拼在正文前还是后

function createHTML($content) {
  $html = '<h3>' . esc_html(get_option('wcp_headline', 'Post Statistics')) . '</h3><p>';

  // get word count once because both wordcount and read time will need it.
  if (get_option('wcp_wordcount', '1') OR get_option('wcp_readtime', '1')) {
    $wordCount = str_word_count(strip_tags($content));
  }

  if (get_option('wcp_wordcount', '1')) {
    $html .= 'This post has ' . $wordCount . ' words.<br>';
  }

  if (get_option('wcp_charactercount', '1')) {
    $html .= 'This post has ' . strlen(strip_tags($content)) . ' characters.<br>';
  }

  if (get_option('wcp_readtime', '1')) {
    $html .= 'This post will take about ' . round($wordCount/225) . ' minute(s) to read.<br>';
  }

  $html .= '</p>';

  if (get_option('wcp_location', '0') == '0') {
    return $html . $content;
  }
  return $content . $html;
}

关键改动点:

  • ifWrap() 判断条件三部分都要满足:is_main_query()(排除侧边栏等次要循环)、is_single()(只在文章详情页生效,is_page() 只匹配 Page 类型,is_single() 才是匹配 Post 详情页的正确函数)、以及「三个开关至少有一个是开的」——get_option() 传第二个参数作为默认值,插件刚安装、用户还没保存过设置时,也能按预期表现(默认全部开启)
  • 判断分支的具体处理逻辑刻意拆到独立的 createHTML($content) 方法里,而不是全塞进 ifWrap() 一个函数——保持每个方法只负责一件事,ifWrap 只管「要不要处理」,createHTML 只管「怎么处理」
  • $wordCount 只计算一次、存进变量复用:字数统计和阅读时间预估都需要用到词数,str_word_count(strip_tags($content)) 是有实际开销的运算(尤其文章很长时),没必要算两遍——用一个 if 提前判断「只要字数统计或阅读时间任一开启,就算一次」,避免在只勾了字符统计时做无意义的字数运算
  • strip_tags($content) 在统计前先去掉 HTML 标签,确保统计的是纯文字内容,不会把 <p><strong> 这类标签本身也算进字数/字符数里
  • str_word_count() 统计词数,strlen() 统计字符长度,都是 PHP 内建函数,不需要自己写字符串遍历逻辑
  • 阅读时间用「平均每分钟阅读 225 词」这个经验值粗略估算:round($wordCount / 225)round() 取整避免出现「2.7 分钟」这种不自然的小数结果
  • 最终决定拼接顺序:get_option('wcp_location', '0') == '0' 为真就是「拼在正文前面」($html . $content),否则拼在后面($content . $html)——对应后台设置里「Beginning of post」/「End of post」两个选项
  • 标题文字输出前用 esc_html() 转义——即使是从后台设置里读出来的、理论上只有管理员能改的值,依然坚持「凡是动态内容输出到前台页面就要转义」的安全习惯,防止万一数据库被篡改注入恶意脚本

[截图:前台博客文章详情页,正文前/后出现的 Post Statistics 字数/字符数/预估阅读时间统计区块]


Hook / Function 速查

名称类型用途
is_single()WP 内建 function判断当前是否为 Post(或其他非 Page/attachment 的普通文章类型)的详情页
str_word_count($string)PHP 内建 function统计字符串中的单词数量
strlen($string)PHP 内建 function统计字符串长度(字符数)
strip_tags($string)PHP 内建 function去除字符串中的 HTML/PHP 标签
round($number)PHP 内建 function四舍五入取整

常见坑

  • 统计字数/字符数前不先 strip_tags()——会把 HTML 标签本身的字符也算进统计结果,导致数字偏大且不准确
  • 字数和阅读时间各自独立计算一遍 str_word_count()——没必要的重复运算,尤其文章内容很长时会有实际的性能损耗,应该只算一次存进变量复用
  • get_option() 不传默认值——插件刚装上、用户还没主动保存过任何设置时,数据库里对应的选项行根本不存在,不传默认值会导致返回 false,一系列判断逻辑的结果会不符合预期(比如新装用户看不到任何统计信息,而不是符合预期的「默认全部显示」)

延伸 / 后续讲座会用到

第一个插件(字数统计)到此完工。下一讲会暂停功能开发,专门讲「怎么让插件方便被翻译成其他语言(国际化/本地化)」,再往后开始做第二个插件——敏感词过滤器。


Sources

Udemy:

  • Become a WordPress Developer: Unlocking Power With Code — Section 23, EP128