PHP 7.1.31源码编译与PHP-FPM部署实践
简介PHP 7.1.31 源码包适用于在 Linux 环境中自行编译部署 PHP 的开发与运维人员这一版本引入了 void 返回类型、nullsafe 运算符、类常量可见性等特性。压缩包约 18.8MB包含近 19510 个文件以 phpt 测试用例、C 源码、头文件、PHP 脚本为主并配有 autoconf 配置脚本、扩展目录、测试样例等文件组织有序便于按模块查阅。利用该源码包读者可完成从 configure、make 到 make install 的完整编译实践也可深入阅读核心 Zend 引擎代码、理解扩展机制或依据大量测试样例进行环境自检与排错。结合 php.ini 配置、OPcache 等还能针对性做安全与性能优化。目前已 287 人学习适合希望掌握源码级部署、PHP 内部实现和二次开发能力的中高级开发者。1. 拿到 php-7.1.31.tar.gz 先别急着解压这个版本到底还在被谁需要如果手头正躺着一个 php-7.1.31.tar.gz 压缩包大概率不是闲来无事考古而是某个老系统的线上环境必须按这个版本重新编译一套运行栈。PHP 7.1 的安全支持早已结束但大量业务代码和第三方扩展还停留在这代直接升 7.4 或 8.x 的兼容成本常常比换语言还高。这时候从这个源码包自己编译出可控的 PHP-FPM而不是依赖发行版仓库里被改过的旧包是内网部署、版本锁定和离线交付时最常见的做法。这个包能解决三件事在无网环境还原一致的二进制、避免系统仓库版本被覆盖、把扩展和配置路径统一到自己的目录下。下面从校验、configure、编译、集成到排错走一遍全是可直接照抄的命令和参数也包括我这些年踩过的坑。2. 获取与校验 php-7.1.31.tar.gz下载链路、哈希比对与解压2.1 为什么认准这个源码包而不是一个“PHP 7.1”发行包我在不同项目里都遇到过同一个争论既然系统仓库里也能装出一个 php 7.1为什么非要用这个源码包手动编译答案不是情怀是可控性。发行版仓库的 PHP 会把二进制拆散到 /usr/bin、/usr/lib/php配置散在 /etc/php 下扩展目录挂在系统目录里你很难把一套旧环境原样搬到另一台机器或是另一个机房里。而你手里这个 php-7.1.31.tar.gz 是干净的原始源码树核心、扩展源码、php.ini 模板、php-fpm 配置模板全部集中在一个目录里。编译产物的路径由 configure 的 --prefix 决定扩展目录、配置文件目录完全可控打包迁移时把 /usr/local/php71 整个拿过去就行不碰系统原有的 PHP。另一个关键点是兼容性。PHP 7.1 的扩展 ABI 版本标识是 20160303这个数字体现在编译后的目录名上。只要同一份源码包加上同一份 configure 参数两台机器编出来的扩展目录名一致第三方 .so 才能在编译后到目标机直接加载。换用发行版包编译器、补丁、configure 参数都不可控出现玄学兼容问题时排查成本远高于重编一次。还有一点必须明确源码包编出来的 PHP 不会自动升级。系统仓库里的 7.1 包会跟着安全源打补丁源码包永远是那个快照。所以如果你决定用这个文件就要把它当成一个固定版本锁定来运维并预留安全补丁的处理窗口而不是“装完就不管”。这也是我为什么特别强调校验和记录构建参数后面两章会展开。2.2 下载、校验和解压三步一步不省下载环节我一般先确认文件字节大小再把 sha256 值和信任源提供的哈希值比对。很多内网团队习惯把源码包拷进共享目录拷了几年没人记得源头是否被改过。源码包被替换成带后门版本的事情并不是没有发生过所以我坚持每次拿到包都做哈希校验。# 假设包已经从官方发布源或内网镜像下载到 ~/src $ cd ~/src $ ls -l php-7.1.31.tar.gz $ sha256sum php-7.1.31.tar.gz先看文件大小再记录 sha256sum 输出的 64 位十六进制字符串去和官方发布记录或你初次下载时的哈希归档比对。比对通过后再解压不要先解压后校验。更规范的做法是把哈希写进一个校验文件然后交给 sha256sum 自动比对。$ cat php-7.1.31.sha256 EOF 官方发布记录给出的64位哈希值 php-7.1.31.tar.gz EOF $ sha256sum -c php-7.1.31.sha256看到php-7.1.31.tar.gz: OK才算通过。如果输出FAILED立即停用这个文件重新从可信源拉取。至于 GPG 签名验证能力够的话更好但内网环境往往拿不到公钥或者连不上密钥服务器我平时在交付场景里至少保证 sha256 这一层不出问题。解压时尽量指定到一个源码目录而不是在压缩包所在目录直接解开$ sudo install -d -o $(id -u) -g $(id -g) /usr/local/src $ tar -xf php-7.1.31.tar.gz -C /usr/local/src $ ls /usr/local/src/php-7.1.31/configure-xf表示解压归档文件、不显示详细过程-C指定目标目录。我把 configure 文件是否存在作为解压成功的标志而不是看解压过程有没有刷屏。2.3 进入构建目录先看三样东西版本号、扩展目录和配置模板解压后不要急着跑 configure先花两分钟确认目录状态。打开压缩包这个黑匣子之前应该确认它确实是 7.1.31而不是某个套壳目录或者被裁剪过的精简版。我常看三个位置主目录下的 configure、main/php_version.h、php.ini-production 模板。$ cd /usr/local/src/php-7.1.31 $ grep -m1 PHP_VERSION main/php_version.h $ ls php.ini-development php.ini-production第一行会看到#define PHP_VERSION 7.1.31这个字符串和 tar 包文件名完全对应说明版本没被人为改写。后面两个文件是编译后配置 php.ini 的模板老项目跑生产环境时我一般用 php.ini-production后面第四章会展开。目录里的ext/子目录也值得看一眼它决定了你能编译出哪些扩展。比如老业务要用的 pcntl、zip、mbstring、opcache在 7.1.31 源码里都在这些子目录中但默认 configure 并不会全部启用这是很多编译翻车点的源头。提前看这些目录本身不解决编译问题但能让你在 configure 阶段就知道哪些扩展要额外通过参数去打开而不是等业务报错后再回来重编。2.4 编译前的目录规划给源码包留一个独立工作区源码编译不是一次 configure 就结束它会在源码目录生成大量中间文件扩展目录也会有 .o 和 .lo 中间产物。如果直接把源码解到业务目录里后续清理会非常麻烦。我习惯把 PHP 源码固定放在 /usr/local/src/php-7.1.31并确保这个分区剩余空间至少 2G。同时不要用 root 直接跑 make切换到一个普通构建用户去编译腾不出权限时再用 root 做 make install。普通用户编译、root 安装是源码包交付最顺的分工也避免后续误操作把构建残留清掉。3. 从源码包到可执行 PHP依赖清单、configure 参数与 make 流程3.1 编译依赖少一个头文件configure 上午就开始闹PHP 7.1 的编译依赖不算复杂但它对系统里有没有对应 dev 包非常较真。configure 阶段运行的大多数检查本质是找头文件和动态库找不到就直接报错退出。下表是必备依赖按 Debian/Ubuntu 系和 RHEL 系对照列出依赖包Debian/Ubuntu对应 RHEL 系作用缺失时的典型报错build-essentialDevelopment Tools 组gcc、make、binutilsmake: command not foundautoconfautoconf部分场景重生成 configureconfigure: error: autoconf is missinglibxml2-devlibxml2-develDOM、XML 解析等No package libxml-2.0 foundlibsqlite3-devsqlite-develSQLite3 相关扩展sqlite3.h: No such file or directorylibssl-devopenssl-develOpenSSL 扩展、https 流式访问cannot find OpenSSLs openssl/evp.hlibcurl4-openssl-devlibcurl-develcURL 扩展Please install libcurlpkg-configpkgconfigconfigure 查找库元信息pkg-config: command not found我用得最多的安装命令是这样的# Debian / Ubuntu sudo apt-get update sudo apt-get install -y build-essential autoconf \ libxml2-dev libsqlite3-dev libssl-dev \ libcurl4-openssl-dev pkg-config # RHEL / CentOS 7 系 sudo yum groupinstall -y Development Tools sudo yum install -y autoconf libxml2-devel sqlite-devel \ openssl-devel libcurl-devel pkgconfig单独解释一下 libxml2-devPHP 7.1 源码包里几乎每个常规编译都会先去探测 libxml2因为 XML、DOM、SimpleXML 这些扩展的底层是同一个库。如果只装了一个精简运行环境没有任何开发头文件configure 会第一时间报No package libxml-2.0 found。这是最常见的现象别去怀疑源码包有问题先补依赖即可。3.2 configure 参数拆解每个选项背后是什么到了 configure 这一步目标已经明确把 PHP 装到 /usr/local/php71 独立目录下配置路径也一并固定。我通常会先建好目录并赋予当前构建用户权限然后把参数分两段写进命令行避免一行太长导致漏看。$ cd /usr/local/src/php-7.1.31 $ ./configure \ --prefix/usr/local/php71 \ --with-config-file-path/usr/local/php71/etc \ --with-config-file-scan-dir/usr/local/php71/etc/conf.d \ --enable-fpm \ --with-fpm-userwww-data \ --with-fpm-groupwww-data \ --enable-mbstring \ --enable-zip \ --with-openssl \ --with-curl \ --with-pdo-mysqlmysqlnd \ --with-mysqlimysqlnd \ --enable-opcache逐个说明关键参数--prefix/usr/local/php71安装根目录决定 bin、sbin、lib、etc 都放在这个前缀下。这样不会和系统自带的 /usr/bin/php 冲突卸载时直接删目录即可。--with-config-file-path/usr/local/php71/etcphp.ini 的位置。这个路径必须固定之后php -i输出的 Configuration File Path 要指向这里否则会出现“我改了 php.ini 但页面没变化”的尴尬。--with-config-file-scan-dir/usr/local/php71/etc/conf.d让 PHP 启动时自动扫这个目录加载额外 .ini。后续手动安装扩展往里面丢一个 xxx.ini 就能启用。--enable-fpm配合--with-fpm-user/--with-fpm-group开启 PHP-FPM 编译并让 fpm 子进程默认以 www-data 运行。现在老项目用 mod_php 的已经不多我基本都是 fpm Nginx 的架构。--enable-mbstring、--enable-zip老业务高频使用但默认不是必开的扩展显式打开避免后续补编。--with-openssl、--with-curl让 curl 扩展支持 https依赖上一节提到的 dev 包。--with-pdo-mysqlmysqlnd和--with-mysqlimysqlnd用 mysqlnd 驱动而不是编译时连接某个 MySQL 客户端库。这对离线迁移很重要因为 mysqlnd 不依赖外部 libmysqlclient能少踩一个大坑。--enable-opcache开启 opcode 缓存生产环境必开。configure 跑完输出末尾出现Thank you for using PHP说明配置检查通过。如果中途报错看最后 20 行输出不要看前面一长串的 checking...。最常见就是依赖缺失装好依赖后重新执行同样的 configure 命令即可。configure 可以反复跑不需要像 make 那样担心残留副作用。3.3 make 与 make install并发别贪磁盘要给够依赖和参数都对接下来只是时间问题。make 的速度取决于机器核数和内存。我平时默认用$(nproc)取全部核数但这里必须提醒一个很重要的经验PHP 7.1 编译时每个编译单元都吃内存32G 内存的机器随便跑4G 以下的云主机直接make -j$(nproc)很容易把系统 OOM 掉。$ make -j2 $ sudo make install小内存机器直接make -j2或make -j1不要因为编译慢就焦虑。编译完make install会把可执行文件、库文件、php-fpm 可执行文件和配置模板复制到 --prefix 指定目录。装完先验证两个二进制$ /usr/local/php71/bin/php -v $ /usr/local/php71/sbin/php-fpm -v第一个输出PHP 7.1.31 (cli) (built: ...)第二个输出PHP 7.1.31 (fpm-fcgi)。到这里源码包已经变成一套可用的本地 PHP 环境。如果 shell 里敲 php 找不到命令执行export PATH/usr/local/php71/bin:$PATHsbin 目录也建议加进去否则后面启动 php-fpm 都要写完整路径。这个设置不做后续所有 systemd 和 crontab 也要统一改成绝对路径很容易埋雷。提示make 之前最好确认 configure 参数无误。如果改了 configure 再重新 make建议先make clean再编译避免旧的中间目标文件干扰。4. 让 PHP 7.1.31 真正对外服务php.ini、PHP-FPM 与 Nginx 配置4.1 初始化配置文件从模板复制出 php.ini 和 php-fpm.conf编译出的 PHP 不会自动生成一份可用的运行配置。源码包根目录里有php.ini-production和php.ini-development两个模板前者偏生产display_errors 关闭、error_reporting 更严格后者适合开发。我按生产环境跑用的是 production 模板。$ cd /usr/local/src/php-7.1.31 $ sudo cp php.ini-production /usr/local/php71/etc/php.ini $ sudo install -d /usr/local/php71/etc/conf.d复制完成后顺手验证一下$ /usr/local/php71/bin/php -i | grep Loaded Configuration File如果显示Loaded Configuration File /usr/local/php71/etc/php.ini说明路径对上了。这一步很容易被忽略结果后面改了 php.ini 一点效果都没有因为 PHP 还在找系统默认路径下的 php.ini。PHP-FPM 的配置是另一套。make install 时已经往安装目录放了模板通常叫php-fpm.conf.default和php-fpm.d/www.conf.default直接复制成正式名$ sudo cp /usr/local/php71/etc/php-fpm.conf.default /usr/local/php71/etc/php-fpm.conf $ sudo mkdir -p /usr/local/php71/etc/php-fpm.d $ sudo cp /usr/local/php71/etc/php-fpm.d/www.conf.default /usr/local/php71/etc/php-fpm.d/www.conf再去改 www.conf 里的运行用户、监听方式和权限。7.1.31 的 www.conf 默认是listen 127.0.0.1:9000我很多时候会改成 unix socket因为 Nginx 和 PHP-FPM 在同一台机器时Unix socket 比走 TCP 回环性能更好也少一个端口占用。但 socket 文件目录权限必须对齐否则 Nginx 会一直 502。$ sudo vi /usr/local/php71/etc/php-fpm.d/www.conf # 必改项 user www-data group www-data listen /run/php71-fpm.sock listen.owner www-data listen.group www-data listen.mode 0660改完后用php-fpm -t检查语法能通过再进下一步$ /usr/local/php71/sbin/php-fpm -t这段有个很关键容易被踩的细节虽然编译时已经指定了--with-fpm-userwww-data但 www.conf 里的 user/group 同样要写对。fpm 主进程以 root 启动后会切换为这个用户如果写成了别的用户socket 文件的属主就和 Nginx worker 不一致权限不对直接 502。主配置里includeetc/php-fpm.d/*.conf这一行也要确认存在别漏了子配置引用。4.2 Nginx 侧 fastcgi 配置样板Nginx 这侧其实不复杂最核心的是把 PHP 请求交给 fpm 的 socket并告诉它要执行的脚本路径。提供一个我常用的 server 配置片段server { listen 80; server_name example.local; root /srv/www/legacy/public; index index.php index.html; location ~ \.php$ { try_files $uri 404; include fastcgi_params; fastcgi_pass unix:/run/php71-fpm.sock; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; } location ~ /\.ht { deny all; } }fastcgi_pass指向 unix socket 时格式是unix:/run/php71-fpm.sock注意冒号后紧跟路径不要加空格。SCRIPT_FILENAME是 PHP 执行脚本的绝对路径我在生产里见过很多人漏掉这一行导致 Nginx 请求打到 fpm 后 PHP 找不到文件返回 404 或File not found。try_files $uri 404也不是多余的装饰。不带这一行Nginx 会直接把 location 匹配到的请求交给 fpm即使文件在磁盘上根本不存在。加上这行让 Nginx 先确认文件存在不存在直接返回 404避免把任意路径交给 PHP-FPM 解析这是防任意代码执行的重要手段。项目如果用了前后端分离部署root 路径要改成实际放入口文件的位置。检查配置并 reload$ sudo nginx -t $ sudo systemctl reload nginx4.3 用 systemd 托管 php-fpm启动才不会像野进程一样没人管源码包编译出来的 php-fpm 不会自带 systemd 服务文件。如果直接php-fpm启动它会变成一个裸进程重启机器后不会自动拉起重启动作也不够优雅。我习惯这样定义一个服务单元[Unit] DescriptionPHP 7.1.31 FPM service Afternetwork.target [Service] Typesimple Userwww-data Groupwww-data ExecStart/usr/local/php71/sbin/php-fpm -F -y /usr/local/php71/etc/php-fpm.conf ExecReload/bin/kill -USR2 $MAINPID ExecStop/bin/kill -QUIT $MAINPID Restarton-failure [Install] WantedBymulti-user.target把这段写入/etc/systemd/system/php71-fpm.service然后执行$ sudo systemctl daemon-reload $ sudo systemctl enable --now php71-fpm $ sudo systemctl status php71-fpm解释两个易混点-F表示前台运行不要 daemonize。systemd 的 Typesimple 要求主进程持续在前台否则 systemd 会认为服务已经退出-y指定配置文件路径。如果服务里漏了-Fphp-fpm 会默认抓为守护进程立刻结束systemd 看到进程退出就会反复重启。服务跑起来后最直接的验证是丢一个探针文件$ echo ?php phpinfo(); | sudo tee /srv/www/legacy/public/i.php /dev/null $ curl -I http://example.local/i.php返回HTTP/1.1 200 OK链路就通了。测完记得把 i.php 删掉不要让 phpinfo 暴露在生产目录里。要是返回 502第一反应看两处一是 socket 文件是否存在、属主对不对二是 Nginx worker 进程的用户能不能访问这个 socket。我平时的排查顺序是先ls -l /run/php71-fpm.sock再ps aux | grep php-fpm再去看/usr/local/php71/var/log/php-fpm.log里的日志。这一步能排掉 80% 的 502。注意unix socket 有个隐藏前提Nginx 的 worker 进程用户和 socket 的 owner/group 必须能匹配。如果 Nginx 跑在别的用户下即使 socket 文件是 www-data 的也会 502。5. 编译 php-7.1.31.tar.gz 的避坑指南五个真实排错记录5.1 现象configure 第一行就报 No package libxml-2.0 found这是一个能让新手下意识地以为源码包损坏的问题。configure 输出往往在最后几行抛一句configure: error: No package libxml-2.0 found原因分析PHP 7.1 的编译系统用 pkg-config 检查 libxml2 是否存在它依赖的不是 libxml2 这个运行时库而是开发头文件包。很多精简系统只装了 libxml2.so没有装 libxml2-dev所以检测失败。解决按第三章的依赖清单把 libxml2-dev 装上然后重新跑 configure。装好之后确认版本而不是凭感觉$ pkg-config --modversion libxml-2.0 $ pkg-config --cflags --libs libxml-2.0第二行能显示-I/usr/include/libxml2 -lxml2说明 pkg-config 找得到configure 也会通过。这里我栽过跟头明明安装了 libxml2-dev但 configure 继续报错最后发现是pkg-config本身不存在库信息路径没人去读。那种情况装一下pkg-config或pkgconf就解决了。5.2 现象make 到一半宿主机器卡死进程显示 Killed这个坑我踩得最狠在 4G 内存 8 核的小机器上我一开始顺手用了make -j8。跑了几分钟后内存被打满swap 开始疯狂交换最后进程被内核 OOM Killer 干掉整个终端只留下一句Killed回去看 dmesg 全是 OOM 记录。原因分析PHP 7.1.31 编译过程中单个 C 文件编译所需的虚拟内存并不小8 个并行任务同时存在时总内存需求远超物理内存。内核只能杀掉一个吃内存最多的进程来保命。解决编译阶段量力而为用 -j 参数控制并发。内存小的机器把并发降到 2 或者干脆 1$ make -j1 $ sudo make install如果连 -j1 都在中途被杀先去检查 swap 和磁盘空间$ free -h $ df -h .df是为了确认 /usr/local/src 所在分区有足够剩余空间PHP 7.1 整个 build 目录编译下来通常在 1G 到 2G 之间空间不足同样会伪造出类似 OOM 的失败。补充一个习惯make -j1虽然慢但它至少不会把线上机器的其他服务带走。编译源码包这种事慢不丢人把整台机器搞挂才麻烦。5.3 现象make 成功、页面却报 PHP Warning: Unable to load dynamic library curl.so现象分两步编译时没有任何错装完扩展目录里也有curl.so但 PHP 一启动就在输出流里打一串警告说Unable to load dynamic library curl.so。用php -m查看curl 扩展不在其中。原因分析这个老版本源码在编译 curl 扩展时链接到了编译机上某个版本的 libcurl/openssl而运行环境里系统库升级了符号对不上。PHP 启动时去 dlopen 这个 .so动态链接器找不到符号版本加载失败PHP 就把整个扩展禁掉了。解决先看真正缺什么而不是急着重编。$ ldd /usr/local/php71/lib/php/extensions/no-debug-non-zts-20160303/curl.so $ /usr/local/php71/bin/php -m | grep curlldd 输出里如果出现libcurl.so.4 not found或libssl.so.1.1 not found就说明运行库里缺对应版本。安装或升级系统 libcurl/openssl 到配套版本再试一次。如果版本就是不可调和比如编译机上单独源码安装了新版 openssl而系统库里没有那就把 /usr/local/lib 或 /usr/local/ssl/lib 显式加进/etc/ld.so.conf执行ldconfig。这里要记住一个结论在没有完全理解动态库依赖前不要随手把别的机器编译好的 PHP 目录拷到另一台机器。源码包编出来的二进制大多数是动态链接不是静态可移植。跨机器移植前要么统一运行时库要么在目标机上重新走一遍 configure 加 make。5.4 现象扩展目录里明明有 zip.sophp -m 就是不显示这类问题最容易发生在老项目里业务代码里调用 ZipArchive测试环境测着测着直接 fatal报Class ZipArchive not found。但你去 extension_dir 一看zip.so 就躺在那里php.ini 里 extension 也写了。原因分析PHP 的扩展除了 ABI 版本要匹配外还依赖该扩展在 configure 阶段是否被编译进 build 系统。php-7.1.31.tar.gz 的 zip 扩展默认未必会开。如果 configure 阶段没有传--enable-zipmake install 过程可能根本不会生成这个 .so你看到的 zip.so 也许是别处拷贝或旧目录残留。PHP 启动时如果发现 .so 的 ABI 或路径不对就会拒绝加载但不一定每次都打警告。解决最可靠的做法是回到源码目录重新 configure 加上缺失的扩展并保留原有参数。这里要介绍 PHP 自带的一个工具configure 跑完会生成 config.nice它把完整的 configure 参数录下来了。重编时执行$ cd /usr/local/src/php-7.1.31 $ ./config.nice --enable-zip $ make -j2 $ sudo make install这样保留原有参数只追加新选项。然后去 php.ini 里把extensionzip.so的注释去掉重启 php-fpm$ /usr/local/php71/sbin/php-fpm -t $ sudo systemctl restart php71-fpm $ /usr/local/php71/bin/php -m | grep zip如果不想重新 make 整个 PHP单独编译这一个扩展也可以用 phpize 路线这是第六章要展开的技巧。这里先记住看到扩展文件存在不要下意识认为是路径问题先检查它到底是不是当前 ABI 编译出来的。5.5 现象phar、openssl 扩展提示版本冲突或者执行时报 internal error这类翻车在编译完老项目用的分析类函数时相当典型表现为 PHP 启动时警告PHP Warning: PHP Startup: Unable to load dynamic library phar.so以及 php -v 直接输出一段和 extension_dir 相关的报错。原因分析PHP 7.1 的 ABI 标识是 20160303任何没有跟主程序一起编译的扩展 .so 都不能直接用。很多人把同一个 tar.gz 在 A 机器编完的 ext/ 目录拷贝到 B 机器上然后 B 机器用同一份源码重编结果混装了不同时间的 .so 文件PHP 启动时就炸了。另一个分支原因php.ini 里手动指定了extension/旧目录/xxx.so和当前主进程的构建参数不一致。解决核心是把扩展回归到和主程序同一次编译。别跨构建目录复用 .so。在 php.ini 里不要写绝对路径改成相对扩展文件名extensionopenssl.so extensionphar.so让 PHP 在编译出来的 extension_dir 里自动加载路径错误会一目了然。每次重编后把/usr/local/php71/lib/php/extensions/no-debug-non-zts-20160303/下的 .so 全部清理一遍再让 make install 重新生成。如果仍报错把php -i | grep extension_dir的输出和目录列表两相对照几乎立即能发现不匹配。这比在 php.ini 里改十遍都管用。6. 用 config.nice 固定 PHP 7.1.31 构建参数第二次编译不翻车上一章的坑几乎全部源于第二次编译时忘了第一次的参数。PHP 源码树里其实自带一个后悔药那就是 configure 成功后在源码根目录生成的config.nice脚本。它把这次 configure 调用的完整参数原样写成一行命令重编时不需要再去记忆或翻终端历史。$ cd /usr/local/src/php-7.1.31 $ grep -- --prefix config.nice $ ./config.nice --enable-pcntl第一行能看到之前设置的 prefix 和所有参数第二行执行时会自动保留旧参数并追加新参数。对 7.1.31 来说追加一个默认关闭的扩展例如 pcntl然后重新 make。建议先make clean再 make虽然多花一点时间但能避免旧目标文件在改动 configure 后造成的奇怪行为。如果要单独编译源码包内某个扩展不重编整个 PHP最常用的是 phpize 流程。以 pcntl 为例$ cd /usr/local/src/php-7.1.31/ext/pcntl $ /usr/local/php71/bin/phpize $ ./configure --with-php-config/usr/local/php71/bin/php-config $ make $ sudo make installphpize会根据当前 PHP 的 ABI 生成独立构建环境编出的 .so 放到扩展目录。之后在 php.ini 或 conf.d 里加入extensionpcntl.so重启 php-fpm 再用php -m | grep pcntl验证。这套流程能把源码包里三十多个默认未启用但已经存在的扩展逐个补上不必为了一个扩展整体重编。最后分享一个我现在已经固化的操作习惯拿到一个 tar.gz第一件事不是解压就开跑而是把校验值、configure 参数、依赖清单写进一个 build 脚本脚本头部放版本说明和哈希记录。我之前吃过特别大的亏两台机器编译 7.1.31一台漏了 --enable-mbstring同样的老代码在 A 机上字符函数全正常B 机上 strlen 对中文按字节数处理线上页面乱成一片排查一整晚最后才发现是编译参数不一致。从那以后我要求所有源码包里都保留 config.nice 和构建脚本。这个习惯比任何编译加速都值钱因为它保证你在项目生命周期里能拿到完全一致的两套可复现环境。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

