编译安装php7.4的一些踩坑和探索

周六进行服务器数据迁移时踩的坑。浅浅记录一下探索过程,以防未来再度踩坑。

事情是这样的。

先前的云服务器快到期了,由于续费很贵,于是我换了一家服务商,最近在断断续续利用闲暇时间做数据迁移。

在这个服务器上我有一个dokuwiki的页面,平时用来存放一些笔记和日记的内容,但是迁移到新机器以后,我发现dokuwiki的页面显示出了些问题,所有页面元素渲染全部不正确。查看控制台日志发现在进行 GET http://125.122.22.249/doku/dokuwiki/lib/exe/css.php?t=dokuwiki&tseed=f690c2922e3bb08ba4733d36a34533de 时收到了500 Internal Server Error错误。进一步排查日志,怀疑是php的问题。

原先那台机器上的php版本为7.4,新机器的系统是Ubuntu 22.04.5,能够用apt安装到的只有php8以上的版本,而dokuwiki的框架是以php7的语法编写的,因此存在一些兼容性的问题(例如, PHP Fatal error: Array and string offset access syntax with curly braces is no longer supported in /var/www/html/doku/dokuwiki/vendor/marcusschwarz/lesserphp/lessc.inc.php on line 761 )。

要解决这个问题,有两种策略:①升级dokuwiki到支持php8的版本;②降级php到php7的版本。

经过一些并不严谨的搜索,我以为dokuwiki依然没有提供php8支持(但后来发现,好像只要升级到最新版就能支持php8),于是采取了第二种策略,降级php到php7。以下是详细步骤:

一、准备工作

使用apt移除已有的php版本

1
sudo apt autoremove php

然后下载php-7的源代码包,将其解压至 /opt 目录,并进入这一目录。

1
2
3
4
cd /opt
sudo wget https://www.php.net/distributions/php-7.4.33.tar.gz
sudo tar -zxvf php-7.4.33.tar.gz
cd php-7.4.33

二、编译、测试与运行

编译前,需要安装下面几个工具库。(这一块的内容可能有所不同,取决于 ./configure 阶段会发生什么样的报错。简单来说,./configure 阶段说缺乏哪个工具,就安装那个工具)

1
2
3
sudo apt install pkg-config
sudo apt install libxml2-dev
sudo apt install libsqlite3-dev

进行make前的configuration:

1
sudo ./configure --enable-fpm # 启动fpm模块的编译

这里额外补充一点知识:当服务器上的httpd是由apache2控制时,php有两种不同的工作模式。

  • 第一种方式:Mod-PHP(Apache PHP 模块),即PHP 直接嵌入 Apache,并通过 libphp 模块执行 PHP 代码。这种模式是 apt install php 安装的默认工作模式,只需要 apt install libapache2-mod-php 就可以工作。
  • 第二种方式:FastCGI(php-fpm 进程),即PHP 以 php-fpm 服务的形式,运行在单独的进程池中,Apache 通过 mod_proxy_fcgiphp-fpm 进程通信。

我们现在编译安装php,要选择的工作模式是FastCGI(php-fpm 进程)模式,这种模式编译比较简单(不需要生成so文件然后去apache2那边再做配置),且进程独立,比较好控制。

image.png

完成configuration以后,我们进行编译与安装的步骤:

1
2
3
sudo make
sudo make install
sudo make test # 这一步是可选步骤,仅用于测试编译是否成功

image.png

编译过程大约需要十多分钟,在此期间我们可以稍作休息。编译完成之后运行 make install ,则php的相关文件就会被复制到相应的系统路径下(如上图),其中包括 /usr/local/bin/,/usr/local/sbin/ 等。

