起因

CentOS 7 官方源里的 PHP 停在 5.4,跑不了要求 PHP 7 的新项目,而第三方 rpm 源又未必敢往生产机上装——于是回到最原始也最可控的路:源码编译安装。步骤多,但每一步都在自己手里。

需求

  • CentOS 7 上编译安装 PHP 7.4(含 php-fpm)
  • fpm 以独立 www 用户运行,装在 /usr/local/php 下,方便整目录卸载

技术实现原理

为什么编译安装的准备工作这么长

yum 装软件时,二进制是别人编好的,依赖由 rpm 关系自动补齐;源码编译则要自己备齐两样东西:编译工具链(gcc、make,即 Development Tools 这组包)和每个依赖库的头文件。因果链是:编译 = 把 C 源码翻译成本机机器码 → 翻译过程中要 include 第三方库的 .h 头文件、链接它们的 .so 库 → 所以每个依赖都要装两份:运行库(如 libxml2)+ devel 开发包(libxml2-devel,里面才是头文件)。后面“报错处理”一节的各类 configure: error,根源几乎都是缺某个 devel 包。

./configure 在干什么

configure 是编译前的探测脚本:挨个检查“你勾选的功能所依赖的库在不在”,生成 Makefile。不在就报 error 停下——所以报错不是灾难,是它在告诉你缺什么。两个关键参数:--prefix=/usr/local/php 决定安装位置(将来卸载 = 删掉这个目录即可);--enable-fpm 决定生成 php-fpm(FastCGI 进程管理器,nginx 就靠它跑 PHP)。

为什么要建 www 用户

php-fpm 以非 root 身份运行是纵深防御:万一 PHP 应用被攻破,攻击者拿到的也只是 www 这个低权限用户,而不是 root。--with-fpm-user=www 参数把运行身份固化进配置。

make 与 make install

make 按 configure 生成的 Makefile 并行翻译源码(机器核多可以 make -j4 提速),make install 把编译产物拷到 –prefix 目录。

1. 下载安装编译工具

1
yum groupinstall 'Development Tools'

2.安装依赖包

1
yum install libxml2 libxml2-devel openssl openssl-devel bzip2 bzip2-devel libcurl libcurl-devel libjpeg libjpeg-devel libpng libpng-devel freetype freetype-devel gmp gmp-devel libmcrypt libmcrypt-devel readline readline-devel libxslt libxslt-devel zlib zlib-devel glibc glibc-devel glib2 glib2-devel ncurses curl gdbm-devel db4-devel libXpm-devel libX11-devel gd-devel gmp-devel expat-devel xmlrpc-c xmlrpc-c-devel libicu-devel libmcrypt-devel libmemcached-devel

3.下载并解压php7.4

1
2
3
wget http://php.net/distributions/php-7.4.0.tar.gz
tar -zxvf php-7.4.0.tar.gz
cd php-7.4.0

4.编译安装(./configure –help 查看编译参数)

编译前,新增用户组、用户。这个用户不是给编译用的,是给 php-fpm 运行时用的(对应下面的 –with-fpm-user/–with-fpm-group),让服务进程脱离 root 身份。

1
2
groupadd www
useradd -g www www

开始编译(根据自己需要增减)

1
./configure   --prefix=/usr/local/php   --with-config-file-path=/etc   --with-fpm-user=www   --with-fpm-group=www    --with-curl   --with-freetype-dir   --enable-gd   --with-gettext    --with-iconv-dir   --with-kerberos   --with-libdir=lib64   --with-libxml-dir   --with-mysqli   --with-openssl   --with-pcre-regex   --with-pdo-mysql   --with-pdo-sqlite   --with-pear   --with-png-dir   --with-jpeg-dir   --with-xmlrpc   --with-xsl   --with-zlib   --with-bz2   --with-mhash   --enable-fpm   --enable-bcmath   --enable-libxml   --enable-inline-optimization   --enable-mbregex   --enable-mbstring   --enable-opcache   --enable-pcntl   --enable-shmop   --enable-soap   --enable-sockets   --enable-sysvsem   --enable-sysvshm   --enable-xml    --enable-zip   --enable-fpm   --enable-yaf   --enable-redis

这里需要注意的是在php7.4 编译参数 –with-gd 要改成了 –enable-gd——GD 图形库在这个版本起改为内置(bundled),不再探测系统的外部 GD,“依赖外部库”型的 –with 自然换成了“打开内置功能”型的 –enable。

报错处理

