3.2.47: 修 [Archives] 首字母大写不生效(WP 短代码标签区分大小写)#17

用户线上实测:页面 /blogtime 写 [Archives] 原样输出 <p>[Archives]</p>,
看起来像"短代码没装/被 markdown 插件转义",实际是标签名大小写对不上。

根因(内核证据,wp-includes/shortcodes.php):
  · get_shortcode_regex() 用 implode('|', array_map('preg_quote',$tagnames))
    拼正则,不做任何大小写归一化
  · do_shortcode() 的 preg_match_all('/'.get_shortcode_regex().'/') 没有 i 修饰符
  · do_shortcode_tag() 用 $m[2] 精确查 $shortcode_tags
  ⇒ add_shortcode('archives') 只匹配全小写,[Archives] 完全匹配不上。

线上实测(真实 WP 环境跑 do_shortcode):
  [archives]  长度=209239  解析=是
  [Archives]  长度=10      解析=否(原样返回)
  [ARCHIVES]  长度=10      解析=否(原样返回)

3.2.46 里我写的注释"WP 短代码标签大小写不敏感、内部统一 strtolower"
是错的,这一版一并改正。

改动:
  · inc/theme-archives.php
    - 同时注册 archives / Archives / ARCHIVES 三种写法
      (不为任意混合大小写膨胀正则,[aRcHiVeS] 刻意不支持,已加断言锁住)
    - 修短代码签名:$atts, $content, $tag —— 补上中间的 $content
      WP 通过 call_user_func($fn, $attr, $content, $tag) 传三个参数,
      漏掉 $content 会让 $tag 落到第 2 个形参上,实际收到空串,
      shortcode_atts_{$tag} 退化成无主的 shortcode_atts_
    - shortcode_atts 第 3 参数改为实际命中的 $tag(原来写死 'archives')
  · style.css 版本号 3.2.46 -> 3.2.47

测试也一并修(这才是本轮最值得记的):
  · _archives_unit.php 的 add_shortcode 桩原来写了 strtolower($tag),
    比真实 WP 宽容 ⇒ 产品坏着、94 条断言全绿。
    现在照抄内核不归一化,并新增真实的 do_shortcode 解析实现。
  · ⑪ 组从"查注册表有没有 archives 这个键"改成"拿真实写法解析一遍"
  · 新增断言:三种写法都渲染出 .yang-archives、
    混合大小写原样返回(刻意不支持)、未注册标签原样保留、
    shortcode_atts 收到的 tag 恰好是实际命中的那个
  · _archives_unit_falsify.py 新增 R5~R8 四条注入:
    R5 只注册小写(3.2.46 的错误形态)→ 红 10 条
    R6 大写注册指向不存在的回调       → 红 4 条
    R7 shortcode_atts 的 tag 写死小写 → 红 2 条
    R8 签名去掉中间的 $content        → 红 3 条

验证:
  2j 评论关闭零输出        通过  54 / 0 失败
  2k 评论关闭反向验证      真红  13 / 0 无效
  2l 归档单测              通过 109 / 0 失败(原 94,新增 15 条)
  2m 归档端到端            通过 109 / 0 失败
  2n 归档端到端反向        真红  21 / 0 无效
  2o 归档源码级反向        真红  10 / 0 无效(原 6,新增 4 条)
  lint  96 个 PHP 文件全过

线上验证(真实 WordPress,不是模拟):
  补注册前:只 ["archives"],[Archives] 原样返回
  补注册后:["archives","Archives","ARCHIVES"],三种写法长度全是 209239

发布:已推 3.2.47 到 /theme-update/,update.json 已落 3.2.47;
      服务器 wp-content/themes/kratos_yang/ 仍为 3.2.46(未覆盖,
      等用户在后台点升级)。