网络基础物理层详解:从网线到协议栈,搞懂数据封装与传输

网络基础物理层详解:从网线到协议栈,搞懂数据封装与传输

简介:这份 docx 文档面向网络初学者与嵌入式开发入门者,系统梳理网络基础中物理层与数据交换的核心概念,帮助读者理解数据从应用层到物理层的完整流转过程。内容围绕数据封装与解封装展开,将封装比作快递包裹,说明上层…

2026/10/17 0:45:07 阅读更多 →
E-office8.0注册机避坑指南:授权校验原理与实操流程

E-office8.0注册机避坑指南:授权校验原理与实操流程

简介:E-office8.0注册机是一款面向协同办公系统部署与运维人员的辅助工具,主要解决E-office8.0版本在安装调试、功能验证阶段遇到的授权激活问题,适合需要搭建测试环境或进行系统评估的技术人员使用。资源以rar压缩包形式提供,包内…

2026/10/17 0:45:07 阅读更多 →
基于LoRA的Qwen2.5-7B微调实操:从AutoDL到模型合并

基于LoRA的Qwen2.5-7B微调实操:从AutoDL到模型合并

简介:面向有一定机器学习基础的研究人员和开发者,这份PDF教程以Qwen2.5-7B-Instruct为例,系统讲解在AutoDL平台完成大模型微调的完整路径。文档从账号注册、实名认证与4090云GPU选型开始,逐步覆盖实例创建、社区镜像选择、SSH/VSC…