保险起见,我们此时最好编辑一下 /etc/profile 文件,将上述路径写入环境变量PATH当中(如下图, PATH=$PATH:/usr/local/bin/:/usr/local/lib/:/usr/local/sbin/

image.png

三、配置php自身的配置文件

编译安装好的php,其配置文件路径在 /usr/local/etc 目录下,主要包括 /usr/local/etc/php-fpm.conf/usr/local/etc/php-fpm.d/www.conf ,前者用于php本身的配置,后者用于工作池(pool)的配置。两个以 .default 结尾的文件是配置文件模板,如果配置文件不存在,可以使用cp指令从模板文件创建新的配置文件。

1
2
3
4
5
6
7
8
9
cyclin@vm:/usr/local/etc$ tree
.
├── php-fpm.conf
├── php-fpm.conf.default
└── php-fpm.d
├── www.conf
└── www.conf.default

1 directory, 4 files

第一个配置文件 /usr/local/etc/php-fpm.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[global]
pid = run/php-fpm.pid
error_log = log/php-fpm.log
syslog.facility = daemon
syslog.ident = php-fpm
log_level = notice
log_limit = 4096
log_buffering = no
emergency_restart_threshold = 0
emergency_restart_interval = 0
process_control_timeout = 0

daemonize = yes
rlimit_files = 1024
rlimit_core = 0
events.mechanism = epoll


include=/usr/local/etc/php-fpm.d/www.conf

第二个配置文件 /usr/local/etc/php-fpm.d/www.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[www]
user = www-data
group = www-data
listen = /run/php/php7.4-fpm.sock

listen.owner = www-data
listen.group = www-data
listen.mode = 0660


pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3

这里的 listen 字段指定了php进程和apache2进程之间的通信方式。 listen = /run/php/php7.4-fpm.sock 即通过 /run/php/php7.4-fpm.sock 套接字文件进行通信。另一种通信方式是 listen = 127.0.0.1:9000 ,通过9000网络端口进行通信。相比之下,sock套接字文件的通信效率高一点,因此我们也采取这种方法进行配置。

可以使用 php-fpm -t 检测配置文件是否编写正确。如果输出 ”test is successful“(如下代码块),则表明一切正常。

1
2
cyclin@vm:/usr/local/etc/php-fpm.d$ sudo php-fpm -t
[18-Jan-2025 23:44:07] NOTICE: configuration file /usr/local/etc/php-fpm.conf test is successful

四、将php-fpm设置为系统服务

现代Linux系统使用systemctl管理系统服务,其配置文件全部存放在 /etc/systemd/system/ 目录下。我们在这里创建一个文件 php7.4-fpm.service ,即可创建相关服务(服务的名称就是 php7.4-fpm

1
2
cd /etc/systemd/system/
sudo vim php7.4-fpm.service #使用vim编辑器创建这个文件。读者也可以换用nano、emacs或其他编辑器

在这个文件中,写入以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[Unit]
Description=PHP 7.4 FastCGI Process Manager
After=network.target

[Service]
ExecStartPre=/bin/mkdir -p /run/php
ExecStartPre=/bin/chown www-data:www-data /run/php
ExecStart=/usr/local/sbin/php-fpm --nodaemonize
ExecStop=/bin/kill -QUIT $MAINPID
Restart=always
User=root
Group=root
PIDFile=/run/php/php7.4-fpm.pid

[Install]
WantedBy=multi-user.target

解释一下 ExecStartPre 字段的意思:这里设置的是每次启动服务前需要执行的操作。对于php-fpm的服务来说,我们需要提前执行的操作有两个,分别是 /bin/mkdir -p /run/php/bin/chown www-data:www-data /run/php ,即创建 /run/php 目录并赋予www-data 访问权限。因为php服务需要在这个目录下创建套接字文件,但是php服务本身的权限不足以创建和修改这个目录,因此我们在这里使用root用户的身份进行提前创建和赋权。

完成上述配置以后,我们启动php-fpm服务,并查看服务状态:

1
2
3
4
sudo systemctl daemon-reload     #重新加载所有系统服务
sudo systemctl enable php7.4-fpm #设置开机自启动
sudo systemctl start php7.4-fpm #立即启动服务
sudo systemctl status php7.4-fpm #检查服务运行状态

如果服务运行正常,我们可以看到类似下面这样的输出:

image.png

如果服务运行不正常,则 sudo systemctl status php7.4-fpm 会输出错误状态,此时我们也可以使用 sudo journalctl -u php7.4-fpm --no-pager | tail -30 查看更详细的报错信息,以便排查错误。

五、配置apache2方面的配置文件

当我们在浏览器中访问一个php页面时(例如 xxx.com/index.php ),apache2会去查询服务器上的 /var/www/html/index.php 这个文件,然后调用php进行php代码的执行,随后将执行完代码的html版本的页面返回给浏览器。因此,apache2与php的进程间通信很重要。使用apt安装的apache2默认没有开启与php的进程间通信,我们需要手动开启。

首先,我们创建配置文件 /etc/apache2/conf-available/php7.4-fpm.conf ,在这个文件中写入下面的内容:

1
2
3
4
5
<IfModule mod_proxy_fcgi.c>
<FilesMatch "\.php$">
SetHandler "proxy:unix:/run/php/php7.4-fpm.sock|fcgi://localhost/"
</FilesMatch>
</IfModule>

随后,通过下面的指令启动php支持并重启apache2的服务:

1
2
3
4
sudo apt install libapache2-mod-fcgid # 安装apache2的相关功能模块
sudo a2enmod proxy_fcgi setenvif # 启动FastCGI 和setenvif模块,其中FastCGI用于代理访问 php-fpm
sudo a2enconf php7.4-fpm # 确保 `php7.4-fpm` 相关的 Apache 配置已启用
sudo systemctl restart apache2 # 重启apache2服务。

完结撒花!

要测试php的效果,可以使用下面的指令创建一个phpinfo文件:

1
echo "<?php phpinfo(); ?>" | sudo tee /var/www/html/info.php

如果一切顺利,当我们使用浏览器访问这一页面时,将会看到如下图的内容:

1737217852558.png

以上。