简介:php-7.1.31.tar.gz 是一份面向 Linux 环境下 PHP 开发与运维人员的 PHP 7.1.31 官方源码压缩包,对应 2016 年 12 月发布的 PHP 7.1 稳定分支,整合了多项性能优化与安全修复。该版本包含 void 返回类型、类常量可见性等语法更新,适合需要手动编译安装、定制扩展或研究源码结构的读者使用。压缩包共 19510 个文件,约 18.8MB,其中以 phpt 测试脚本、c 源码、h 头文件为主,辅以 configure 脚本、php.ini 配置模板、扩展模块及相关文档,覆盖 PHP 核心、标准库与扩展的完整目录结构,便于在 Linux 中按官方流程完成配置、编译与安装。已有 287 人学习下载。通过这份源码包,读者可深入理解 PHP 内部实现与构建体系,掌握 PHP-FPM、Apache/Nginx 集成时的编译参数选择,也可利用内置测试集验证环境正确性,或参考其中大量构建脚本与 Makefile,为二次开发、性能调优或安全加固打下基础。
1. php-7.1.31.tar.gz:锁死版本的老项目,为什么最需要它
如果手上还有一个业务系统必须跑在老栈上,你迟早会在某个变更窗口里遇到 php-7.1.31.tar.gz 这个名字。它是 PHP 7.1 分支的最后一个源码发行压缩包,解决一件非常具体的事:在不升主版本、不换运行时的前提下,把这一分支补齐到官方最后一次安全修复的形态。它不是新版本,没有新功能,也不会再有后续发行,但对动不了的老系统来说,这就是最后一块体面的补丁。
你真正要判断的不是“PHP 7.1 好不好”,而是“这份包能不能在我手上落地”。比如某公司老项目,第三方扩展没有高版本替代,业务代码还依赖一堆旧语法,这时候从源码编译出的 7.1.31 就可能成为大升级前的唯一过渡方案。这篇内容就是给要实际操作它的人写的:怎么确认源码没被改过、在现代系统上怎么编译、哪几个坑必然踩到,以及装完之后这个版本还能不能放心用。下面直接从这份压缩包本身开始拆。理解这一点,你才不会在编译时期待它有新特性,也不会因为版本号旧就草率否定,它存在的意义是“补安全”,不是“做升级”。
2. 从源码树看 php-7.1.31:版本语义、解包动作与依赖底线
2.1 版本号决定了你的目标:补安全,不升功能
php-7.1.31.tar.gz 的版本号信息非常直接:主版本 7,次版本 1,发行版本 31。PHP 7.1 这条线从 7.1.0 一路走到 31 这个号就到头了,之后不会再出现 7.1.32。也就是说,这个压缩包对应的是 7.1 分支的“封板版本”,这一阶段的更新内容全部是安全修复,不引入新功能、不调整语言行为。对新项目它是古董,对老项目它是收官版。
这种“只补安全”的版本对生产环境意味着行为边界稳定可预期。你为了修复 CVE 把它装上去,业务逻辑不会因为升级而翻车;反过来,如果它附带功能变化,老代码反而不敢升。所以 7.1.31 在运维语境里本质上是一张安全补丁,而不是一次软件升级。我一般会先跟团队确认一件事:这个系统升不动主版本的具体原因是什么。是第三方扩展没高版本,还是内部代码积累的兼容债太深。原因搞清楚,才能决定 7.1.31 是终点还是中转站。
为什么这么判断?因为 PHP 7.1 已经 EOL,官方不会再发布任何补丁。你拿到 7.1.31 等于拿到了这个分支的最后状态。它适合补漏洞窗口,但不适合被当成可以永久依赖的新基线。后续版本的主线早已走了很多年,老代码的兼容红利会随时间一点点耗尽,这点在后面第五章会专门展开。
2.2 解包之前先做的 4 个确认动作
拿到 php-7.1.31.tar.gz 之后,我一般不会急着 tar -xzf 一把梭。先看包体、再核校验值、再列目录、最后确认版本头文件,这套动作能避免把一份被改过的源码编到生产环境里。下面是把这套确认写成命令:
# 看包基本信息,确认不是下载断档的空文件 ls -lh php-7.1.31.tar.gz # 计算哈希,和发布源给出的校验值做对照 sha256sum php-7.1.31.tar.gz # 只列包内容而不解包,确认顶层目录名是 php-7.1.31 tar -tzf php-7.1.31.tar.gz | head -20 # 确认 configure 脚本在里面,这是后续编译的入口 tar -tzf php-7.1.31.tar.gz | grep -E 'configure$' | head -3第一行看大小,先排除 0 字节文件;第二行是重点,官方发布时会同步给出校验值文件,你对照的是那份文件而不是某个网页上抄来的哈希,否则等于没有校验;第三行用 -t 参数只读包内清单不落盘,快速确认目录名没被改过;第四行确认 configure 存在,配合后面的版本号检查,这份源码才算验证到手。
真正确认版本不能只靠文件名,文件名可以被改名,但源码根目录里的版本宏不会被人刻意伪造。解包后执行下面的检查:
tar -xzf php-7.1.31.tar.gz cd php-7.1.31 grep -E 'PHP_(MAJOR|MINOR|RELEASE)_VERSION' main/php_version.hgrep 输出应该是三个宏,分别对应 7、1、31。只要这三个值和你的预期一致,就可以放心进入下一步。我习惯把这条命令放在写进所有老版本 PHP 安装笔记的第一页,因为它能拦住大多数“包名看着对、内容货不对板”的下载事故。
2.3 编译前必须清楚的依赖底线:PCRE1、OpenSSL、libxml2
php-7.1.31 是 2019 年前后冻结的代码,但你要在现在的系统上把它编出来,依赖版本就不能按当年标准来。常见做法是先核对四样:PCRE1 的头文件、OpenSSL 的开发包、libxml2 的开发包、以及基础编译工具链。列成表是这样:
| 依赖组件 | 关键底线 | 踩坑表现 |
|---|---|---|
| PCRE1 | 系统里有 pcre.h,PCRE2 不算 | configure 阶段报错,说找不到 pcre.h |
| OpenSSL | 开发头文件与库要配套 | --with-openssl 探测失败或扩展静默缺失 |
| libxml2 | libxml2-dev 与 xml2-config 都在 | configure 中途中断在 libxml2 检查 |
| 工具链 | gcc / make / autoconf 可用 | 高版本编译器偶发告警但不致命 |
我一般会把这四个问题的排查放到 configure 之前做一次。因为 configure 的报错顺序很不友好,它先探测一堆无关模块,走到 pcre 或者 libxml2 时才停,等你绕了一圈回来发现只是系统缺了一个开发包,半天已经过去了。第四章里的 5 个坑有一大半就是从这里长出来的,这一节先把底线划出来,后面解码时就不会手忙脚乱。
3. 把 php-7.1.31 编译出来:configure 参数组合与最短命令路径
3.1 先把编译依赖装齐,再谈编译
在 deb 系发行版上,编译 php-7.1.31 需要的最小依赖包是下面这一组。我把它们一次性装齐,避免 configure 走到一半因为缺头文件翻车:
sudo apt update sudo apt install -y build-essential autoconf \ libxml2-dev libssl-dev libpcre3-dev pkg-configbuild-essential 提供 gcc 和 make;libxml2-dev 提供 libxml2 头文件;libssl-dev 对应 OpenSSL 的开发头文件;libpcre3-dev 装的是 PCRE1,不是新系统默认的 pcre2,这一点最容易忽略;pkg-config 帮助 configure 定位部分库的编译参数。如果你的发行版仓库里已经没有 libpcre3-dev 这个包名,说明它已经全面切到 PCRE2,这时候不要硬绕,直接单独编译一份 PCRE1(8.x 版本)源码,再在 configure 时用 --with-pcre-dir 指向它的安装前缀即可。
注意一点:换到 rpm 系发行版时,包名后缀会变成 -devel,对应关系是 libxml2-devel、openssl-devel、pcre-devel。不要拿着 deb 系的包名去另一派系统里搜,否则会花很多时间在“编译环境缺依赖”这件事上做无效往返。
3.2 configure 的最小可用参数组合
configure 参数越少,后面排查越省事。下面这组是我在一台只跑 php-fpm 的机器上常用的最小组合,既保留 mysqlnd,又带 openssl,也不依赖 Apache 模块:
cd php-7.1.31 ./configure \ --prefix=/usr/local/php71 \ --with-config-file-path=/usr/local/php71/etc \ --enable-fpm \ --with-fpm-user=www-data \ --with-fpm-group=www-data \ --with-openssl \ --with-pcre-jit \ --with-mysqli=mysqlnd \ --with-pdo-mysql=mysqlnd--prefix 决定一切产物的安装根目录,将来 make install 不会往系统目录里撒文件,卸载时直接删安装目录就行;--with-config-file-path 指定 php.ini 的查找位置,建议放在 prefix 下面,避免和系统自带 PHP 的 ini 混淆;--enable-fpm 生成 php-fpm 可执行文件,这是当前绝大多数老项目跑服务的方式。--with-fpm-user 和 --with-fpm-group 设的是 worker 进程身份,一般用 www-data,不要用 root 跑业务进程。--with-openssl 不带路径时,configure 会去系统默认目录找头文件,这对多数环境够用。--with-pcre-jit 开启 PCRE1 的 JIT 加速,正则有明显收益。--with-mysqli=mysqlnd 和 --with-pdo-mysql=mysqlnd 使用 PHP 内置 mysqlnd 驱动,不依赖 libmysqlclient,这是 7.1 上最省事的数据库接入方式。
如果项目里还用到 curl、mbstring、iconv、gd 之类扩展,可以按需追加 --with-curl、--enable-mbstring、--with-iconv、--with-gd。这里的“最小路径”不贪多,先把主链跑通,缺什么再补什么。configure 成功结束时会生成 Makefile,这一步如果出错,先回头看是不是上一节依赖底线没满足,再去想其他原因。补一个我习惯的做法:保留 configure 的完整输出到一个文件里,排查时直接搜 configure: error 前后几行,比人肉盯滚动快得多。
3.3 make 与 make install:用对并行参数
configure 通过之后,真正的耗时在 make 这一步。常见做法是用多核并行编译,但也要看机器内存:
# 核数多、内存充裕时并行编译 make -j$(nproc) # 内存不足 2G 的机器,老老实实 -j2 # make -j2-j 后面的数字是并行编译任务数,$(nproc) 自动取 CPU 核数。并行能把几十分钟的编译压到十分钟以内,但每个编译任务都会占内存,老机器 4 核 2G 内存跑全核并行反而会 OOM,日志里看到 virtual memory exhausted 基本就是这个原因。编译中途如果出现 error,先看清楚是 C 语言的 error 还是库缺失的 error,不要一上来就怀疑源码。7.1.31 的源码在近几年的 gcc 上整体能一次过,顶多有些 deprecated 告警,不影响产物。
编译完成后安装:
sudo make install sudo mkdir -p /usr/local/php71/etc sudo cp php.ini-production /usr/local/php71/etc/php.ini sudo cp /usr/local/php71/etc/php-fpm.conf.default /usr/local/php71/etc/php-fpm.confmake install 会把 php 可执行文件放进 /usr/local/php71/bin/php,php-fpm 放进 /usr/local/php71/sbin/php-fpm。后面三条是把生产用 php.ini 和 php-fpm.conf 落到 config-file-path 对应的位置。php.ini-production 是源码根目录自带的建议生产配置,先拿来跑,后续按业务调即可。
注意:configure 时设了 --with-config-file-path=/usr/local/php71/etc,php 就只在这个路径找 ini。如果你把 php.ini 复制到了别的目录,CLI 启动会提示找不到配置文件,这类问题和编译无关,优先级却往往被误判成编译失败。
如果 make 中途失败需要重试,先执行 make clean 清掉上一次的中间产物,再重新 make。带着旧.o文件重编,很容易出现“改了个 configure 参数却不生效”的假象。
4. php-7.1.31 编译与运行避坑:5 个绕不开的经典问题
4.1 configure 卡在 pcre.h 上
现象:configure 执行到一半停在 checking for pcre.h... no,随后报 configure: error: Could not find pcre.h in /usr/include。装完 pcre 的包再编还是没用。
原因:这是老 PHP 源码在近两三年的发行版上最典型的一次翻车。新系统的默认正则库是 PCRE2,头文件是 pcre2.h,而 php-7.1.31 源码里写死找 pcre.h,也就是 PCRE1。你装 libpcre2-dev 完全没用,名字看着像,实际不是同一套接口。
解决:安装 libpcre3-dev;如果发行版仓库里已经没有这个包,就自己编一份 PCRE1 源码,安装到 /usr/local/pcre1,再在 configure 时加 --with-pcre-dir=/usr/local/pcre1。装完之后不要急着跑 configure,先用 find /usr/include -name pcre.h 确认头文件真实存在,只有文件在,configure 才认。
4.2 --with-openssl 不起作用,扩展静默缺失
现象:configure 带了 --with-openssl,整个编译也顺利结束,但装完的 php -m 里没有 openssl,或者 php -i 显示的 OpenSSL 版本和系统实际版本对不上。
原因:--with-openssl 不带路径时,configure 按默认 include 路径找 ssl.h。系统里同时存在多套 OpenSSL 时,它可能探测到一套“看起来合法”的头文件,make 时却链接到另一套库,最后扩展没编进产物,或者编进去了但版本不对。这个问题表面是参数问题,实际是路径混乱。
解决:configure 前先用 pkg-config --modversion openssl 查系统默认 OpenSSL 版本,再用 --with-openssl=/usr 或实际前缀显式指定路径。安装后不要只看 php -m,还要用 php --ri openssl 确认扩展信息能正常输出,并和系统 openssl version 对照,两处一致才说明链接正确。
4.3 老代码连不上 MySQL 8 的新认证
现象:PHP 7.1.31 编译好的程序去连 MySQL 8.0,数据库返回 SQLSTATE[HY000] [2054] The server requested authentication method unknown to the client,或者直接提示 caching_sha2_password 不被支持。
原因:MySQL 8.0 默认身份认证换成 caching_sha2_password,而 PHP 7.1.31 内置的 mysqlnd 是这一代就冻结的,客户端认证逻辑里没有这个新插件,两边握手时服务端怎么换方案都没用。
解决:这一步要改的是数据库端而不是 PHP 端。给应用账号指定旧认证方式,执行 ALTER USER 'app'@'host' IDENTIFIED WITH mysql_native_password BY '新密码';。如果整个实例是新建的,也可以在数据库配置里设 default_authentication_plugin=mysql_native_password 再重建账号。前提是你能控制数据库的认证策略;管理严格的环境里,要先走变更流程申请,别自己偷偷改。这个坑在你升级 MySQL 而 PHP 不动的情况下几乎必踩,建议在立项时就列入迁移清单。
4.4 configure 中断在 libxml2 检查
现象:configure 进行到 checking LIBXML version... 后直接就报 configure: error: libxml2 2.6.23 or newer required,换了好几次参数都停在这。
原因:多半是只装了运行时 libxml2 的 .so 文件,没装开发头文件;或者装了 dev 包,但 xml2-config 不在 PATH 里。php-7.1.31 的 configure 主要靠 xml2-config 这个辅助脚本探测 libxml2 信息,它找不到,就算库里明明有头文件也没用。
解决:安装 libxml2-dev 后重试。如果 dev 包装了却还报错,先看 /usr/bin/xml2-config 是否存在,不存在就手动把 /usr/include/libxml2 和 /usr/lib 通过 --with-libxml-dir=/usr 指给 configure,或者导出一组 LIBXML2_CFLAGS 和 LIBXML2_LIBS 环境变量。这里最忌反复在同一参数上猜测,直接打开 configure 日志,看它到底在找哪个路径,路径补对,这一关就过了。
4.5 systemd 拉起 php-fpm 总是失败或不断重启
现象:把 php-fpm 交给 systemd 后,systemctl status 显示 active (exited),进程根本没起来;或者进入不停 Restart 的循环,日志里只有 pid 相关的报错。
原因:php-fpm.conf 里默认 daemonize=yes。php-fpm 自己 fork 到后台后,systemd 认为主进程已退出,于是判定服务失败,或者按 Restart 策略不停拉起,其实都是同一个配置在作怪。
解决:在 php-fpm.conf 里改成 daemonize=no,或者在 systemd unit 的 ExecStart 里直接加 -F 参数,强制前台运行,让 systemd 自己管进程生命周期。示例的 ExecStart 行如下:
ExecStart=/usr/local/php71/sbin/php-fpm -F改完记得 systemctl daemon-reload 再 start。这个配置对 7.1.31 和后面所有 php-fpm 版本都一样,属于一劳永逸的写法,免得哪天换了个发行版,又在同样的问题上浪费一轮排查时间。
5. 装完别急着上:验证安装的三步检查与安全处置
5.1 php -v 之外还要用 ldd 确认动态库
php -v 只能证明二进制能跑,不能证明它链接对了库。我一般会这样验证:
/usr/local/php71/bin/php -v /usr/local/php71/bin/php -m | grep -E 'openssl|mysqli|pdo_mysql|libxml' ldd /usr/local/php71/bin/php | grep -E 'ssl|crypto|xml2|pcre'ldd 输出会和 php -i 里的扩展清单互相印证,重点看链接到的 OpenSSL 库版本是不是编译时想用的那套,避免出现“CLI 能跑但 HTTPS 请求全部失败”这种隐性问题。
5.2 最小链路测试:起 fpm 并验证一次真正的 https 请求
php-fpm 的最小跑通测试是先做配置检查再起进程:
/usr/local/php71/sbin/php-fpm -t -y /usr/local/php71/etc/php-fpm.conf /usr/local/php71/sbin/php-fpm -y /usr/local/php71/etc/php-fpm.conf ss -lnt | grep 9000-t 先验证 fpm 配置语法,ss 确认 9000 端口在听,说明 worker 进程已经起来。接下来用 CLI 发一次真实 HTTPS 请求,验证 openssl 扩展的证书链路:
/usr/local/php71/bin/php -r "echo file_get_contents('https://example.com');"如果报 SSL certificate problem,说明 ini 里缺 openssl.cafile,在 php.ini 里指到 /etc/ssl/certs/ca-certificates.crt 即可。这一步能同时暴露两件容易藏很久的问题:openssl 扩展没编译进去,或者系统 CA 路径没配对。
5.3 这个版本的适用边界:能当过渡,别当终局
php-7.1.31 是 EOL 分支的最后一个包,不会再有任何官方更新。它适合放在隔离内网、离线环境或短期过渡期里,不适合直接暴露在公网当长期底座。如果要继续用,至少做两件事:一是把 php.ini 里的 expose_php 关掉,把高风险函数在 disable_functions 里列掉;二是自己维护一份已知漏洞清单,官方不补就只能靠前置的网络隔离和防护设备来缓解。老代码的兼容性红利会随版本代差增大而迅速贬值,我见过不少因为“反正还能跑”就一直拖着的系统,最后升级成本滚得比当年重建还高。所以拿到这份源码、跑通这套安装流程之后,更值钱的下一步是开一个升级窗口,把业务往还有维护的版本上迁移。这是我多年养成的习惯:源码包不是终点,验证完、测完,还要给它标一个退役时间。希望帮到你。
本文还有配套的精品资源,点击获取