雖然現代 Linux 發行版提供套件管理器(apt、yum、dnf)快速安裝 PHP,但原始碼編譯安裝仍具備不可替代的優勢:版本自主控制、模組精簡裁剪、效能優化參數調整、安全加固編譯選項。本文以 PHP 8.3 為例,詳述在 CentOS 9 / Ubuntu 22.04 / Debian 12 上的完整編譯流程,涵蓋依賴處理、配置優化、多版本共存與生產環境部署最佳實踐。
# 安裝編譯工具鏈與開發庫
dnf groupinstall -y "Development Tools"
dnf install -y \
libxml2-devel openssl-devel sqlite-devel \
libcurl-devel libpng-devel libjpeg-devel \
freetype-devel oniguruma-devel libzip-devel \
libicu-devel libsodium-devel \
readline-devel libedit-devel \
systemd-devel
apt update && apt install -y \
build-essential pkg-config \
libxml2-dev libssl-dev libsqlite3-dev \
libcurl4-openssl-dev libpng-dev libjpeg-dev \
libfreetype6-dev libonig-dev libzip-dev \
libicu-dev libsodium-dev \
libreadline-dev libedit-dev \
libsystemd-dev
# 建議從官方 GitHub Release 或鏡像站下載
VERSION="8.3.10"
wget "https://www.php.net/distributions/php-${VERSION}.tar.xz"
wget "https://www.php.net/distributions/php-${VERSION}.tar.xz.asc"
# GPG 簽名驗證(確保原始碼完整性)
gpg --keyserver keyserver.ubuntu.com --recv-keys 0x5A52880781F755608BF815FC910DEB46F53EA312
gpg --verify php-${VERSION}.tar.xz.asc php-${VERSION}.tar.xz
# 解壓
tar -xf php-${VERSION}.tar.xz && cd php-${VERSION}
./configure \
--prefix=/usr/local/php/8.3 \
--with-config-file-path=/usr/local/php/8.3/etc \
--with-config-file-scan-dir=/usr/local/php/8.3/etc/conf.d \
--enable-fpm \
--with-fpm-user=www-data \
--with-fpm-group=www-data \
--enable-mbstring \
--enable-xml \
--enable-bcmath \
--enable-sockets \
--enable-pcntl \
--enable-mysqlnd \
--with-mysqli=mysqlnd \
--with-pdo-mysql=mysqlnd \
--with-pdo-sqlite \
--with-openssl \
--with-curl \
--with-zlib \
--with-zip \
--with-gd \
--with-jpeg \
--with-freetype \
--with-iconv \
--with-gettext \
--with-readline \
--enable-opcache \
--enable-gd-jis-conv \
--enable-intl \
--enable-calendar \
--enable-exif \
--enable-ftp \
--enable-shmop \
--enable-sysvsem \
--enable-sysvshm \
--enable-sysvmsg \
--disable-short-tags \
--disable-rpath \
--disable-debug \
--disable-phpdbg \
--disable-cgi \
--with-pear \
--with-sodium \
--with-password-argon2 \
--with-argon2=libsodium \
--with-libxml \
--with-system-ciphers \
--with-kerberos \
--with-ldap \
--with-ldap-sasl \
--with-snmp \
--with-tidy \
--with-xmlrpc \
--with-xsl \
--with-ffi \
--enable-embed=shared \
--enable-litespeed \
CFLAGS="-O3 -pipe -march=native -mtune=native -fstack-protector-strong -fPIE" \
LDFLAGS="-Wl,-O1 -Wl,--as-needed -Wl,-z,relro -Wl,-z,now -fPIE" \
CPPFLAGS="-D_FORTIFY_SOURCE=2"
| 參數 | 用途 | 建議 |
|---|---|---|
--enable-fpm | 啟用 PHP-FPM 進程管理器 | 生產環境必選 |
--enable-opcache | 啟用 Zend Opcache 緩存 | 效能關鍵,必須開啟 |
--with-mysqli=mysqlnd | 使用原生驅動連接 MySQL | 比 libmysqlclient 效能更好 |
CFLAGS="-O3 -march=native" | 針對當前 CPU 架構深度優化 | 生產環境建議開啟 |
--disable-debug --disable-phpdbg | 移除除錯符號與 phpdbg | 減小二進位體積、提升安全性 |
--with-password-argon2 | 支援 Argon2id 密碼雜湊 | 現代密碼儲存標準 |
# 利用所有 CPU 核心平行編譯(-j$(nproc)) make -j"$(nproc)" # 執行測試套件(可選,耗時較長) # make test # 安裝 make install # 複製生產環境配置檔 cp php.ini-production /usr/local/php/8.3/etc/php.ini # 建立 php-fpm 配置 cp /usr/local/php/8.3/etc/php-fpm.conf.default /usr/local/php/8.3/etc/php-fpm.conf cp /usr/local/php/8.3/etc/php-fpm.d/www.conf.default /usr/local/php/8.3/etc/php-fpm.d/www.conf
; /usr/local/php/8.3/etc/php.ini [PHP] engine = On short_open_tag = Off precision = 14 output_buffering = 4096 zlib.output_compression = Off implicit_flush = Off unserialize_callback_func = serialize_precision = -1 disable_functions = exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source,pcntl_exec,pcntl_fork,pcntl_signal,pcntl_waitpid,pcntl_wexitstatus,pcntl_wifexited,pcntl_wifsignaled,pcntl_wifstopped,pcntl_wstopsig,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_getpriority,pcntl_setpriority disable_classes = zend.enable_gc = On expose_php = Off max_execution_time = 30 max_input_time = 60 memory_limit = 256M error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT display_errors = Off display_startup_errors = Off log_errors = On log_errors_max_len = 1024 ignore_repeated_errors = Off ignore_repeated_source = Off report_memleaks = On html_errors = Off variables_order = "GPCS" request_order = "GP" register_argc_argv = Off auto_globals_jit = On post_max_size = 50M auto_prepend_file = auto_append_file = default_mimetype = "text/html" default_charset = "UTF-8" doc_root = user_dir = enable_dl = Off file_uploads = On upload_max_filesize = 50M max_file_uploads = 20 allow_url_fopen = On allow_url_include = Off default_socket_timeout = 60 [CLI Server] cli_server.color = On [Date] date.timezone = Asia/Taipei [Pdo_mysql] pdo_mysql.default_socket = [MySQLi] mysqli.allow_local_infile = Off mysqli.allow_persistent = On mysqli.max_links = -1 mysqli.cache_size = 2000 mysqli.default_port = 3306 mysqli.default_socket = mysqli.default_host = mysqli.default_user = mysqli.default_pw = mysqli.reconnect = Off [mysqlnd] mysqlnd.collect_statistics = On mysqlnd.collect_memory_statistics = Off [OpCache] zend_extension=opcache.so opcache.enable=1 opcache.enable_cli=1 opcache.memory_consumption=256 opcache.interned_strings_buffer=16 opcache.max_accelerated_files=20000 opcache.revalidate_freq=60 opcache.fast_shutdown=1 opcache.save_comments=1 opcache.enable_file_override=0 opcache.validate_timestamps=1 opcache.jit=1255 opcache.jit_buffer_size=64M [Session] session.save_handler = files session.save_path = "/var/lib/php/sessions" session.use_strict_mode = 1 session.use_cookies = 1 session.use_only_cookies = 1 session.name = PHPSESSID session.auto_start = 0 session.cookie_lifetime = 0 session.cookie_path = / session.cookie_domain = session.cookie_httponly = 1 session.cookie_secure = 1 session.cookie_samesite = "Lax" session.gc_probability = 1 session.gc_divisor = 1000 session.gc_maxlifetime = 1440 session.sid_length = 26 session.sid_bits_per_character = 5 [Assertion] zend.assertions = -1 assert.active = Off [GD] gd.jpeg_ignore_warning = 1
; /usr/local/php/8.3/etc/php-fpm.d/www.conf [www] user = www-data group = www-data listen = /run/php/php8.3-fpm.sock listen.owner = www-data listen.group = www-data listen.mode = 0660 listen.allowed_clients = 127.0.0.1 pm = dynamic pm.max_children = 100 pm.start_servers = 10 pm.min_spare_servers = 5 pm.max_spare_servers = 20 pm.process_idle_timeout = 10s pm.max_requests = 500 pm.status_path = /status ping.path = /ping ping.response = pong request_terminate_timeout = 30s request_slowlog_timeout = 5s slowlog = /var/log/php-fpm/www-slow.log rlimit_files = 65535 rlimit_core = 0 catch_workers_output = yes clear_env = no security.limit_extensions = .php .php8 .phtml env[HOSTNAME] = $HOSTNAME env[PATH] = /usr/local/bin:/usr/bin:/bin env[TMP] = /tmp env[TMPDIR] = /tmp env[TEMP] = /tmp php_admin_value[error_log] = /var/log/php-fpm/www-error.log php_admin_flag[log_errors] = on php_admin_value[memory_limit] = 256M php_admin_value[open_basedir] = /var/www/html:/tmp:/var/lib/php/sessions
cat > /etc/systemd/system/php8.3-fpm.service <<'EOF' [Unit] Description=The PHP 8.3 FastCGI Process Manager After=network.target [Service] Type=notify PIDFile=/run/php/php8.3-fpm.pid ExecStart=/usr/local/php/8.3/sbin/php-fpm --nodaemonize --fpm-config /usr/local/php/8.3/etc/php-fpm.conf ExecReload=/bin/kill -USR2 $MAINPID PrivateTmp=true RuntimeDirectory=php RuntimeDirectoryMode=0755 [Install] WantedBy=multi-user.target EOF systemctl daemon-reload systemctl enable --now php8.3-fpm
# /etc/nginx/conf.d/php.conf
upstream php_backend {
server unix:/run/php/php8.3-fpm.sock;
}
server {
listen 80;
server_name example.com;
root /var/www/html;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass php_backend;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_intercept_errors off;
fastcgi_buffer_size 16k;
fastcgi_buffers 4 16k;
fastcgi_connect_timeout 300s;
fastcgi_send_timeout 300s;
fastcgi_read_timeout 300s;
}
location ~ /\.(ht|git|svn) {
deny all;
}
}
採用目錄隔離 + systemd 管理 + Nginx upstream 切換:
# 安裝 PHP 8.2 並行版本
./configure --prefix=/usr/local/php/8.2 --with-config-file-path=/usr/local/php/8.2/etc ...
make -j$(nproc) && make install
# 建立獨立 systemd 服務
# php8.2-fpm.service 對應 /usr/local/php/8.2/sbin/php-fpm
# Nginx 切換 upstream 指向不版本 socket
# upstream php_backend { server unix:/run/php/php8.2-fpm.sock; }
# 檢查版本與模組 /usr/local/php/8.3/bin/php -v /usr/local/php/8.3/bin/php -m # 測試 PHP-FPM 狀態 curl -s https://127.0.0.1/status?json | jq . # 簡易壓測 ab -n 10000 -c 100 https://example.com/info.php
| 現象 | 可能原因 | 解決方案 |
|---|---|---|
| configure: error: libxml2 not found | 缺少開發套件 | 安裝 libxml2-devel / libxml2-dev |
| make 失敗:undefined reference to `libiconv` | iconv 庫路徑問題 | 加上 --with-iconv=/usr/local 或安裝 libiconv-devel |
| PHP-FPM 啟動失敗:address already in use | Socket 檔案殘留或端口衝突 | 刪除 /run/php/php8.3-fpm.sock 或檢查 port 9000 佔用 |
| Opcache 不生效 | zend_extension 路徑錯誤 | 確認 zend_extension=opcache.so 路徑正確,或使用絕對路徑 |
原始碼編譯安裝 PHP 雖然步驟較多,但能獲得極致的效能與安全性控制。關鍵在於:選對優化編譯旗標、啟用 Opcache JIT、調整 PHP-FPM 進程模型、硬化 php.ini 安全配置。建議建立自動化腳本(Ansible/Shell/CI/CD)實現可重複部署,並定期關注 PHP 官方安全公告進行版本更新。