十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

PHP 7.1.31源码包编译安装与老项目兼容性实战指南

PHP 7.1.31源码包编译安装与老项目兼容性实战指南 简介PHP 7.1.31 的 Linux 源码压缩包面向需要在 Linux 环境下编译安装 PHP、进行二次开发或研究 PHP 内部实现的中高级开发者。该版本属于 7.1 系列收尾更新自带性能优化与安全修复可用于搭建生产或学习环境。压缩包约 18.8MB共 19510 个文件包含 15594 个 phpt 测试用例、1009 个 C 源码、774 个头文件、415 个 inc 配置脚本及 175 个 php 脚本等目录结构完整覆盖核心、标准库、扩展及测试体系。通过阅读源码与测试文件可快速理解 PHP 解释器运行机制、扩展编写方式和配置选项同时包内含 configure、Makefile 等构建配置脚本与大量文档便于在主流发行版上完成编译安装。目前已有 285 人学习适合希望从源码层面掌握 PHP 环境从零构建与调优的运维或研发人员。 直接进入正题吧。今天想聊一个看起来特别不起眼、但几乎是老一代 PHP 开发绕不开的东西php-7.1.31.tar.gz。如果你接触过 2017 到 2020 年之间的 PHP 项目尤其是一些维护了很久的 ThinkPHP 3.2.3、老电商系统、二手 CMS大概率对这个文件名不陌生。它就是一个 PHP 7.1 分支的官方源码压缩包tar.gz 是 Linux/Unix 下最常见的打包压缩格式7.1.31 是 PHP 7.1 系列的最后一个安全维护版本发布于 2019 年 10 月左右。换句话说这个版本既是 7.1 时代的终点也是很多老项目生产环境的最后一代安全港。这篇内容我会从源码包本身出发讲清楚它适合谁、怎么编译安装、怎么和老框架搭配、会遇到哪些经典问题以及为什么到了今天你依然可能在下载旧依赖、复现老环境、接手古董项目时碰到它。不是照着官方文档念一遍而是把我实际踩过的坑、确认过的参数、调过的编译选项都写出来希望对你有用。1. 这个源码包到底是什么为什么还有人用它1.1 PHP 7.1 在 PHP 历史里的位置先简单回顾一下背景。PHP 7.0 是 2015 年底发布的当时最大的变化是性能对比 PHP 5.6 有了翻倍级别的提升背后的 Zend Engine 3.0 重写了大量底层数据结构内存占用也大幅下降。随后的 7.1 在 2016 年 12 月发布引入了一些非常重要的语法和功能特性包括可空类型Nullable Types比如function foo(?int $a)明确允许参数为 null这个对老代码迁移很友好。void 返回类型声明函数不返回任何值。iterable 伪类型接受 array 或者 Traversable 对象。多异常捕获catch (ExceptionA | ExceptionB $e)写起来舒服很多。类常量可见性public const FOO 1之前只能默认 public。list() 短数组语法[$a, $b] $arr。这些特性放在今天看很基础但在当时算是相当实用的一波更新。更重要的是PHP 7.1 对老代码的兼容性比 7.2、7.3 更好尤其是对某些在 7.2 里被标记为废弃的语法比如each()、create_function()、$m-${$name}这种写法在 7.1 里还能正常跑。所以很多跑着老业务、没办法大改代码的公司都停在了 7.1。1.2 7.1.31 是这条分支的句号PHP 官方的支持策略是每个分支有 2 年活跃支持加 1 年安全维护。PHP 7.1 从 2016 年 12 月发布活跃支持到 2018 年 12 月安全维护到 2019 年 12 月。php-7.1.31.tar.gz就是安全维护期内发布的最后一个版本专门修了一批安全问题包括后来被广泛提及的某些反序列化相关漏洞和 EXIF 扩展问题。所以你会看到很多老项目的部署脚本里写死的就是php-7.1.31.tar.gz而不是 7.1.30 或者 7.1.32其实没有 32。原因很简单它是 7.1 系列里最完善的版本修了前面已知的安全问题。再往后就只有 7.2、7.3 了代码不兼容升不动。对运维来说固定一个已知的版本号比追新要稳得多。1.3 适合谁的场景我自己的经验是碰到这个包基本上就是下面几种情况之一接手老项目服务器上还跑着 PHP 7.1代码里有用到 7.2 开始废弃的语法升不上去。复现老环境比如要在本地 Docker 里起一个和线上一致的 PHP 版本用来排查 bug。学习老框架源码很多经典 PHP 教程、ThinkPHP 3.2.3 项目、老开源系统默认环境就是 7.1为了不折腾直接用同版本。离线内网部署生产环境不能随便访问外网必须用 tar.gz 源码包手动编译安装。如果你属于上面任何一种这篇文章应该能帮你少走弯路。2. 编译安装前的准备工作和核心思路2.1 新手最容易忽略的依赖检查从源码编译 PHP 和用 apt/yum 装二进制包完全是两码事。源码包只给你 C 源码具体编译成什么样、带哪些扩展全看你本机装了哪些依赖库。所以很多人拿着php-7.1.31.tar.gz直接./configure结果报一屏错误多半就是缺依赖。我整理了一份我常用的依赖清单以 CentOS 7 / 8 类系统为例yum install -y gcc gcc-c make autoconf libxml2-devel openssl-devel \ curl-devel libjpeg-devel libpng-devel freetype-devel \ bison re2c sqlite-devel oniguruma-devel readline-devel \ libzip-devel如果是 Ubuntu/Debianapt-get install -y build-essential autoconf libxml2-dev libssl-dev \ libcurl4-openssl-dev libjpeg-dev libpng-dev libfreetype6-dev \ libsqlite3-dev libonig-dev libreadline-dev libzip-dev这里面的关键点libxml2-devel是编译 PHP 核心必须的没有它 configure 会直接挂掉因为 PHP 默认要启用 XML 相关支持。openssl-devel影响的是 https 请求、openssl 扩展、以及新版 PHP 里password_hash的某些加密算法依赖。libzip-devel在 PHP 7.1 里不是强制的但如果你后面想单独编译 zip 扩展最好提前装好。oniguruma-devel本质上是为 mbstring 正则功能服务的缺少它可能导致某些字符处理场景行为异常。2.2 configure 参数的选择逻辑PHP 源码编译最核心的就是./configure这一步。虽然参数很多但不要无脑全开要根据实际业务来。我一般会用一个偏保守、但足够覆盖常见项目的组合./configure \ --prefix/usr/local/php-7.1.31 \ --with-config-file-path/usr/local/php-7.1.31/etc \ --with-config-file-scan-dir/usr/local/php-7.1.31/etc/conf.d \ --enable-fpm \ --with-fpm-userwww \ --with-fpm-groupwww \ --enable-mbstring \ --enable-zip \ --enable-pcntl \ --enable-bcmath \ --enable-opcache \ --enable-sockets \ --with-mysqlimysqlnd \ --with-pdo-mysqlmysqlnd \ --with-curl \ --with-openssl \ --with-zlib \ --with-gettext \ --with-mhash \ --enable-xml \ --enable-session \ --with-APCu \解释几个比较关键的选择--with-config-file-path指定 php.ini 的位置。如果不指定PHP 可能去/usr/local/lib这种默认路径找到时候改了 php.ini 发现没生效排查半天。--enable-fpm是把 PHP-FPM 编进去绝大多数 Web 场景都靠它除非你只跑 CLI 脚本。--with-mysqlimysqlnd和--with-pdo-mysqlmysqlnd使用 mysqlnd 驱动这是 PHP 官方推荐的 MySQL 驱动性能好而且不需要额外安装 libmysqlclient。--enable-pcntl是给常驻脚本、队列消费用的如果是纯 Web 项目可以不编但编上没坏处。--enable-opcache强烈建议打开PHP 7.1 配合 Zend OPcache 性能提升非常明显。2.3 为什么我建议用单独目录而不是覆盖系统版本很多新手习惯直接./configure --prefix/usr/local把 PHP 装到默认路径这样做其实风险很大。比如 CentOS 自带 php 或者后面 yum 装的其他软件依赖了系统 PHP你手动覆盖之后可能导致php命令版本混乱、扩展路径错乱。我个人的习惯是固定一个独立目录比如/usr/local/php-7.1.31然后在/usr/local/bin下做软链指向它需要哪个版本就切哪个ln -s /usr/local/php-7.1.31/bin/php /usr/local/bin/php ln -s /usr/local/php-7.1.31/bin/phpize /usr/local/bin/phpize ln -s /usr/local/php-7.1.31/sbin/php-fpm /usr/local/bin/php-fpm这样做的好处是多个 PHP 版本共存不冲突。卸载直接删目录和软链就行不会污染系统。出问题可以随时切回旧版本回滚成本极低。3. 实操过程从解压到 PHP-FPM 跑起来3.1 解压、编译、安装完整命令序列以 CentOS 系统为例完整的操作流程我整理成了一套命令直接复制粘贴基本能跑通# 1. 解压源码包 tar -zxvf php-7.1.31.tar.gz cd php-7.1.31 # 2. 生成 configure源码包已经带 configure一般不需要这一步 # 如果你是从 git clone 的源码需要先执行 ./buildconf # 3. 使用独立目录配置 mkdir -p /usr/local/php-7.1.31/etc ./configure \ --prefix/usr/local/php-7.1.31 \ --with-config-file-path/usr/local/php-7.1.31/etc \ --with-config-file-scan-dir/usr/local/php-7.1.31/etc/conf.d \ --enable-fpm \ --with-fpm-userwww \ --with-fpm-groupwww \ --enable-mbstring \ --enable-zip \ --enable-pcntl \ --enable-bcmath \ --enable-opcache \ --enable-sockets \ --with-mysqlimysqlnd \ --with-pdo-mysqlmysqlnd \ --with-curl \ --with-openssl \ --with-zlib \ --with-gettext \ --with-mhash # 4. 编译安装-j 后面的数字取决于 CPU 核数 make -j 4 make install编译过程中有两个注意点make -j 4的-j参数表示并行编译任务数。如果机器配置低建议去掉或者用-j 2否则容易内存爆掉。我遇到过在 1G 内存的小机器上并行编译直接 OOM 的情况。编译时间一般在几分钟到十几分钟不等取决于机器性能。如果中途报错多半是缺依赖回到第 2 节补包再重新 make clean make。3.2 初始化配置文件和 PHP-FPM安装完成后你还需要手动初始化 php.ini 和 php-fpm.conf这一步源码包不会自动做经常被人忽略。# 复制 php.ini 开发环境配置生产用 php.ini-production 更合适 cp php.ini-production /usr/local/php-7.1.31/etc/php.ini # 复制 php-fpm 配置 cp /usr/local/php-7.1.31/etc/php-fpm.conf.default \ /usr/local/php-7.1.31/etc/php-fpm.conf # PHP-FPM 的 www 池配置 cp /usr/local/php-7.1.31/etc/php-fpm.d/www.conf.default \ /usr/local/php-7.1.31/etc/php-fpm.d/www.conf这里有个小细节如果你提前创建了/usr/local/php-7.1.31/etc/conf.d目录并且在 configure 里指定了 scan-dir那后续每个扩展只要放一个xxx.ini进去就能自动加载比如opcache.ini、redis.ini非常方便。启动 PHP-FPM# 启动方式一直接用二进制跑 /usr/local/php-7.1.31/sbin/php-fpm # 启动方式二指定配置文件 /usr/local/php-7.1.31/sbin/php-fpm -y /usr/local/php-7.1.31/etc/php-fpm.conf # 查看是否启动成功 ps -ef | grep php-fpm3.3 验证安装结果启动之后建议做一个完整验证确认版本、编译参数、加载模块都符合预期# 查看版本号 /usr/local/php-7.1.31/bin/php -v # 查看编译参数确认是否带 fpm 和 mysqlnd /usr/local/php-7.1.31/bin/php -i | grep configure # 列出已加载模块 /usr/local/php-7.1.31/bin/php -m # 测试 PHP-FPM 是否响应 curl -I http://127.0.0.1:9000/ 21 | head -n 5php -m的输出里重点看有没有mysqli、pdo_mysql、mbstring、curl、openssl、opcache这几个是老项目最常用的。如果缺了某个就得回头检查依赖库或者重新 configure。3.4 与 Nginx 集成PHP-FPM 默认监听127.0.0.1:9000Nginx 里只需把 PHP 请求转发给它即可。一个最基本的 server 配置server { listen 80; server_name your-domain.com; root /var/www/html; index index.php index.html; location ~ \.php$ { root /var/www/html; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } }很多老项目还用了伪静态尤其是 ThinkPHP 3.2.3 这种前后端一体的框架。伪静态规则需要额外处理后面第 4 节我会专门讲。4. 老项目实战ThinkPHP 3.2.3 搭配 PHP 7.1.314.1 为什么这个组合特别常见ThinkPHP 3.2.3 是个非常典型的老而弥坚框架2014 年左右发布用的人极多很多企业内部系统、OA、商城项目都是基于它开发的。它官方推荐 PHP 5.3但实际在 PHP 7.1 下跑得还算顺原因有几点ThinkPHP 3.2.3 的核心代码没有用太多 PHP 7 里被移除的语法比如旧版 mysql 扩展、mysql_*函数只要底层 PDO 和 mysqli 是好的就能跑。PHP 7.1 保留了each()、create_function()等兼容性写法而这些在 7.2 里会被报 deprecated在 7.3/7.4 里更麻烦。对老框架来说7.1 几乎是还能忍受的上限。7.1.31 作为该分支最终版本既有性能收益又有安全修复是兼顾稳定与安全的最优选择。所以你会看到php 7.1.31 ThinkPHP 3.2.3 Nginx/Apache MySQL这个组合在很长一段时间里是大量国内中小项目的标准配置。4.2 ThinkPHP 3.2.3 在 PHP 7.1 下的常见坑说实话3.2.3 直接拿到 PHP 7.1 里跑基本功能没问题但有些隐藏 bug 你还是得提前知道坑一each()函数废弃警告PHP 7.1 已经将each()标记为废弃调用时会产生Deprecated: The each() function is deprecated的提示虽然不影响运行但会刷日志。ThinkPHP 3.2.3 里有些旧代码会用到处理方式有几种在 php.ini 里把error_reporting调低过滤掉 deprecationerror_reporting(E_ALL ~E_DEPRECATED ~E_NOTICE)。源码里全局替换each()为foreach遍历。保留下日志但不在页面上显示display_errors Off。我个人建议第一种最直接符合这个组合的实际使用场景。坑二字符集和 mbstring 问题老项目数据库连接经常写charsetutf8没问题但 PHP 7.1 如果没有正确启用 mbstring可能导致ThinkPHP的自动字符串截断、字符编码转换功能异常。所以编译时--enable-mbstring是必须的同时 php.ini 里建议设置mbstring.internal_encoding UTF-8 mbstring.func_overload 0注意mbstring.func_overload在 PHP 7.2 里被废除了但 7.1 还能配置所以这个配置只适用于 7.1 环境千万别拷到新版本上去。坑三伪静态配置ThinkPHP 3.2.3 默认 URL 模式是 PATHINFO比如http://domain/index.php/Home/Index/index。如果要用伪静态去掉index.phpNginx 需要额外配置location / { if (!-e $request_filename) { rewrite ^/(.*)$ /index.php/$1 last; } }Apache 环境下则是.htaccessIfModule mod_rewrite.c Options FollowSymlinks -Multiviews RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L] /IfModule这个配置一旦写错最容易出现的现象是首页能开但内页全部 404。我接过不少这类工单最后发现都是伪静态规则和框架 URL 模式不匹配。4.3 老项目的运行期优化PHP 7.1 本身的性能已经相当不错但跑老框架还可以做几个提升开启 OPcache在 php.ini 里加上zend_extensionopcache.so opcache.enable1 opcache.memory_consumption128 opcache.interned_strings_buffer8 opcache.max_accelerated_files10000 opcache.validate_timestamps0这里注意生产环境如果代码不会频繁改动opcache.validate_timestamps0可以极大减少文件系统检查开销但每次发布代码后必须手动 reload php-fpm否则更新不生效。我踩过这个坑发布后页面没变化排查半天发现是 OPcache 缓存了旧代码。调整 PHP-FPM 进程数在www.conf里根据服务器内存调整pm dynamic pm.max_children 50 pm.start_servers 5 pm.min_spare_servers 5 pm.max_spare_servers 35一个粗略的计算标准假设每个 PHP-FPM 进程平均占用 30MB 内存服务器有 2GB 可用内存max_children 不宜超过 60。不能盲目调大否则内存不足反而更慢。5. 常见问题与排查技巧实录5.1 configure 阶段报错汇总我在不同机器上编译 PHP 7.1.31 时遇到过好几类经典报错这里整理成速查表报错信息原因解决方法configure: error: xml2-config not found缺少 libxml2 开发库安装libxml2-develconfigure: error: Please reinstall the libcurl distribution缺少 curl 开发库安装curl-develconfigure: error: mcrypt.h not foundPHP 7.1 默认不再捆绑 mcrypt需要单独指定使用--with-mcrypt/path/to/libmcrypt或改用 opensslconfigure: error: Cannot find OpenSSLs evp.hopenssl 开发库缺失安装openssl-develmake: *** [ext/fileinfo/fileinfo.lo] Error 1编译 fileinfo 内存不足降低-j参数或先make clean再单线程编译表格里特别说明一下 mcryptPHP 7.1 虽然还支持 mcrypt 扩展但官方已经标记废弃在 7.2 里直接移除了。编译时如果业务代码还在用mcrypt_encrypt这类函数就需要额外安装 libmcrypt 并指定路径。如果业务可以用 openssl 替代建议直接用 openssl后面升级省事。5.2 运行期报错和排查思路问题一PHP Warning: Module mbstring is already loaded in Unknown on line 0这个报错很经典通常是因为 php.ini 里加载了两次 mbstring 扩展比如同时写了extensionmbstring.so和mbstring相关的 ini 配置文件或者php.ini里的extension_dir设置导致重复加载。解决思路检查 php.ini 的extensionmbstring行是否重复。检查/etc/php.d/或conf.d目录下是否有额外的 mbstring.ini。如果用了--with-config-file-scan-dir所有 .ini 文件都会被自动加载此时不要在 php.ini 里重复写 extension。问题二页面白屏或直接 502502 基本是 PHP-FPM 没起来或者进程崩了。先看php-fpm日志tail -f /usr/local/php-7.1.31/var/log/php-fpm.log常见原因包括FPM 监听端口被占用。进程数配置过高内存不足fpm 被 OOM killer 杀掉。www用户不存在或目录权限不对。问题三call to undefined function mysqli_connect()说明 mysqli 扩展没加载。检查两步# 是否编译进去了 /usr/local/php-7.1.31/bin/php -m | grep mysqli # php.ini 里是否加载 grep -i mysqli /usr/local/php-7.1.31/etc/php.ini如果没编译进去就只能重新 configure 加上--with-mysqlimysqlnd再 make make install。这也是为什么我强调编译参数一开始就要尽量想全。5.3 Docker 环境下的复现技巧如果你不想在物理机上折腾也可以直接用 Docker 快速复现 PHP 7.1.31 环境。官方镜像php:7.1.31-fpm其实就是基于这个源码包构建的日常开发用起来最省事FROM php:7.1.31-fpm RUN apt-get update apt-get install -y \ libzip-dev libpng-dev libjpeg-dev \ docker-php-ext-install pdo_mysql mysqli \ docker-php-ext-install opcache然后docker-compose.yml里配合 Nginx 和 MySQL 起一套version: 3 services: nginx: image: nginx:1.20 ports: - 8080:80 volumes: - ./html:/var/www/html - ./nginx.conf:/etc/nginx/conf.d/default.conf depends_on: - php php: build: . volumes: - ./html:/var/www/html mysql: image: mysql:5.7 environment: MYSQL_ROOT_PASSWORD: root这种方式的优点是完全隔离不污染宿主机适合临时复现 bug 或者做本地开发。但生产环境如果追求极致性能还是建议直接编译到系统里省去 Docker 这一层开销。6. 扩展编译与后续选择建议6.1 给 PHP 7.1.31 安装扩展的正确姿势老项目经常需要额外装扩展最典型的是 Redis、Memcached、Swoole runtime 解密工具等。以 Redis 为例# 下载 phpredis 扩展源码注意选择支持 7.1 的分支 git clone -b 4.3.0 https://github.com/phpredis/phpredis.git cd phpredis # 用 phpize 生成 configure /usr/local/php-7.1.31/bin/phpize ./configure --with-php-config/usr/local/php-7.1.31/bin/php-config make make install安装完成后在/usr/local/php-7.1.31/etc/conf.d/redis.ini里写入extensionredis.so然后用php -m检查是否加载成功。这里有一点必须提醒PHP 7.1 时代的老扩展很多现在的主分支已经不支持了。比如新版 phpredis 需要 PHP 7.2所以你必须选一个兼容 7.1 的 tag。下载扩展前先看它的 CHANGELOG 或者 composer.json 里的php版本要求这个习惯能帮你省很多时间。6.2 一套基于 conda 的替代方案如果你常用的环境管理工具是 conda也能用它创建包含 PHP 的虚拟环境不过 conda 默认的 PHP 包比较旧而且在 conda 环境中编译 PHP 源码会遇到一些路径问题。我更推荐的做法是保持系统级 PHP 7.1.31 不变。用 conda 管理 Python、Node、数据库等周边依赖。PHP 项目直接通过 php-fpm 与 Nginx 集成不走 conda 环境。原因在于 conda 的软件包使用独立的 lib 路径PHP 的扩展往往依赖系统库如果混用容易出现libssl.so.10 not found这类动态库缺失问题。不是不行是性价比太低没必要跟自己过不去。6.3 什么情况下你需要升级离开 PHP 7.1虽然 7.1.31 是老项目救星但我不建议新项目用它也不建议在有选择的情况下继续长期停留在 7.1。主要理由PHP 7.1 已经停止官方支持安全漏洞不再修复。很多现代 Composer 包、SDK 都要求 PHP 7.2老版本已经装不了。PHP 7.4 / 8.0 在性能和类型系统上进步巨大迁移收益明显。如果你维护的是老项目短期内可以继续用 7.1.31 维持稳定运行但应该做一个运行环境升级 代码兼容性修复的专项计划逐步把代码迁移到 PHP 7.4 甚至 8.x。迁移过程中php -l语法检查、error_reporting(E_ALL)全开观察日志、逐个模块测试是三个核心手段。7. 说点个人心得编译安装php-7.1.31.tar.gz这件事表面看很简单网上教程一抓一大把但真正让人觉得顺手的往往是对细节的把控configure 参数怎么选才能少走弯路、php.ini 放哪里才能不迷惑、扩展装完为什么加载不上、ThinkPHP 3.2.3 的伪静态怎么配才不 404。这些东西官方文档不会一项项告诉你只有自己踩过坑才记得牢。我个人的体会是对待 PHP 7.1.31 这个版本没必要盲目崇拜也不用一棍子打死。它就是 PHP 7.1 时代的收尾之作是很多老项目的稳定锚点。如果你正在维护一套跑在上面的系统建议先把 PHP-FPM 进程数、OPcache 配置、日志级别这三项检查好这三点是运行稳定性的基本盘。如果只是刚接触这个源码包租一台低配云机器从头编译一遍体验一次完整流程比看十篇教程都管用。最后再分享一个小技巧源码包里的INSTALL文件其实写得很清楚包含 configure 参数的完整说明、常见错误处理建议很多人却懒得看。真到排查疑难杂症的时候它往往比搜索引擎更有用。把这份文件当成参考手册保留一份遇到问题先翻两页说不定答案就在里面。本文还有配套的精品资源点击获取
返回列表