在 Docker 里的 WordPress 安装 WP-Markdown,并修复 PHP 8 致命错误的完整记录

这是一篇可直接发布或再加工的排障记录。文中保留了关键命令、输出、报错和修复过程。为了便于复现,我尽量把每一步都写成可执行的操作。

背景

这次的目标很简单:把一个旧插件 WP-Markdown 安装到运行在 Docker 中的 WordPress 里,并完成启用。

插件安装包是 wp-markdown.1.6.1.zip,WordPress 容器使用的是 wordpress:6.7.2,容器里的 PHP 版本是 8.2.28

最初的安装步骤参考的是插件官方说明:

  1. 上传 wp-markdown 文件夹到 WordPress 的 wp-content/plugins/ 目录。
  2. 在后台插件页启用插件。
  3. 到 Settings > Writing 里打开 Markdown 相关选项。

但实际执行后,第二步失败了:插件一启用就触发了 fatal error。

环境信息

  • 主机环境:Linux
  • WordPress 容器:wordpress_ygtb-wordpress_yGTB-1
  • WordPress 镜像:wordpress:6.7.2
  • PHP 版本:8.2.28
  • 插件压缩包:/root/wp-markdown.1.6.1.zip

第一步:确认压缩包和容器状态

先确认插件压缩包确实存在,并找到正在运行的 WordPress 容器。

pwd && ls -la
docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}'

输出中能看到 WordPress 容器:

wordpress_ygtb-wordpress_yGTB-1   wordpress:6.7.2   Up 12 days   0.0.0.0:39005->80/tcp

这一步的结论是:容器在跑,而且可以通过 39005 端口访问 WordPress。

第二步:确认插件压缩包结构

在把文件塞进容器之前,先确认压缩包顶层目录是不是 wp-markdown/

unzip -l /root/wp-markdown.1.6.1.zip | head -n 20

输出显示压缩包的顶层目录确实是:

wp-markdown/
wp-markdown/CHANGELOG.md
wp-markdown/css/
wp-markdown/js/
wp-markdown/wp-markdown.php

这意味着可以直接把整个 wp-markdown 文件夹复制到 WordPress 的插件目录。

第三步:安装插件到 Docker 里的 WordPress

先解压,再用 docker cp 复制到容器的 wp-content/plugins/

rm -rf /tmp/wp-markdown && unzip -q /root/wp-markdown.1.6.1.zip -d /tmp
docker cp /tmp/wp-markdown wordpress_ygtb-wordpress_yGTB-1:/var/www/html/wp-content/plugins/

复制后检查容器内目录:

docker exec wordpress_ygtb-wordpress_yGTB-1 sh -lc 'ls -la /var/www/html/wp-content/plugins/wp-markdown | head -n 20'

能看到这些文件:

wp-markdown.php
markdown-extra.php
CHANGELOG.md
readme.md
css/
js/
languages/

到这一步,插件文件已经进了容器,但还没有真正启用。

第四步:第一次启用失败,出现 fatal error

我尝试直接激活插件,但 WordPress 报了致命错误。

激活时的现象是:

无法启用插件,因为它引起了一个致命错误(fatal error)。

接下来就进入排障阶段。

第五步:先看 PHP 版本,再看错误日志

先确认容器里的 PHP 版本:

docker exec wordpress_ygtb-wordpress_yGTB-1 php -v

结果是:

PHP 8.2.28

这个信息非常关键。因为很多老插件在 PHP 8 下会因为旧语法直接挂掉。

然后我看了插件主文件和错误日志。

docker exec wordpress_ygtb-wordpress_yGTB-1 sh -lc 'php -l /var/www/html/wp-content/plugins/wp-markdown/wp-markdown.php && php -l /var/www/html/wp-content/plugins/wp-markdown/markdown-extra.php'

输出里,主入口文件没有语法问题,但 markdown-extra.php 直接报了 fatal:

Errors parsing /var/www/html/wp-content/plugins/wp-markdown/markdown-extra.php
[31-May-2026 13:27:12 UTC] PHP Fatal error:  Array and string offset access syntax with curly braces is no longer supported in /var/www/html/wp-content/plugins/wp-markdown/markdown-extra.php on line 846

这就是根因。

第六步:定位根因

我用 grep 搜索了 markdown-extra.php 里所有可疑的花括号字符串下标写法。

grep -nE '\\{[0-9]+\\}' /var/www/html/wp-content/plugins/wp-markdown/markdown-extra.php

最开始扫出来的可疑位置很多,包括:

846
1142
1167
1507
1730
1995
2004
2166
2548
2866

这些位置大多是 PHP 5 / PHP 7 时代常见的老写法,比如:

$level = $matches[2]{0} == '=' ? 1 : 2;

而 PHP 8 里,这种写法已经被移除,必须改成方括号:

$level = $matches[2][0] == '=' ? 1 : 2;

第七步:修复策略

我的处理思路不是只修报错行,而是一次性把同类语法都清掉。

原因很直接:

  1. 这类 fatal error 往往不止一个点。
  2. 只修当前行,插件后面很可能在另一个页面或另一段解析逻辑里再次崩溃。
  3. 老插件移植到 PHP 8,最稳妥的办法是先把“语法级问题”一次性清理掉,再验证功能。

第八步:修复代码

我把容器里的插件目录拷回工作区,先在本地文件中修补,再同步回容器。