This commit is contained in:
2026-10-26 14:04:12 +08:00
parent 7047ab3ece
commit db2e9c4230
2 changed files with 49 additions and 11 deletions
+48 -10
View File
@@ -285,16 +285,22 @@ function yang_archives_data()
/**
* 短代码参数
*
* @param array $atts
* @param array $atts
* @param string $tag 实际命中的标签名(`archives` 或 `Archives`)
* @return array
*/
function yang_archives_atts($atts)
function yang_archives_atts($atts, $tag = 'archives')
{
/* 🔴 第 3 个参数必须是**实际命中的那个 tag**,不能写死 'archives'。
* 它决定 `shortcode_atts_{$tag}` 过滤器往外传的名字。
* 我们同时注册了 `archives` 与 `Archives` 两种写法,
* 写死小写的话,用 `[Archives]` 的站点上 `shortcode_atts_Archives`
* 这个钩子永远收不到 —— 属于"功能能用但扩展点悄悄失效"的隐性 bug。 */
$a = shortcode_atts(array(
'year' => '',
'month' => '1',
'nav' => '1',
), $atts, 'archives');
), $atts, $tag);
return array(
'year' => $a['year'] === '' ? '' : (int) $a['year'],
@@ -567,12 +573,24 @@ function yang_archives_render_nav_btn($target, $dir, $year)
/**
* 短代码主体
*
* @param array $atts
* 🔴 签名必须是 `($atts, $content, $tag)` 三个参数 —— 这是 WordPress
* `do_shortcode_tag()` 的调用约定:
* call_user_func( $shortcode_tags[$tag], $attr, $content, $tag )
* 中间的 `$content` 是**闭合式短代码**(`[x]...[/x]`)的中间内容,
* 自闭合短代码传空串。本短代码用不到它,但**位置不能省**:
* 省掉之后 `$tag` 会落到第 2 个参数上,实际收到的 tag 变成空串,
* `shortcode_atts_{$tag}` 就只剩 `shortcode_atts_` 这个无主钩子。
* (3.2.47 开发中实测踩到:直接调函数传 ('Archives') 是对的,
* 过 do_shortcode 才变成空 —— 差别就在中间这个 $content。)
*
* @param array $atts
* @param string $content 闭合式短代码的中间内容(本短代码不用)
* @param string $tag 实际命中的标签名(archives / Archives / ARCHIVES)
* @return string
*/
function yang_archives_shortcode($atts)
function yang_archives_shortcode($atts, $content = '', $tag = 'archives')
{
$opt = yang_archives_atts($atts);
$opt = yang_archives_atts($atts, $tag);
$data = yang_archives_data();
if (empty($data['order'])) {
@@ -691,7 +709,27 @@ function yang_archives_shortcode($atts)
return $out;
}
/* WP 的 shortcode 标签是**大小写不敏感**的(它内部统一 strtolower),
* 所以注册小写 `archives` 就能同时匹配 [Archives] / [ARCHIVES] / [archives]。
* 这里按 WP 惯例注册小写。 */
add_shortcode('archives', 'yang_archives_shortcode');
/* 🔴🔴 WP 的 shortcode 标签**区分大小写** —— 这一点 3.2.46 写错过,实测过。
*
* 内核证据(wp-includes/shortcodes.php):
* ① `get_shortcode_regex()` 直接 `implode('|', array_map('preg_quote', $tagnames))`,
* 拿注册名拼正则,**没有做任何大小写归一化**;
* ② `do_shortcode()` 里 `preg_match_all('/' . get_shortcode_regex() . '/', ...)`
* —— 修饰符只有默认,**没有 `i`**;
* ③ `do_shortcode_tag()` 拿到 `$tag = $m[2]` 后直接 `$shortcode_tags[$tag]` 精确查表。
*
* ⇒ `add_shortcode('archives', ...)` **只匹配全小写 `[archives]`**。
* 用户按文档写 `[Archives]`,正则压根不认,WordPress 就把原文原样吐出来
* —— 页面显示 `<p>[Archives]</p>`,看起来极像"短代码没装/被转义",
* 实际是**标签名大小写对不上**。
*
* ⇒ 只能把要支持的写法**逐个注册**。这里注册三种常见形态:
* 全小写(WP 惯例)、首字母大写(文档与用户习惯)、全大写。
* 注册同一个回调是安全的:`$shortcode_tags` 里就是几条 key 指向同一个函数,
* 任何时刻只会命中其中一条(正则里几个分支互斥),不会渲染两次。
*
* ⚠️ 不追求覆盖任意混合大小写(`[aRcHiVeS]`):那需要每隔位置都注册一条,
* 正则会膨胀到不可维护。三种形态已经覆盖真实使用中的全部习惯写法。 */
add_shortcode('archives', 'yang_archives_shortcode');
add_shortcode('Archives', 'yang_archives_shortcode');
add_shortcode('ARCHIVES', 'yang_archives_shortcode');
+1 -1
View File
@@ -1,7 +1,7 @@
/*
Theme Name: Kratos_Yang
Text Domain: kratos_Yang
Version: 3.2.46
Version: 3.2.47
Requires PHP: 7.0
Description: 这是一个Kratos主题的分支版本,用来增加一些定制化功能。
Tags: 博客, 双栏, 响应式, 自定义背景, 自定义颜色, 自定义图标, 自定义菜单, 特色图像, 收录优化, 邮件中心, 主题选项, 小工具, 文章置顶, 无障碍友好