如果你在 WordPress 文章里写过 <pre lang="lua" line="1" escaped="true"> 这样的标签,又在后台插件列表里翻来覆去找不着"代码高亮"插件,那这篇文章就是为你写的。
本文讲清楚四件事:这套标签到底是谁定义的、三个参数各自管什么、怎么给 GeSHi 增加一门它原本不认识的语言(以易语言为例),以及插件在 2025 年下架之后还能不能继续放心用。
一、这套标签不是 WordPress 原生能力
先给一个明确结论:WordPress 核心从来不做代码高亮。
核心挂在 the_content 上的默认过滤器,只有 wptexturize、wpautop、do_blocks、wp_filter_content_tags 这些,没有任何一个环节会去识别代码、给关键字上色。你在前台看到的彩色代码,一定是某个插件或主题在替你干。
那么 lang、line、escaped 这三个属性是哪来的?它们是 GeSHi 系高亮插件自定义的"方言"。GeSHi(Generic Syntax Highlighter)是一个老牌 PHP 语法高亮引擎,phpBB、DokuWiki、MediaWiki 都用过它。下面这三个 WordPress 插件同宗同源,都吃这套写法:
| 插件 | 说明 |
|---|---|
| WP-Syntax | 最早把 GeSHi 带进 WordPress 的插件,已多年未更新 |
| WP-GeSHi-Highlight | 对 WP-Syntax 的重写改进版,参数完全兼容,输出更简洁 |
| Flyn Syntax | 另一个分支,额外支持 Gutenberg 区块 |
一句话记住:你在文章里存的,永远只是 <pre lang="lua">…</pre> 这段原始文本;彩色 HTML 是插件在页面输出时临时生成的。这个区别很重要——它意味着换高亮方案时,你的历史文章一个字都不用改。
二、三个参数逐个说清
1. lang —— 语言名
唯一必需的参数,取值必须是 GeSHi 语言文件的文件名(不含 .php)。常用值如下:
| 你想高亮 | lang 该写 | 备注 |
|---|---|---|
| PHP | php | 纯 PHP 片段;含 HTML 混排用 php-brief 效果一般 |
| JavaScript | javascript | 建议全小写,大写多数版本也能识别 |
| HTML | html4strict | 坑点:写 lang="html" 部分版本不识别,改用 html4strict 或 html5 |
| CSS | css | — |
| C# | csharp | 不能写 C#,# 是非法字符会被过滤掉 |
| Lua | lua | — |
| Python | python | — |
| SQL | sql | — |
| Bash / Shell | bash | — |
| 纯文本(不着色) | text | 只想保留等宽和换行时用 |
2. line —— 起始行号
line="1" 表示显示行号、从第 1 行开始编号。写 line="18" 就从 18 开始。整个属性去掉,则不显示行号。
3. escaped —— 最容易踩坑的一个
这个参数的含义是:告诉插件"我存的这段代码已经被 HTML 实体转义过了,你先反转义再着色"。加上它,插件会对内容执行一次 htmlspecialchars_decode()。
为什么需要它?因为 WordPress 的可视化编辑器会偷偷帮你转义。举个完整例子:
| 你存进数据库的 | 标签写法 | 访客最终看到的 |
|---|---|---|
<div>(实体) |
escaped="true" | <div> ✅ 正确 |
<div>(实体) |
不加 escaped | <div> ❌ 显示成字面实体 |
<div>(裸代码) |
不加 escaped | <div> ✅ 正确 |
看起来"裸代码不加 escaped"也行,但问题是:只要你切过一次可视化模式,TinyMCE 就会把裸代码里的 < 转成 < 存起来,此时再不加 escaped,前台就会显示成一堆 <div>。
本站实践口诀:一律带 escaped="true",代码块内所有 < 写成 <、> 写成 >、& 写成 &。写完全文不切可视化,直接保存。这样无论怎么编辑都不会翻车。
三、怎么知道你的站是谁在着色
如果你接手了一个旧站、或者忘了自己装过什么,最快的方法是看前台 HTML 的"指纹":打开一篇有代码块的文章,F12 审查元素,对照下表:
| 看到的包裹结构 | 着色引擎 |
|---|---|
wp-geshi-highlight-wrap5 → wp-geshi-highlight → div.lua → pre.de1 |
GeSHi(WP-GeSHi-Highlight / WP-Syntax) |
pre.language-lua + code.language-lua + span.token |
Prism.js |
code.hljs + span.hljs-keyword |
highlight.js |
pre.EnlighterJSRAW + data-enlighter-language |
Enlighter |
pre.wp-block-code,里面没有任何彩色 span |
没人在着色,只是等宽字体 + 底色 |
看到 de1、kw1、st0 这类类名,就是 GeSHi 没跑了——这是它独有的 CSS class 体系。
想进一步定位代码在哪,可以在服务器上搜关键字(Windows 用 findstr,Linux 用 grep -r):
cd wp-contentfindstr /s /i /m "geshi" *.php
findstr /s /i /m "geshi" *.css
或者更简单:前台右键"查看网页源代码",搜 geshi,找到那条 <link> 样式表引用,URL 里的目录名就是答案——含 /themes/ 说明是主题内置,含 /plugins/ 说明是插件。
四、易语言怎么办:给 GeSHi 加一门语言
GeSHi 支持 200 多种语言,但没有易语言。好在它是插件式架构,加一门语言只需要往语言目录丢一个 PHP 文件。
第 1 步:找到语言目录
注意是双层 geshi 目录,这是 GeSHi 库的标准结构:
wp-content/plugins/wp-geshi-highlight/geshi/geshi/
├── php.php
├── lua.php
├── csharp.php
├── javascript.php
└── epl.php ← 我们要加的就是它第 2 步:写语言定义文件
文件名决定 lang 的值:epl.php 对应 lang="epl"。下面是精简版骨架(真实文件的关键字表会长得多):
<?php$language_data = array (
'LANG_NAME' => 'Easy Programming Language',
// 易语言行注释:单引号 + 全角 ※'COMMENT_SINGLE' => array( 1 => "'", 2 => '※' ),
'COMMENT_MULTI' => array(),
'CASE_KEYWORDS' => GESHI_CAPS_NO_CHANGE,
'QUOTEMARKS' => array( '"', '“', '”' ),
'ESCAPE_CHAR' => '',
'KEYWORDS' => array(
// 1 组:数据类型与结构1 => array( '双精度小数型', '整数型', '文本型', '逻辑型', '字节集',
'子程序', '程序集', '返回值', '参数', '变量' ),
// 2 组:流程控制(注意:长关键字必须排在短关键字前面)2 => array( '判断循环', '计次循环', '如果真', '循环', '判断',
'如果', '否则', '返回', '结束' ),
// 3 组:常用命令3 => array( '信息框', '到文本', '到数值', '取文本长度', '分割文本',
'读入文件', '写到文件', '调试输出' ),
),
'CASE_SENSITIVE' => array( GESHI_COMMENTS => false, 1 => false,
2 => false, 3 => false ),
'STYLES' => array(
'KEYWORDS' => array(
1 => 'color: #0000ff; font-weight: bold;', // 类型:蓝色加粗
2 => 'color: #af00db; font-weight: bold;', // 流程:紫色加粗
3 => 'color: #267f99;', // 命令:青色
),
'COMMENTS' => array( 1 => 'color: #008000; font-style: italic;',
2 => 'color: #008000; font-style: italic;' ),
'STRINGS' => array( 0 => 'color: #a31515;' ),
'NUMBERS' => array( 0 => 'color: #098658;' ),
),
'URLS' => array( 1 => '', 2 => '', 3 => '' ),
'OOLANG' => false,
'REGEXPS'=> array(),
'STRICT_MODE_APPLIES' => GESHI_NEVER,
'SCRIPT_DELIMITERS' => array(),
'TAB_WIDTH' => 4,
);
第 3 步:三个必踩的坑
① 文件名必须等于 lang 的值。 epl.php ↔ lang="epl",写错一个字母就是纯文本。
② 长关键字必须排在短关键字前面。 这是最容易忽略的一条。GeSHi 用正则顺序匹配,如果"如果"排在"如果真"前面,那么源码里的"如果真"会被切成"如果"+"真"两段。同理"整数型"要排在"长整数型"之后。
③ 文件必须是 UTF-8 无 BOM。 用记事本另存会加 BOM,导致 PHP 报 Cannot modify header information 或语言文件加载失败。用 VS Code / Notepad++ 保存时显式选择"UTF-8 无 BOM"。
第 4 步:清缓存再验证
装了页面缓存(Redis、WP Rocket 等)的站,前台很可能还挂着旧 HTML。清一次全站缓存,再发一段测试代码:
.版本 2
.子程序 _启动子程序, 整数型
信息框 ("你好,易语言", 0, ) ' 这是一行注释
返回 (0)
中文关键字变蓝变紫即成功;整块是纯文本,回去检查文件名和目录层级。
五、插件 2025 年下架了,还能用吗
事实部分
- WP-GeSHi-Highlight 于 2025 年 3 月 18 日被 WordPress.org 关闭,原因是 Security Issue,官方页面已无法下载;
- 最后一个版本是 1.4.3(2023 年 4 月发布),此后不再更新;
- 对应漏洞编号 CVE-2024-13896,类型是正则回溯导致的拒绝服务(ReDoS),CVSS 6.5;
- 触发条件是"拥有作者及以上权限的账号提交恶意构造的代码块内容";
- 底层的 GeSHi 引擎自 2019 年起就已停止维护。
怎么判断自己要不要换
| 你的情况 | 建议 |
|---|---|
| 单管理员、不开放投稿 | 继续用没问题。能提交内容的人只有你自己,漏洞的攻击面不存在 |
| 多作者、开放注册、接受投稿 | 应该换了。任何一个作者账号都能触发 |
| 站点对可用性要求高(怕被打挂) | 建议换 |
关于"停更"本身:停更 ≠ 不能用。这个插件逻辑简单、不依赖外部服务,在 PHP 8.x + 新版 WordPress 上实测仍正常工作。真正需要权衡的只有上面那个 ReDoS,而它对单作者站几乎无意义。
真要换,怎么做到老文章零改动
关键思路:既然数据库里存的是原始标签,那就在 the_content 上挂一个过滤器,把这套老标签在输出时就地翻译成新库能识别的结构。以迁移到 Prism.js 为例:
<?phpadd_filter( 'the_content', function ( $content ) {
if ( stripos( $content, '<pre' ) === false ) return $content;
return preg_replace_callback( '#<pre\b([^>]*)>(.*?)</pre>#si', function ( $m ) {
$attrs = $m[1];
$code = $m[2];
if ( ! preg_match( '/\blang\s*=\s*["\']([^"\']+)["\']/i', $attrs, $lm ) ) {
return $m[0];
}$lang = strtolower( trim( $lm[1] ) );
$alias = array( 'js' => 'javascript', 'html' => 'markup',
'html4strict' => 'markup', 'php-brief' => 'php',
'shell' => 'bash', 'py' => 'python' );
$lang = isset( $alias[$lang] ) ? $alias[$lang] : $lang;
if ( ! preg_match( '/\bescaped\s*=\s*["\']true["\']/i', $attrs ) ) {
$code = htmlspecialchars( $code, ENT_NOQUOTES, 'UTF-8' );
}$class = 'language-' . $lang;
if ( preg_match( '/\bline\s*=\s*["\'](\d+)["\']/i', $attrs, $sm ) ) {
$class .= ' line-numbers';
}return sprintf( '<pre class="%1$s"><code class="language-%2$s">%3$s</code></pre>',
esc_attr( $class ), esc_attr( $lang ), $code );
}, $content );
}, 9 );
几个注意点:优先级必须是 9 或更早,否则 wpautop(优先级 10)会先把换行变成 <br>;切换前记得停用 GeSHi 插件,两套过滤器会互相抢标签;国内站点建议自托管 Prism,别直接用公共 CDN。
迁移的代价要说清楚:GeSHi 是服务端渲染,访客零额外请求;Prism 是浏览器端 JS,会多出 4-5 个请求、约 10-15KB,首屏还有一瞬间无色代码的闪烁。单作者站做这个迁移,性能上是退步的。别为了"用新东西"而迁移。
六、换个配色:自定义 CSS
插件自带一个 wp-geshi-highlight.css。想改样式,不要直接改插件目录里的文件(更新会覆盖),而是:
- 把
wp-geshi-highlight.css复制到当前主题根目录; - 改这个文件——插件发现主题目录里有同名文件,会优先加载主题里的版本;
- 这样即使插件更新,你的自定义样式也不会丢。
常用调整片段:
/* 代码块整体:字号、行高、圆角 */.wp-geshi-highlight pre {
font-size: 14px;
line-height: 1.6;
border-radius: 4px;
padding: 12px 15px;
}/* 允许横向滚动,长代码不撑破布局 */.wp-geshi-highlight {
overflow-x: auto;
}/* 行号列颜色调淡一点 */.wp-geshi-highlight .de1,
.wp-geshi-highlight .li1 { color: #333; }
七、小结
<pre lang="xxx">是 GeSHi 系插件的私有方言,WordPress 核心不认识它;- 三个参数:
lang是语言文件名、line管行号起始、escaped="true"表示内容已转义需先反转义; - 语言名有坑:C# 写
csharp,HTML 建议写html4strict; - 加自定义语言 = 往
geshi/geshi/丢一个以语言命名的 PHP 文件,注意长关键字排前面; - 插件虽已下架且带 ReDoS,但单管理员站点可继续放心使用,服务端渲染反而是性能最优的方案。
如果你也在用易语言写自动化脚本、又习惯用 WordPress 记笔记,希望这篇能帮你把代码块的显示彻底理顺。