关键修改是把所有字符串下标的花括号写法改成方括号写法,例如:

$matches[2]{0}   ->  $matches[2][0]
$token{0}        ->  $token[0]
$tag{1}          ->  $tag[1]
$tag{strlen($tag)-2} -> $tag[strlen($tag)-2]

修复覆盖了这些位置:

  • 标题解析
  • 强调/加粗标记处理
  • HTML tag 处理
  • 代码块和内联代码解析
  • 类名处理

这类改动本质上是“兼容新 PHP 版本”,没有改变插件的功能逻辑。

第九步:验证修复是否成功

先在修复后的文件上做 lint,再同步回容器做一次最终验证。

本地文件里已经不再残留这类语法:

grep -nE '\\$[A-Za-z_][A-Za-z0-9_]*\\{[^}]+\\}' /root/wp-markdown-fix/markdown-extra.php

结果为空,说明旧语法已经清理干净。

然后同步回容器,并在容器里 lint:

docker cp /root/wp-markdown-fix/. wordpress_ygtb-wordpress_yGTB-1:/var/www/html/wp-content/plugins/wp-markdown/
docker exec wordpress_ygtb-wordpress_yGTB-1 sh -lc 'php -l /var/www/html/wp-content/plugins/wp-markdown/markdown-extra.php && php -l /var/www/html/wp-content/plugins/wp-markdown/wp-markdown.php'

最终结果:

No syntax errors detected in /var/www/html/wp-content/plugins/wp-markdown/markdown-extra.php
No syntax errors detected in /var/www/html/wp-content/plugins/wp-markdown/wp-markdown.php

第十步:重新激活插件

插件语法修复完成后,再执行激活。

docker exec wordpress_ygtb-wordpress_yGTB-1 sh -lc 'php -r "require \"/var/www/html/wp-load.php\"; include_once ABSPATH . \"wp-admin/includes/plugin.php\"; include_once ABSPATH . \"wp-admin/includes/file.php\"; $result = activate_plugin(\"wp-markdown/wp-markdown.php\"); if (is_wp_error($result)) { fwrite(STDERR, $result->get_error_message() . PHP_EOL); exit(1); } echo \"activated\\n\";"'

返回结果是:

activated

这说明插件已经可以在当前 WordPress + PHP 8.2 环境下正常启用。

顺手发现的非致命警告

激活时还出现了一个警告,但它不是 fatal error,所以不会阻止插件启用:

[31-May-2026 13:34:16 UTC] PHP Warning:  "continue" targeting switch is equivalent to "break". Did you mean to use "continue 2"? in /var/www/html/wp-content/plugins/wp-markdown/markdownify/markdownify.php on line 299

这个警告说明插件内部还有更老的 PHP 代码风格,但它目前不是阻断性问题。

最终结论

WP-Markdown 本身可以安装到 Docker 里的 WordPress,但它的老版本代码不完全兼容 PHP 8.2。真正导致无法启用的原因不是 WordPress 安装过程,而是插件内部的旧语法:

  • Array and string offset access syntax with curly braces is no longer supported

只要把这些旧式字符串下标全部改成方括号形式,插件就能正常加载并激活。

可复现的命令清单

下面把整个过程按顺序整理成一套可复现命令:

# 1. 查看容器
docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}'

# 2. 确认压缩包结构
unzip -l /root/wp-markdown.1.6.1.zip | head -n 20

# 3. 解压并复制到容器
rm -rf /tmp/wp-markdown && unzip -q /root/wp-markdown.1.6.1.zip -d /tmp
docker cp /tmp/wp-markdown wordpress_ygtb-wordpress_yGTB-1:/var/www/html/wp-content/plugins/

# 4. 查看 PHP 版本
docker exec wordpress_ygtb-wordpress_yGTB-1 php -v

# 5. lint 检查
docker exec wordpress_ygtb-wordpress_yGTB-1 sh -lc 'php -l /var/www/html/wp-content/plugins/wp-markdown/wp-markdown.php && php -l /var/www/html/wp-content/plugins/wp-markdown/markdown-extra.php'

# 6. 激活插件
docker exec wordpress_ygtb-wordpress_yGTB-1 sh -lc 'php -r "require \"/var/www/html/wp-load.php\"; include_once ABSPATH . \"wp-admin/includes/plugin.php\"; include_once ABSPATH . \"wp-admin/includes/file.php\"; $result = activate_plugin(\"wp-markdown/wp-markdown.php\"); if (is_wp_error($result)) { fwrite(STDERR, $result->get_error_message() . PHP_EOL); exit(1); } echo \"activated\\n\";"'

适合写在博客里的“排障方法论”

如果你想把这件事写成一篇技术博客,可以把核心思路概括为下面三句话:

  1. 先确认环境,而不是先改代码。PHP 版本决定了很多插件是否还活着。
  2. 遇到 fatal error,优先看日志和 lint,不要凭感觉猜。
  3. 老插件迁移到新 PHP 时,先处理语法级兼容问题,再看功能级问题。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

To create code blocks or other preformatted text, indent by four spaces:

    This will be displayed in a monospaced font. The first four 
    spaces will be stripped off, but all other whitespace
    will be preserved.
    
    Markdown is turned off in code blocks:
     [This is not a link](http://example.com)

To create not a block, but an inline code span, use backticks:

Here is some inline `code`.

For more help see http://daringfireball.net/projects/markdown/syntax