
简介php-7.1.31.tar.gz 是 PHP 7.1.31 在 Linux 环境下的官方源码压缩包定位面向需要手动编译 PHP 的服务器管理员、运维工程师以及希望深入源码层面的开发者。压缩包约 18.8MB共含 19510 个文件其中 15594 个 .phpt 测试用例覆盖核心功能与扩展行为另外包含 .c/.h 源码、.php 脚本、.xml/.wsdl 接口定义及 .m4/.am 构建配置足以支撑从 configure、make 到 make install 的完整编译安装流程。资源中还附带 php.ini 开发与生产样板、大量扩展模块以及 PECL 相关辅助文件便于按需定制和排查扩展加载问题。已有 285 人学习浏览适合用于研究 PHP 7.1 的 void 返回类型、类常量可见性等特性在源码层的实现也可作为 Linux 下 PHP 编译部署与源码分析的参考材料。 先说说我为什么会对这个压缩包这么敏感。干过老项目维护的兄弟应该都有印象生产环境上跑着 PHP 5.6 或者 7.0 的站点一抓一大把尤其是那些用 ThinkPHP 3.2.3 搭的业务系统想升级框架又怕业务崩最后只能退一步把 PHP 运行时升到 7.1。而 php-7.1.31.tar.gz 就是 PHP 7.1 系列最后一个正式版本也是很多运维手里压箱底的离线安装包。这篇文章就围绕这个包从解压到编译、从 php.ini 调优到 php-fpm 落地再把我这些年踩过的坑挨个摆出来给正在折腾老项目环境的你一个能直接抄作业的完整流程。我拿到这个包的第一反应不是解压而是先确认一件事这个版本到底是不是 7.1 系列的收尾版。PHP 7.1.31 发布于 2019 年 10 月是 7.1 分支最后的正式版本。虽然官方安全支持早停了但国内大量存量业务还跑在这条版本线上尤其是 ThinkPHP 3.2.3 这种老框架在 PHP 7.1 下的表现比 5.x 好了不止一个档次也比直接跳到 7.4 更稳妥。所以这篇内容适合谁就是那些守着老项目、不敢随便动框架代码但又想通过升级 PHP 运行时来提升性能和兼容性的开发者或运维。1. 拿到 php-7.1.31.tar.gz 之后先干三件事1.1 校验包完整性别让坏包浪费半小时下载完 tar.gz很多人直接 tar -zxvf 就开始了等 configure 报错才发现包有问题那是真浪费时间。我习惯先做两件事看 MD5 和 SHA256再看包内文件列表。你从官方下载页面或者自己内网镜像拿到的包旁边一般都有 .md5 或 .sha256 文件。# 校验 MD5 和 SHA256 md5sum php-7.1.31.tar.gz sha256sum php-7.1.31.tar.gz # 不解压查看包内容 tar -tzf php-7.1.31.tar.gz | head -20tar -tzf这个参数很实用t 表示列出内容z 表示通过 gzip 解压f 指定文件。这样能确认包内根目录是不是 php-7.1.31如果解压出来是一堆散文件后续操作会非常被动。实际生产里我还遇到过一种情况从网盘下载的包被第三方重新打包过里面多了奇怪的脚本或者 PHP 二进制被插过马。所以有条件的话尽量从官方源或可信内网镜像拉取尤其是要部署到生产环境的包这个步骤真不能省。1.2 认识源码包目录结构后面排查问题靠它解压后你会看到 php-7.1.31 目录下有很多子目录核心的几个要心里有数ext/官方扩展源码目录pcntl、mbstring、mysqli、redis 这类扩展都在这里有对应子目录。main/PHP 核心主逻辑很多编译错误和函数冲突的根源在这里。Zend/Zend 引擎源码PHP 7 系列性能提升的核心。sapi/服务器接口层里面包含 fpm、cli、cgi 等子目录。为什么会专门提这个因为后面你单独装扩展、排查函数冲突的时候经常要回到源码目录操作。比如你想确认某个扩展有没有被官方内置先看 ext 下有没有对应目录而不是盲目去 pecl 装。PHP 7.1 时代很多常用扩展其实已经内置在源码包里了只是默认没启用这和你从 pecl 装的版本会有差异编译前搞清楚能避免很多兼容问题。1.3 想清楚这个版本到底要配合什么环境很多人容易忽略一件事PHP 7.1.31 是源码包你需要在当前系统上完成编译器和依赖库的匹配。我见过有人在 CentOS 6 上强行编译 PHP 7.1结果 GCC 版本太老编译到一半直接报错也见过在 CentOS 7 上编译好的二进制拿到 CentOS 6 上跑不起来提示 GLIBC 版本不对。所以开始之前先确认好你的操作系统版本和架构。# 查看系统信息 cat /etc/redhat-release uname -m gcc --version如果是 CentOS 7 这类老系统GCC 4.8.5 默认是够用的但如果要用到一些新特性或者编译 openssl 扩展可能需要 devtoolset 升级 GCC。这个隐患提前装好后面就不会卡壳。2. 编译参数的设计逻辑比执行命令更重要2.1 先解决依赖否则 configure 会一路报错PHP 源码包编译最烦的就是依赖不全。PHP 7.1 编译需要 libxml2、openssl、curl、libjpeg、libpng、freetype 等一堆开发库。如果你用 CentOS最好把下面这一串一次性装齐yum install -y gcc gcc-c make autoconf libxml2-devel openssl-devel \ curl-devel libjpeg-turbo-devel libpng-devel freetype-devel \ libmcrypt-devel mhash-devel libxslt-develUbuntu/Debian 对应的包名略有差异比如 libxml2-dev、libssl-dev、libcurl4-openssl-dev。这里有个细节PHP 7.1 里mcrypt扩展虽然被标记为废弃但很多老项目还在用所以libmcrypt-devel尽量装。如果系统源里找不到 libmcrypt可以考虑用 epel 源或者干脆在 configure 参数里去掉--with-mcrypt后面用兼容方案替代别让一个扩展挡住整个编译流程。2.2 configure 参数分三组看不要盲目复制一大串网上搜到的编译参数五花八门很多是复制粘帖的万能模板但生产环境一定要按需裁剪。我把参数拆成三组第一组基础路径和运行模式./configure \ --prefix/usr/local/php7.1 \ --with-config-file-path/usr/local/php7.1/etc \ --with-config-file-scan-dir/usr/local/php7.1/etc/conf.d \ --enable-fpm--prefix决定安装目录--enable-fpm开启 php-fpm 模式这两个是必选。--with-config-file-scan-dir是扫描扩展配置文件的目录这个设计很像 /etc/ld.so.conf.d把每个扩展拆成单独的 .ini 文件好管理不容易乱。第二组数据库和常用函数库支持--with-pdo-mysqlmysqlnd \ --with-mysqlimysqlnd \ --with-mysql-sock/var/lib/mysql/mysql.sock \ --with-curl \ --with-openssl \ --with-zlib \ --with-gd \ --with-jpeg-dir \ --with-png-dir \ --with-freetype-dir \ --enable-mbstring \ --enable-gd-native-ttf这里要解释下--with-pdo-mysqlmysqlnd这个参数。mysqlnd 是 PHP 官方维护的 MySQL 驱动和传统的 libmysqlclient 相比它不需要额外安装客户端库而且在内存占用和性能上更好PHP 5.4 之后已经是默认推荐。老项目千万别用--with-mysql这个旧参数PHP 7.0 之后这个扩展直接移除了你写了也没用。第三组性能和相关模块开关--enable-opcache \ --enable-pcntl \ --enable-posix \ --enable-sockets \ --enable-zip \ --enable-exif \ --enable-bcmath \ --enable-intl--enable-opcache一定要开PHP 7 系列配合 Zend OPcache 提升非常明显不开等于白升级。--enable-pcntl和--enable-posix是 CLI 模式下做队列消费、进程管理常用的扩展像 ThinkPHP 的队列任务、Swoole 类项目都依赖它们。--enable-intl涉及国际化如果你不需要多语言或 Unicode 相关功能可以关掉省一点编译时间。2.3 参数取舍的核心思路静态编译还是动态扩展关于扩展的编译方式我个人的习惯是基础且必须的扩展直接编进去比如 openssl、curl、mbstring、pdo_mysql次要的、业务相关的扩展如 redis、mongodb、swoole用phpize动态安装。原因是动态扩展升级方便不用重新编译整个 PHP而基础扩展动态化反而容易出问题比如mbstring重载冲突很多老项目反复踩。不过有一种情况不建议静态编译swoole。这个扩展跟 PHP 主版本耦合度极高必须通过phpize方式单独编译而且每次升级 PHP 都要重新编译 swoole这一点如果你在热搜词里看到 swoole loader 加密 php 文件 就应该有概念它是要做 Zend 扩展兼容的跟普通动态扩展还不一样。3. 编译安装与 php-fpm 落地实操3.1 make 和 make install 的完整流程configure 没有报错之后就进入编译阶段。我建议先make -j2或者make -j4这个参数是并行编译能明显加速。但要注意如果是虚拟机或内存较小的机器并行编译可能把内存吃满甚至导致编译进程被杀。我踩过这个坑在 1G 内存的云主机上make -j4跑到一半直接 oom。保守一点内存 2G 以下用make -j24G 以上再考虑-j4。make -j2 make installmake install会将 PHP 可执行文件、php-fpm、扩展库安装到刚才 configure 指定的 prefix 目录下。整个流程顺利的话大概 5 到 15 分钟取决于机器配置。如果中途报错不要慌常见错误基本集中在依赖缺失或者版本不匹配上后面第四章专门说怎么处理。3.2 php.ini 怎么选关键参数怎么设安装完成后源码目录里有两份默认配置模板php.ini-development和php.ini-production。本地开发可以用 development生产环境必须用 production两者差异主要在错误显示、日志级别和资源限制上。把 production 模板复制到目标配置目录cp php.ini-production /usr/local/php7.1/etc/php.ini然后打开 php.ini 调整几个关键项。第一个是时区不设置的话date 函数会告警而且时间差八小时很多日志系统会因此乱掉。date.timezone Asia/Shanghai第二个是错误显示生产环境一定要关掉 display_errors改成写日志。display_errors Off log_errors On error_log /var/log/php_errors.log第三个是上传和请求体大小老项目经常要传大文件默认的upload_max_filesize2M远远不够按业务调整upload_max_filesize 20M post_max_size 40M max_execution_time 300 max_input_time 300 memory_limit 256M这里有个个经验post_max_size一定要比upload_max_filesize大因为表单里除了文件还有别的字段如果整个请求体超过 post_max_size文件上传会直接失败而且日志里不容易看出来排查半天都找不到原因。3.3 php-fpm 配置与系统服务化php-fpm 的配置在/usr/local/php7.1/etc/php-fpm.conf默认模板在源码目录的php-fpm.conf.default先复制一份cp /usr/local/php7.1/etc/php-fpm.conf.default /usr/local/php7.1/etc/php-fpm.confphp-fpm.conf 里核心要改的是include和运行用户。默认它会 include 一个php-fpm.d/*.conf目录下的 pool 配置文件实际工作主要在这个 pool 配置里完成。打开php-fpm.d/www.conf关键项如下user www group www listen 127.0.0.1:9000 pm dynamic pm.max_children 50 pm.start_servers 10 pm.min_spare_servers 5 pm.max_spare_servers 20pm是进程管理模式dynamic 表示动态调整子进程数。max_children是最大子进程数这个要根据机器内存算假设每个 PHP-FPM 进程平均占用 40M 内存服务器可用内存 4G那 max_children 不要超过 100留出系统和其他服务的内存余量。计算方式很简单但很多人直接复制 50 或 100导致内存不够直接被 CGroup 杀掉。最后把 php-fpm 注册成 systemd 服务这样开机自启和崩溃重启都有保障。新建/etc/systemd/system/php-fpm.service[Unit] DescriptionPHP-FPM 7.1 Afternetwork.target [Service] Typeforking PIDFile/usr/local/php7.1/var/run/php-fpm.pid ExecStart/usr/local/php7.1/sbin/php-fpm ExecReload/bin/kill -USR2 $MAINPID PrivateTmptrue [Install] WantedBymulti-user.target然后执行systemctl daemon-reload systemctl enable php-fpm systemctl start php-fpm到这一步PHP-FPM 就算正式跑起来了。用ps -ef | grep php-fpm能看到一个 master 进程和多个 worker 进程用php -v能确认 CLI 版本用php -m查看已加载的扩展模块。4. 老项目迁移到 PHP 7.1 的填坑实录4.1 mbstring 重复加载警告这个坑在热搜词里出现了说明遇到的人非常多。症状是启动 php-fpm 或执行 php -v 时出现类似这样的警告PHP Warning: Module mbstring is already loaded in Unknown on line 0原因很简单编译时已经通过--enable-mbstring把 mbstring 编进去了而 php.ini 里又有一行extensionmbstring.so或者在 conf.d 目录里有一个 mbstring.ini 文件再次加载。重复加载就会报这个警告。解决办法是把 php.ini 或 conf.d 里那个扩展项注释掉。还有一种隐蔽情况你用的发行版自带 PHP 包比如 yum 安装的 php-mbstring然后你又手动编译了一个 PHP两套环境混在一起导致加载了不同路径的 mbstring.so。这种基本只能把系统自带的 PHP 相关 rpm 包卸载干净避免干扰。4.2 PHP 7.1 移除了哪些老函数ThinkPHP 3.2.3 会不会崩PHP 7.0 开始彻底移除了mysql_*系列函数mysql_connect、mysql_query 等PHP 7.1 再把mcrypt系列标记为废弃deprecated到 7.2 直接移除。ThinkPHP 3.2.3 默认使用 PDO 操作数据库所以mysql_*函数的问题不大但如果你项目里用了 ThirdParty 插件或者自己写的公共函数还调用 mysql_connect那升级后就会直接 fatal error。排查方法很简单项目根目录执行grep -r mysql_connect\|mysql_query\|mysql_fetch_array --include*.php .如果是 mcrypt 加密相关比如老的加密类库在 PHP 7.1 下会提示 deprecated但还能跑。这时候我建议立刻换掉这样的调用因为一旦后面升级到 7.2代码会直接不能运行。开源方案里最常用来替代的是openssl_encrypt和openssl_decrypt提前做好切换。4.3 动态扩展怎么装以 redis 和 openssl 升级为例装 PECL 扩展最标准的方式是cd /usr/local/src curl -O https://pecl.php.net/get/redis-4.3.0.tgz tar -zxvf redis-4.3.0.tgz cd redis-4.3.0 /usr/local/php7.1/bin/phpize ./configure --with-php-config/usr/local/php7.1/bin/php-config make make install装完会在扩展目录生成 redis.so然后在 php.ini 或 conf.d 下加一行extensionredis.so重启 php-fpm 就生效。注意phpize和--with-php-config一定要指向你当前安装的 PHP 7.1 路径不然会把扩展装到系统自带的 PHP 上加载的时候又发现版本对不上。如果是替换 openssl 扩展一般是系统自带的 openssl 版本太旧PHP 代码里用了 openssl_encrypt 的新算法但系统 openssl 库跟不上。这种情况要重新编译 openssl然后在 configure 时指定--with-openssl/usr/local/openssl同时可能要设置环境变量export PKG_CONFIG_PATH/usr/local/openssl/lib/pkgconfig否则 PHP 编译时找不到新版头文件。4.4 安全提醒老版本更要管好反序列化入口热搜词里出现了 PHP 反序列化这是个老生常谈但必须强调的点。PHP 7.1 以后虽然对对象序列化加了很多限制但unserialize()的危险性没有真正消除尤其是老框架里如果存在可控的$_COOKIE或$_POST值直接被unserialize()攻击者完全可以通过构造恶意序列化字符串触发代码执行。这跟版本有关但更跟代码写法有关。我处理过一些老项目经验是第一别把unserialize直接用在用户输入上加一层白名单校验或者用json_decode代替第二开启allow_url_includeOff和filter.default_flags相关的安全配置第三如果项目里用了 ThinkPHP 3.2.3一定要保持核心文件是官方最新修复版不要随便装来路不明的插件。安全这块网上很多谈论反序列化利用的文章看归看防御的本质还是不要把用户输入交给危险函数。5. 我的实操体会把 php-7.1.31.tar.gz 从压缩包变成一台能扛业务的生产环境这套流程我自己前前后后走过了不下十遍。最大的体会是编译安装并不是网上说的“三步走”那么简单真正的难点往往不在安装本身而是后面那一个月里的兼容性排查和参数调优。我强烈建议你在切换环境前先在测试机完整跑一遍业务特别是老项目的 CLI 脚本比如队列任务、定时脚本它们在 PHP 7.1 下暴露的问题往往比 Web 请求更隐蔽。最后分享一个我自己的小技巧编译安装完成后用php -v确认版本用php -m列出所有扩展然后用php -l对老项目所有 PHP 文件做一次语法检查。这个方法能在正式切换前找出 90% 的因版本升级导致的语法级问题。还有一句实话PHP 7.1 已经是官方停止支持很久的版本了如果你只是自己在维护新项目我建议直接上更高的版本但如果真的是老系统搬迁移不动那这套基于源码包自建环境的方式就是你能做的最可控的方案。本文还有配套的精品资源点击获取