下面的报错全部发生在 ./configure 阶段:它按顺序探测依赖,报什么就是缺什么,对着补就行。

安装libxml库

yum install libxml2
yum install libxml2-devel -y

安装libzip

报错

1
checking for libzip... configure: error: system libzip must be upgraded to version >= 0.11

解决
先删除旧版本

1
yum remove -y libzip

下载编译安装

1
2
3
4
5
wget https://nih.at/libzip/libzip-1.2.0.tar.gz
tar -zxvf libzip-1.2.0.tar.gz
cd libzip-1.2.0
./configure
make && make install

libsqlite3x

报错

1
error: Package requirements (sqlite3 > 3.7.4) were not met

解决

1
yum install libsqlite3x-devel -y

oniguruma

报错

1
error: Package requirements (oniguruma) were not met

解决

1
yum install oniguruma-devel -y

当yum install 提示 “没可用软件包”

解决

1
yum install -y epel-release

off_t undefined 报错

1
configure: error: off_t undefined; check your library configuration

off_t 类型是在 头文件 unistd.h中定义的,
在32位系统 编程成 long int ,64位系统则编译成 long long int ,
在进行编译的时候 是默认查找64位的动态链接库,
但是默认情况下 centos 的动态链接库配置文件/etc/ld.so.conf里并没有加入搜索路径,
这个时候需要将 /usr/local/lib64 /usr/lib64 这些针对64位的库文件路径加进去。
解决
添加搜索路径到配置文件

1
2
3
4
echo '/usr/local/lib64
/usr/local/lib
/usr/lib
/usr/lib64'>>/etc/ld.so.conf

然后 更新配置

ldconfig -v

zipconf.h 找不到

报错

/usr/local/include/zip.h:59:21: fatal error: zipconf.h: No such file or directory

1
cp /usr/local/lib/libzip/include/zipconf.h /usr/local/include/zipconf.h

编译没问题了

编译安装

执行

1
make && make install

5.配置环境

执行完安装命令后php7.4就已经安装在到了/usr/local/php目录下了

1
/usr/local/php/bin/php -v

查看版本

添加环境变量

1
vim /etc/profile

添加到最后

1
2
PATH=$PATH:/usr/local/php/bin
export PATH

更新环境变量

1
source /etc/profile

查看版本

1
php -v

6.配置php-fpm

源码安装不会自动放配置文件,四行 cp 分别是:php.ini 用生产版模板(production 关闭调试、限制更严,比 development 适合线上)、php-fpm 主配置与 www.conf 池配置从 .default 模板复制、init.d.php-fpm 注册成服务脚本(顺带 chmod +x)。

1
2
3
4
5
cp php.ini-production /etc/php.ini
cp /usr/local/php/etc/php-fpm.conf.default /usr/local/php/etc/php-fpm.conf
cp /usr/local/php/etc/php-fpm.d/www.conf.default /usr/local/php/etc/php-fpm.d/www.conf
cp sapi/fpm/init.d.php-fpm /etc/init.d/php-fpm
chmod +x /etc/init.d/php-fpm

启动php-fpm

1
/etc/init.d/php-fpm start

或者

1
service php-fpm start

踩坑记录与注意事项

  • –with-gd 换成 –enable-gd:7.4 起 GD 改为内置库,参数从“依赖外部”变“启用内置”,老教程照抄必报 unknown option;
  • configure 报错别慌,逐个补:报错本质是依赖探测的结果,报什么缺什么;优先怀疑对应的 -devel 包没装;
  • libzip 是最绕的一个:系统源自带的版本太老(要求 >= 0.11),只能源码装新版;装完又引出 zipconf.h 位置的连锁问题(上文两段解决法)。源码装的库装到 /usr/local 下,头文件路径和 yum 装的不一致是这类连锁问题的根源;
  • off_t 报错的根因:编译默认按 64 位找库,而 /usr/local/lib64 不在动态链接器的搜索配置里,把路径写进 /etc/ld.so.conf 再 ldconfig 刷新缓存(上文做法);
  • “没有可用软件包”先上 EPEL:CentOS 基础源覆盖有限,epel-release 装上再试一次;
  • make -j 加速:核多的机器 make -j$(nproc) 能明显缩短编译时间;
  • 重编译前 make clean:改了 configure 参数重新编译,旧的 .o 文件会干扰,先清理;
  • 验证安装:/usr/local/php/bin/php -v 出版本号、php -m 能列出编译进去的模块(mysqli、gd 等),确认 configure 参数真的生效了。