2026/10/17 0:45:07 阅读更多 →

最新新闻

基于MP2642GR-Z与STM32F405RG的两串电池被动均衡方案设计与实现

基于MP2642GR-Z与STM32F405RG的两串电池被动均衡方案设计与实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 1:23:21 阅读更多 →
双节锂电主动均衡原理与MP2642GR-Z+PIC18LF4682实战设计

双节锂电主动均衡原理与MP2642GR-Z+PIC18LF4682实战设计

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 1:23:21 阅读更多 →
双电芯串联电池均衡的工程落地:MP2642GR-Z与PIC18LF4458实战方案

双电芯串联电池均衡的工程落地:MP2642GR-Z与PIC18LF4458实战方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 1:23:21 阅读更多 →
Arduino RGB灯模块入门:从共阳共阴到彩虹渐变与声控律动

Arduino RGB灯模块入门:从共阳共阴到彩虹渐变与声控律动

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 1:23:21 阅读更多 →
战地6启动失败、闪退、进不去?从驱动到BIOS的保姆级排查指南

战地6启动失败、闪退、进不去?从驱动到BIOS的保姆级排查指南

战地6启动失败、闪退、进不去,这三个词儿这几天在我们玩家群里几乎成了高频问候语。你问十个人,可能七个会告诉你“换电脑”,但其实大多数情况根本不是硬件不够猛,而是系统环境和游戏文件之间在较劲。这篇保姆级教程专门帮你解决这…

