
前阵子在一个老玩家长辈的电脑里翻出他当年做的动漫论坛备份数据库压缩包解出来一看引擎是 phpBB 1.4.4那一刻我脑子里的第一反应就是这东西还能在现代环境里跑起来吗我本来以为把它丢进 Docker 就是几分钟的事结果从拉镜像到安装向导跑通硬生生折腾了一个通宵。这篇东西就是我那晚踩坑排查的全过程也顺便把“把 20 年前的 PHP 论坛跑在 Docker 里”这件看起来冷门、其实很多软件考古/怀旧玩家都用得上的事一次性说透。如果你手里有老论坛数据想还原或者想研究早期 PHP 应用的结构这篇可以直接照着操作。1. 为什么还有人要在Docker里跑20年前的phpBB很多人看到 phpBB 1.4.4 这种版本号会觉得莫名其妙现成的 phpBB 3.3 不香吗新版本安全、功能全、模板好看为什么要折腾一个 2002 年发布的古董但我实际接触下来会搜这种标题的人通常有比较明确的需求不是单纯闲着没事干。最常见的是老数据还原。当年很多个人论坛用的就是 phpBB 1.4.x站长们手里可能还留着当年的 SQL 备份或者整站压缩包。数据是无价的但旧程序跑不起来数据就只能是压缩包里的死文件。把 phpBB 1.4.4 在容器里还原再把备份导入就等于让二十年前的社区“复活”那种成就感不是装个新论坛能比的。其次是插件和皮肤开发调试。phpBB 1.4.x 时代有一批特有风格的主题和 MOD 插件现在很难找到支持它们的现代环境。如果你恰好收藏了当年的经典皮肤想在本地看一眼实际渲染效果就必须让 1.4.4 的原始运行环境重新工作。再就是安全研究和 CTF 靶场搭建。老版本 phpBB 存在大量已公开的漏洞SQL 注入、XSS、验证绕过都有研究早期 Web 漏洞的人经常需要搭建一个有漏洞的 phpBB 环境做分析。这种情况下跑一个“与时俱进的干净新版”反而没有意义要的就是它当年的旧依赖和旧逻辑。Docker 在这个场景里的价值是决定性的。老 PHP 应用最麻烦的不是代码本身而是它依赖的运行时环境——PHP 4/5 的解释器、mysql 扩展、register_globals 开关、特定版本的 MySQL 认证方式这些东西和现在的操作系统、软件源完全脱节。你不可能为了一个怀旧项目去重装一台 2002 年的服务器但你可以用 Docker 把那个年代的运行时完整封装成一个“环境快照”想启动就启动想销毁就销毁完全不污染宿主机。所以这篇记录虽然标题是“Getting phpBB 1.4.4 working in Docker”本质上是在讲一个更通用的问题当生态环境已经面目全非怎么用最小的成本让一个老软件重新跑起来。这个思路可以迁移到 phpBB 2.0.x、老 Discuz、老 WordPress 甚至其他任何老语言运行时上。2. 1.4.4年代的技术栈和现代Docker环境差在哪2.1 PHP层面的断层比想象中大得多phpBB 1.4.4 发布于 PHP 4 当道的年月它的代码风格和老运行环境深度绑定。直接放在现代 PHP 8.x 上跑大概率连页面都打不开。第一个致命伤是mysql_*函数族。phpBB 1.4.4 的数据库操作全部基于mysql_connect()、mysql_query()、mysql_fetch_array()这一系列函数而这些函数在 PHP 7.0 就被正式移除了。PHP 8 里连影子都找不到一调用就是Call to undefined function mysql_connect()。第二个问题是register_globals。这个特性会把 URL 参数、表单提交值直接注册成 PHP 全局变量phpBB 1.4.4 非常多逻辑都依赖这种变量注入机制。而register_globals从 PHP 5.4 开始被彻底移除后面的版本根本没有这个开关。第三个坑是正则函数。老代码里经常能看到ereg()、eregi()、split()这类 POSIX 风格正则函数它们在 PHP 5.3 前后陆续被废弃或挪出默认扩展。到了 PHP 5.6官方镜像里已经默认没有ereg扩展了老应用跑起来会直接报 undefined function。除此之外还有魔术引号magic_quotes_gpc、short_open_tag、$HTTP_POST_VARS这类老超全局变量用法每一项都能成为压垮老应用的最后一根稻草。2.2 数据库平台的代沟也很致命phpBB 1.4.4 当时的数据库组合是 MySQL 3.23 / 4.0而现在 Docker Hub 上默认的mysql:latest是 8.x。中间隔了四五个大版本除了 SQL 语法差异之外最让老 PHP 应用崩溃的是认证协议的改动。MySQL 8.0 默认的caching_sha2_password认证插件PHP 5.6 自带的 mysql 扩展根本不认识连接时会被直接拒绝。另外字符集方面老论坛大量使用 latin1 或者早期 utf8数据库默认排序规则一变中文内容很容易变成乱码。2.3 一个基本判断先选时代镜像不要一上来就改代码我见过不少人拿到老代码的第一反应是“把它改到能跑在现代 PHP 上”——比如写一个 mysql 扩展兼容层、把所有已废弃函数替换掉。这个思路不是不行但它非常耗时而且很容易在改造过程中引入新的逻辑错误。尤其像 phpBB 1.4.4 这种面向过程、全局变量满天飞的代码库兼容层要覆盖的面太广投入产出比很低。更务实的路线是让环境去迁就代码而不是让代码迁就环境。先找到一套尽可能接近当年运行条件的基础镜像把老程序先跑通再考虑要不要做现代化改造。在实际操作中这套“环境适配优先”的策略能省下几倍的时间。3. 基础镜像选型和Dockerfile落地3.1 为什么我选定 php:5.6-apache mysql:5.7phpBB 1.4.4 虽然出生在 PHP 4 年代但实际测试下来 PHP 5.6 是目前“现成镜像里能跑的最低合理版本”。原因有几个PHP 5.6 仍然保留了mysql_*函数族虽然已经标记 deprecated但能用register_globals开关仍然存在于php.ini里php:5.6-apache官方镜像在 Docker Hub 上可以直接拉取不需要自己编译。有人可能想问为什么不直接上 PHP 4 的镜像因为 Docker Hub 官方根本没有维护过 PHP 4 的镜像自己从源码编译 PHP 4 再配 Apache 和 mysql 扩展步骤非常繁琐而且和现代系统库的兼容性也是大坑。PHP 5.6 是一个已经足够接近、又足够省事的折中点。数据库方面选mysql:5.7而不是 8.x主要考虑认证插件。MySQL 5.7 默认还是mysql_native_passwordPHP 5.6 的 mysql 扩展可以直接握手换到 8.0 就得额外加启动参数指定认证插件增加不确定因素。我把几个典型方案对比如下方案能否直接用需要额外工作推荐度php:5.6-apache mysql:5.7基本能跑安装 mysql 扩展、开启 register_globals最推荐php:5.6-apache mysql:8.0连不上数据库MySQL 启动参数加--default-authentication-pluginmysql_native_password可行但没必要PHP 8.x 兼容层大量报错要写 mysql_* 兼容函数、处理正则函数工作量大不推荐自编译 PHP 4 Apache最接近历史环境构建过程复杂镜像体积大极客向不推荐3.2 Dockerfile完整内容与每一行解释项目目录我建议先建一个独立的文件夹把源码和配置文件都放在里面方便整体管理phpbb144-playground/ ├── Dockerfile ├── docker-compose.yml ├── phpbb-1.4.4/ # phpBB 1.4.4 解压后的源码目录Dockerfile 内容如下FROM php:5.6-apache RUN docker-php-ext-install mysql RUN { \ echo register_globals On; \ echo short_open_tag On; \ echo display_errors On; \ echo error_reporting E_ALL ~E_NOTICE ~E_DEPRECATED ~E_STRICT; \ } /usr/local/etc/php/conf.d/phpbb-legacy.ini RUN a2enmod rewrite WORKDIR /var/www/html # 如果你的源码目录就在构建上下文里可以取消下面这行注释 # COPY phpbb-1.4.4/ /var/www/html/第一行基础镜像不用说。第二行非常重要php:5.6-apache默认并没有启用 mysql 扩展虽然镜像里保留了扩展源码但必须通过docker-php-ext-install mysql手动编译启用一次。注意这里装的是mysql而不是mysqli因为 phpBB 1.4.4 只认mysql_*函数族。后面那段RUN是把 PHP 5.6 的关键运行参数写进conf.d下的独立配置文件。register_globals On是整个 phpBB 1.4.4 能正常登录、发帖、管理会话的核心short_open_tag是防止老模板里出现?短标签时被 PHP 当作文本输出display_errors在排查阶段必须开不然后台报错全被吞掉排错无从下手错误级别要屏蔽NOTICE和DEPRECATED否则老代码会产生大量警告刷屏甚至在页面顶部输出非 HTML 内容导致 header 报错。a2enmod rewrite是为了启用 Apache 的 URL 重写模块phpBB 1.4.4 虽然默认用的是带参数的 URL但某些 MOD 和主题会用到.htaccess规则提前开好省得到时候抓瞎。3.3 准备源码目录的一个容易忽略的细节phpBB 1.4.4 的源码包解压之后一般是一个子目录比如phpBB-1.4.4/里面对应结构大致如下phpBB-1.4.4/ ├── admin/ ├── db/ ├── includes/ ├── install/ ├── language/ ├── templates/ ├── files/ ├── config.php └── index.php如果你在 compose 里把整个phpBB-1.4.4目录挂载到容器的/var/www/html那访问/时 Apache 找的是子目录里的index.php而不是 phpBB 自己的index.php。大多数情况下要挂载的是phpBB-1.4.4里面的内容也就是把宿主机源码目录指向容器的 web 根目录。挂载时注意路径别多套一层。老 phpBB 对files/附件目录和config.php的可写性有要求。最省事的方式是在 compose 里给容器指定用户或者直接预先chown目录为www-data。我习惯在宿主机执行一次chown -R www-data:www-data phpbb-1.4.4/这样容器内 Apache 进程写缓存、写附件就都没有权限问题了。这个操作经常被忽略但恰恰是安装向导卡住的常见原因之一。4. 用docker-compose把数据库和论坛串起来4.1 compose文件设计思路最简配置和启动顺序Dockerfile 只负责 PHP 运行环境真正把论坛和数据库连起来还得靠 docker-compose。compose 文件我写得比较朴素没有引入太复杂的编排因为这类老应用不稳定因素已经够多了编排层越简单越容易排查。services: db: image: mysql:5.7 container_name: phpbb144-db command: --character-set-serverutf8 --collation-serverutf8_general_ci environment: MYSQL_ROOT_PASSWORD: root123 MYSQL_DATABASE: phpbb144 MYSQL_USER: phpbb MYSQL_PASSWORD: phpbb123 volumes: - dbdata:/var/lib/mysql healthcheck: test: [CMD, mysqladmin, ping, -h, localhost, -uroot, -proot123] interval: 5s retries: 12 phpbb: build: . container_name: phpbb144-app depends_on: db: condition: service_healthy ports: - 8080:80 volumes: - ./phpbb-1.4.4:/var/www/html volumes: dbdata:数据库启动命令里指定了字符集为utf8排序规则为utf8_general_ci。这一步很关键如果你要导入的中文论坛数据是在老 MySQL 里导出的保持 utf8 字符集会减少大量乱码问题。healthcheck是为了确保 PHP 容器等数据库真正就绪再去连否则会出现“应用先起来数据库还在初始化导致连接失败”的竞态问题。depends_on配合condition: service_healthy是 docker compose 里比较稳妥的启动顺序控制方式。PHP 容器这边用build: .让 compose 使用当前目录的 Dockerfile 构建镜像用端口映射把容器的 80 暴露到宿主机的 8080 上源码通过卷挂载进去这样每次改动宿主机里的 phpBB 文件容器内即时生效不需要反复重建镜像。dbdata数据卷用于持久化 MySQL 数据这样即使容器删除重建论坛数据库也不会丢。4.2 启动后的基础检查步骤在浏览器打开http://localhost:8080之前先做几项检查能省不少事。docker compose up -d --build docker compose psdocker compose ps应该看到两个容器都处于 running 状态。如果phpbb144-app反复重启先看日志docker logs phpbb144-app docker logs phpbb144-db常见的失败场景有两种。一种是 PHP 容器因为docker-php-ext-install编译失败构建不过日志能看到编译错误多半是网络拉取依赖或基础镜像源的问题。另一种是 PHP 容器起来但数据库还没就绪连接超时后进程直接报错退出这种加好healthcheck之后基本不会出现。5. 启动和安装向导的真实报错排查记录5.1 报错一Call to undefined function mysql_connect()这是最经典的一个坑也是我在第一轮启动就撞上的。现象打开http://localhost:8080/install/install.php页面出现Fatal error: Call to undefined function mysql_connect()后面的安装向导完全无法进入。排查思路先进容器看一眼 PHP 到底加载了哪些模块docker exec -it phpbb144-app php -m | grep mysql如果输出为空说明 mysql 扩展确实没装。php:5.6-apache官方镜像为了保持精简虽然带了很多扩展源码但默认编译启用的只是常用部分。mysql 扩展需要显式安装。修复在 Dockerfile 里加上前面那行RUN docker-php-ext-install mysql然后重建容器docker compose build phpbb docker compose up -d重建完再次php -m | grep mysql能看到mysql输出页面上的报错就消失了。这个报错看起来很低级但它告诉我们的道理是老代码对应的运行环境每一项都必须显式搭建任何一项缺失都会让进度归零。5.2 报错二安装向导提示数据库连接失败现象安装向导里填了数据库主机localhost、数据库名phpbb144、用户名phpbb、密码phpbb123点下一步后提示类似“Could not connect to the database server”或“Failed to obtain valid data from the database”。这里要解释一个容器网络关键概念在 PHP 容器内部localhost指向的是 PHP 容器自己不是宿主机更不是 MySQL 容器。phpBB 安装向导如果填localhost它尝试连接的是一个根本不存在、也没有监听 3306 端口的 PHP 容器本地 socket必然失败。修复把数据库主机改成db——也就是 compose 服务名。docker compose 会自动创建一个内部 DNS 网络服务之间通过服务名互相访问。db就是 MySQL 容器的服务名所以正确的数据库服务器地址应该是数据库服务器: db 数据库端口: 3306 数据库名: phpbb144 用户名: phpbb 密码: phpbb123有个容易混淆的点如果你在 macOS 上用 Navicat 等外部客户端连 MySQL宿主机侧的主机地址是localhost、端口是映射出来的3306/13306之类用户、密码、数据库名是一样的。容器内外的连接地址并不相同填错地方就连接失败。5.3 报错三开启register_globals后页面还是行为异常这个坑比较隐蔽比数据库连接失败更能折腾人。现象安装向导能走通论坛首页能打开但登录之后出现各种诡异问题——登录状态保持不住、发帖后跳转不对、管理员后台部分功能失灵。排查链路我一开始怀疑是 session 问题去查 PHP 的session.save_path、cookie配置折腾了一圈没结果。后来在 phpinfo() 页面里看到register_globals显示为Off才意识到我写的配置根本没生效。原因register_globals是PHP_INI_PERDIR级别的指令它只能在php.ini、pdfl或 Apache 配置文件层面设置不能通过ini_set()在运行时打开。更重要的是虽然我把配置写进了conf.d/phpbb-legacy.ini但如果该文件的加载顺序靠前、或者在同一个指令上存在多处配置后面的某个设置仍可能把它覆盖。修复我最后是在 Dockerfile 里直接用一次RUN把配置追加为独立文件并确保没有其他地方覆盖它。同时用 phpinfo() 页面复核docker exec -it phpbb144-app php -i | grep register_globals确认输出是register_globals On之后论坛登录状态和行为就恢复正常了。这个经历也说明了一个排查原则老应用在运行期出现诡异现象时不要只盯着业务逻辑先回头确认运行环境的开关状态——环境变量没生效往往才是根因。5.4 报错四安装完成后一直在安装页打转现象安装向导正常走完提示安装完成但访问论坛首页还是被引导到install/install.php形成一个“装完了又让装”的死循环。原因phpBB 1.4.4 的安装程序在检测到install目录还存在、且安装标记文件没有被删除时会持续认为系统尚未安装完成。老版本安装完成后官方文档要求手动删除或改名install目录现在的新版本一般都自动跳转或生成锁文件但 1.4.4 没有这个机制。修复进入容器把install目录删掉docker exec -it phpbb144-app rm -rf /var/www/html/install删掉之后刷新首页就能看到真正的论坛页面了。这个步骤如果不在本章节写明很多人会卡在“明明装完了却进不去论坛”这一步而且不容易想到问题是出在残留目录上。5.5 一个补充坑ereg 或 split 函数报 undefined function如果你把源码放到 PHP 5.6 后还遇到Call to undefined function ereg()或split()之类的错误说明代码里用到了更早时代的 POSIX 正则函数。phpBB 1.4.4 的某些 MOD 或自定义扩展里比较常见主程序不一定有。对这种问题我的建议是不要强行去改源码只写一个极小的兼容函数垫片在入口文件或一个compat.php里定义if (!function_exists(ereg)) { function ereg($pattern, $subject, $matches null) { return preg_match(/ . $pattern . /, $subject, $matches); } }注意这种垫片只是让程序不崩溃行为不完全等价于老ereg但对大多数简单场景已经够用。如果你在安装引导页就报这个错说明你用的那个改造包比官方 1.4.4 加了更多自定义逻辑这个垫片能帮你撑过启动阶段。6. 安装向导通过之后的验证、数据保留和安全注意事项6.1 完整走一遍安装向导安装向导通过数据库连接验证之后还需要设置管理员账号、论坛名称、站点描述等信息。在老版本 phpBB 里这一步也是向数据库写入基础数据的过程。填好之后论坛根目录会生成config.php里面包含数据库连接参数。这个文件在容器里是被 Apache 的www-data用户读写的权限不对会导致后续任何写操作失败。进入论坛后做几个简单验证首页能显示版块列表中文标题不乱码。注册一个测试账号能收到激活邮件本地环境大概率发不出邮件但注册流程本身要能跑通。发一个测试帖帖子能正常插入数据库并在列表中显示。进入管理员后台能看到插件、风格、会员管理等菜单。如果某个环节报错第一件事看 Apache 错误日志docker exec -it phpbb144-app tail -f /var/log/apache2/error.log这是排查老论坛问题最直接的手段比反复刷新页面瞎猜有效得多。6.2 数据库备份和容器状态保留既然投入一晚上把老环境跑起来了数据备份不能省。导出 MySQL 数据库的命令docker exec phpbb144-db sh -c exec mysqldump -uroot -proot123 --default-character-setutf8 phpbb144 phpbb144-backup.sql注意--default-character-setutf8参数避免导出时把中文转成乱码。备份文件放在宿主机上即使容器全部删除只要备份文件还在随时能用新容器把数据库恢复回来。如果想连整个运行环境都保存下来可以用docker commit把当前容器打包成自定义镜像或者直接保存Dockerfile、docker-compose.yml和phpbb-1.4.4源码目录。这几个文件加起来并不大但它们是“任意时刻都能复现这套老环境”的最小集合建议扔到 Git 仓库里管理。6.3 老论坛的安全边界只建议本地使用这里必须把话说重一点。phpBB 1.4.4 的年代Web 安全还没有形成完整体系SQL 注入、跨站脚本、任意文件上传、会话固定这类漏洞在源码里到处都是。它不是我们常说的“有攻击面”而是“全身都是攻击面”。所以这套环境只适合在本地做怀旧研究、数据还原、安全分析千万不要把它通过公网端口映射暴露到互联网上更不要在上面存任何真实用户数据。即使只用 Docker 端口映射到局域网也要意识到周边设备一旦被攻破这台老论坛就可能变成横向移动的跳板。我通常会在 compose 里把端口映射故意绑到本机回环地址ports: - 127.0.0.1:8080:80这样只有本机能访问局域网其他设备连不上能最大限度降低暴露面。6.4 后续还能怎么扩展如果你把 phpBB 1.4.4 跑通之后还想继续研究其实有不少方向可以扩展。一个方向是数据迁移。既然 1.4.4 环境已经能读取老备份就可以把数据导出为通用格式再导入新版本 phpBB 或其他论坛系统。这个迁移链路比直接删掉旧环境重新建站要有价值得多。另一个方向是运行环境升级实验。在跑通 1.4.4 的基础上你可以尝试把它迁移到 PHP 7.4 或 8.x 上看看需要补哪些兼容层、改哪些代码。这个过程能让你深入理解 PHP 20 年的演进比单纯看文档生动得多。当然这属于进阶玩法前提是先有一个稳定的 1.4.4 运行环境做对照。最后一个很小但很实用的经验折腾这类老软件一定要养成“改一个变量就验证一次”的习惯。老应用的运行链条很长从镜像构建、PHP 扩展、php.ini、数据库字符集、Apache rewrite 到源码目录权限任何一环出问题都会以各种诡异的报错形式呈现。别一次性改七八个配置再重启那样报错出现时你根本不知道罪魁祸首是谁。我那一晚上大部分时间都花在“一次改一个变量、重启、看日志”的循环上虽然慢但每一步都走得踏实。最终能把 phpBB 1.4.4 这个二十年前的老论坛在 Docker 里重新点亮看到那个熟悉的蓝白风格首页在浏览器里加载出来的时候会觉得这一切都值了。