macOS 上使用 Homebrew 安装的 PHP 切换版本
一、查看已安装的 PHP 版本
|
1 |
brew list --versions | grep '^php' |
也可以查看 Homebrew Cellar 中实际存在的 PHP:
|
1 |
ls -d "$(brew --cellar)"/php* 2>/dev/null |
查看当前终端正在使用的 PHP:
|
1 2 |
which php php -v |
二、切换命令行 CLI PHP 版本
假设要从当前版本切换到目标版本 php@X.Y:
|
1 2 3 4 5 |
brew unlink php@当前版本 brew link --overwrite --force php@X.Y hash -r php -v which php |
例如切换到 PHP 7.4:
|
1 2 3 4 |
brew unlink php@7.2 brew link --overwrite --force php@7.4 hash -r php -v |
注意:brew link 切换成功后,当前已经打开的终端会话仍可能继续使用旧 PHP。原因通常是当前 Shell 的 PATH 中仍保留了旧版本的专用目录,或者 Shell 仍保留旧命令查找结果。最简单可靠的处理方式是关闭当前终端窗口/标签页并重新打开一个新的终端会话,然后再次执行 php -v。
这里的“终端”和“Shell”是两个概念:macOS 自带“终端(Terminal)”、iTerm2、Warp 等属于终端软件;zsh、bash、fish 等属于终端中实际运行的 Shell。无论使用哪种终端软件,新开一个会话通常都能重新加载 PATH 配置。
可使用以下命令确认当前 Shell、PHP 查找顺序以及 Homebrew 公共链接:
|
1 2 3 4 |
echo $SHELL ps -p $$ -o command= which -a php ls -l "$(brew --prefix)/bin/php" |
如果 which -a php 中旧版本路径排在 $(brew –prefix)/bin/php 前面,应检查 Shell 配置文件中是否写死了某个 PHP 版本。常见配置文件包括 ~/.zshrc、~/.zprofile、~/.bash_profile、~/.bashrc 和 ~/.config/fish/config.fish。
|
1 |
grep -nEi 'php(@[0-9]+\.[0-9]+)?|opt/php' ~/.zshrc ~/.zprofile ~/.bash_profile ~/.bashrc ~/.profile 2>/dev/null |
建议不要长期在 PATH 中固定 /opt/homebrew/opt/php@X.Y/bin,而统一让 php 走 Homebrew 公共链接 $(brew –prefix)/bin/php。这样以后只需要 brew unlink / brew link 即可切换版本。
若只想刷新当前会话:zsh 或 bash 可执行 hash -r;zsh 也可执行 rehash。若仍显示旧版本,直接新开终端窗口/标签页最简单。对于其他 Shell,可采用其对应的命令缓存刷新方式,或直接重新打开终端会话。
如果同时安装了无版本号的 php 包,必要时先执行:
|
1 |
brew unlink php |
三、Apache 使用 mod_php 时的切换方法
先判断 Apache 是否加载了 PHP 模块:
|
1 |
apachectl -M | grep php |
如果能看到 php7_module、php_module 等,说明 Apache 使用 mod_php。此时仅执行 brew link 不会自动切换 Apache,必须修改 Apache 配置中的 LoadModule。
先查找当前 PHP 模块配置:
|
1 |
grep -RniE 'LoadModule.*php|libphp' "$(brew --prefix)/etc/httpd" 2>/dev/null |
典型配置:
|
1 |
LoadModule php7_module /opt/homebrew/opt/php@7.2/lib/httpd/modules/libphp7.so |
切换到 PHP 7.4 时修改为:
|
1 |
LoadModule php7_module /opt/homebrew/opt/php@7.4/lib/httpd/modules/libphp7.so |
修改前先确认目标模块存在:
|
1 |
ls -l "$(brew --prefix php@7.4)/lib/httpd/modules/" |
然后检查并重启 Apache:
|
1 2 3 4 5 6 |
apachectl configtest # Homebrew Apache brew services restart httpd # 或系统/手工 Apache sudo apachectl restart |
四、Apache 使用 PHP-FPM 时的切换方法
如果 Apache 配置中使用 proxy_fcgi、SetHandler 或 fcgi://127.0.0.1:9000,则通常是 PHP-FPM。查看相关配置:
|
1 |
grep -RniE 'proxy:fcgi|ProxyPassMatch|SetHandler|127.0.0.1:9000' "$(brew --prefix)/etc/httpd" 2>/dev/null |
查看 PHP-FPM 服务:
|
1 |
brew services list | grep php |
切换服务示例:
|
1 2 |
brew services stop php@7.2 brew services start php@7.4 |
确认目标 PHP-FPM 的监听地址:
|
1 |
grep -E '^listen\s*=' "$(brew --prefix)/etc/php/7.4/php-fpm.d/www.conf" |
如果新旧版本都监听同一个地址(例如 127.0.0.1:9000),Apache 配置一般不需要修改;如果监听端口不同,则需要同步修改 Apache 的 FCGI 地址。
五、切换后的验证
CLI:确认 php -v 和 which php。
Apache:确认 apachectl -M | grep php。
PHP-FPM:确认 brew services list | grep php。
浏览器:建议放置临时 phpinfo() 页面确认 Web 端版本,验证后删除。
|
1 2 |
<?php phpinfo(); |
还可检查常用扩展是否存在:
|
1 |
php -m | grep -Ei 'mysqli|pdo_mysql|gd|curl|mbstring|openssl' |
1. 扩展缺失时的处理方法
切换 PHP 版本后,各版本使用的是各自独立的扩展目录和 php.ini。旧版本中存在的扩展,不代表新版本自动具有。先使用以下命令确认当前加载的配置文件、扩展目录和模块:
|
1 2 3 4 |
php -v php --ini php -i | grep ^extension_dir php -m |
常见扩展可分为两类:
① PHP / Homebrew PHP 包自带扩展:如 mysqli、mysqlnd、PDO、pdo_mysql、gd、curl、mbstring、openssl、intl、xml、soap、zip、sockets、bcmath、ldap、pgsql、pdo_pgsql、sqlite3 等。目标 PHP 安装正常时,这类扩展通常随 PHP 一起编译安装。如果切换后缺失,优先检查是否加载了错误的 php.ini,或重新安装该 PHP 版本,而不是直接使用 PECL。
|
1 |
brew reinstall php@X.Y |
② PECL / 第三方扩展:如 redis、imagick、xdebug、mongodb、swoole 等。这些扩展需要针对当前 PHP 版本单独安装或重新编译,不能直接复用另一个 PHP 版本生成的 .so 文件。切换 CLI 到目标 PHP 后再安装,例如:
|
1 2 3 4 5 6 7 |
brew unlink php@旧版本 brew link --overwrite --force php@X.Y hash -r php -v which phpize which php-config pecl install redis |
PECL 安装完成后,如果没有自动写入配置,可先查看当前扫描的配置目录:
|
1 |
php --ini |
然后在当前 PHP 对应的 conf.d 目录创建扩展配置,例如:
|
1 2 |
echo "extension=redis.so" > "$(brew --prefix)/etc/php/X.Y/conf.d/ext-redis.ini" php -m | grep -i redis |
注意:不同 PHP 版本的扩展 API/ABI 通常不同。不要把 php@7.2 的扩展 .so 直接复制到 php@7.4、php@8.x 使用,否则可能出现 Unable to load dynamic library、Module compiled with module API=… 或直接崩溃。
2. 常用扩展参考基线
下面这组模块可作为常用业务 PHP 环境的核对参考。不同 PHP 大版本可能有少量模块增删或名称变化,不要求逐项完全一致,但数据库、字符集、网络、图像、XML、压缩和加密相关模块应重点确认。
|
1 2 3 4 5 6 |
bcmath bz2 calendar ctype curl dba dom exif fileinfo filter ftp gd gettext gmp iconv intl json ldap libxml mbstring mysqli mysqlnd odbc openssl pcntl PDO pdo_dblib pdo_mysql PDO_ODBC pdo_pgsql pdo_sqlite pgsql Phar posix pspell readline Reflection session shmop SimpleXML soap sockets sodium SPL sqlite3 sysvmsg sysvsem sysvshm tidy tokenizer xml xmlreader xmlrpc xmlwriter xsl Zend OPcache zip zlib |
常用项目可重点快速检查:
|
1 |
php -m | grep -Ei 'mysqli|mysqlnd|pdo_mysql|gd|curl|mbstring|openssl|intl|ldap|soap|sockets|xml|zip|opcache' |
若需要判断某个扩展是否真正加载,可使用:
|
1 2 3 |
php --ri mysqli php --ri gd php --ri curl |
Web 环境还需用 phpinfo() 再确认一次,因为 Apache mod_php / PHP-FPM 与 CLI 可能读取不同的 php.ini。确认完成后应删除 phpinfo() 测试文件。
六、回滚方法
如果切换后项目不兼容,可按相反方向恢复。CLI 示例:
|
1 2 3 4 |
brew unlink php@7.4 brew link --overwrite --force php@7.2 hash -r php -v |
mod_php 环境还需要把 Apache 的 LoadModule 路径改回原 PHP 版本,再执行 apachectl configtest 并重启 Apache。PHP-FPM 环境则停止新版本服务并重新启动旧版本服务。
七、常见问题
1. brew link 已切换,但网页仍是旧 PHP
原因通常是 Apache 使用 mod_php 或独立 PHP-FPM。brew link 主要影响 CLI,不代表 Web 服务自动切换。应按第 3 或第 4 节检查。
2. 出现 Library not loaded / *.dylib
通常是旧 PHP 二进制依赖的 Homebrew 动态库已被升级或清理。优先尝试重新安装对应 PHP 版本,使其重新匹配当前依赖:
|
1 |
brew reinstall php@X.Y |
如果 PHP 来自第三方 tap,Homebrew 可能要求先显式信任该 formula 或 tap。不要随意把不同 ABI 版本的 dylib 强行软链接到旧文件名。
3. PHP-FPM 已启动,但 Apache 使用 mod_php
这种情况下 PHP-FPM 服务可能完全没有被 Apache 使用。应以 Apache 配置为准,不要仅凭 brew services list 判断 Web 使用的 PHP 版本。
八、推荐的标准切换流程
1. 查看已安装版本与当前版本。
2. 确认 Apache 使用 mod_php 还是 PHP-FPM。
3. 切换 CLI 的 Homebrew link。
4. 按实际模式切换 Apache 模块或 PHP-FPM 服务。
5. 执行 apachectl configtest。
6. 重启 Apache 或相关服务。
7. 分别用 CLI 和浏览器验证 PHP 版本及扩展。
8. 确认业务正常后,再清理不再使用的旧服务。
提示:生产或长期使用环境中,切换前建议备份 PHP 配置目录(php.ini、conf.d、php-fpm.d)和 Apache 配置,并记录原版本,以便快速回滚。