2026/10/17 1:23:21 阅读更多 →
基于MP2642GR-Z与STM32F765ZI的储能电池主动均衡方案设计与实现

基于MP2642GR-Z与STM32F765ZI的储能电池主动均衡方案设计与实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 1:22:20 阅读更多 →

日新闻

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

简介:《DeepSeek使用方法.pdf》是一份面向DeepSeek入门及进阶用户的完整使用指南,重点围绕V3与R1两款模型的能力差异与适用场景展开,帮助读者根据任务类型与成本选择合适的模型。资源共1个PDF文件,压缩包大小约311KB,篇…

2026/10/17 0:00:25 阅读更多 →
200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

简介:这是一份聚焦DeepSeek提示词优化的《200Deepseek润色指令》PDF文档,共收录两百余条可直接套用的润色指令,涵盖语法修正、风格统一、逻辑梳理、表达精炼等维度,并针对商务写作、学术论文、广告文案等场景给出差异化建议。文档…

2026/10/17 0:00:25 阅读更多 →
OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

简介:本资源是一份基于OpenCV的本科课程设计完整报告,面向计算机视觉初学者、图像处理学习者及高校通信/信息类专业学生,解决物体智能计数与检测结果结构化记录的实际问题。项目覆盖图像预处理、轮廓检测、边界框绘制、轨迹栏交互式参数调节、…

2026/10/17 0:00:25 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/16 21:35:51 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/15 19:41:51 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/16 15:04:24 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 21:54:38 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 14:23:36 阅读更多 →