1. Mac 上 PHP 开发环境到底难在哪
在 Mac 上折腾 PHP 开发环境,几乎每个后端或全栈方向的人都经历过一段不太愉快的时光。macOS 自带的 PHP 版本往往偏旧,而且随着系统升级会被直接移除;用 Homebrew 装 PHP 又经常遇到依赖冲突、扩展缺失、配置文件路径混乱的问题;想同时维护多个项目、跑不同 PHP 版本时,切换成本更是高得离谱。我自己最早是用 Homebrew 手动装 PHP,再单独编译扩展,后来换成 Docker 方案,虽然隔离性好,但每次改配置都要重建镜像,调试体验并不算顺畅。
FlyEnv 这个工具就是冲着这些痛点来的。它把 PHP、Nginx、MySQL、Redis、Node.js 等常用开发组件打包成一套可视化管理方案,在 Mac 上装完之后,基本不用再碰命令行去处理环境问题。这篇文章我会从实际使用角度出发,把 FlyEnv 在 Mac 上的安装、配置、多版本管理、常见坑点全部拆开讲一遍,适合正在被 PHP 环境折磨的开发者,也适合刚接触 Mac 开发环境的新手参考。
提示:本文所有操作均基于 macOS 本地开发场景,不涉及任何服务器部署或网络代理相关内容。
2. 为什么我最终选了 FlyEnv 而不是继续手搓
2.1 传统 Mac PHP 环境方案的三个死结
在聊 FlyEnv 之前,先说说我踩过的那些坑,这样你才能理解为什么一个“一站式”工具值得单独写一篇。
第一个死结是Homebrew 的版本管理混乱。Homebrew 默认只保留最新版本的 PHP,想装 PHP 7.4 或者 8.0 需要额外 tap,而且不同版本之间的扩展路径、ini 文件位置都不一样。我试过在一台机器上同时装 PHP 7.4 和 PHP 8.2,结果php -v输出的版本和php-fpm实际加载的版本对不上,排查了半天才发现是 PATH 顺序问题。
第二个死结是扩展安装门槛高。PHP 的很多扩展(比如 imagick、swoole、redis)在 Mac 上需要先装系统级依赖,再用 pecl 编译。每次系统升级或者 PHP 版本切换,这些扩展都要重新编译一遍,非常消耗时间。
第三个死结是服务管理分散。Nginx、MySQL、Redis 各自用不同的方式启动,有的用 brew services,有的用 launchctl,端口冲突、进程残留是家常便饭。尤其是 MySQL,经常出现“上次没关干净,这次启动报端口占用”的情况。
2.2 FlyEnv 的核心思路:把环境当“应用”来管
FlyEnv 的设计逻辑和上面这些方案完全不同。它不依赖系统级的包管理器去装 PHP,而是自己维护一套独立的运行时目录,每个组件(PHP、Nginx、MySQL 等)都是独立安装、独立配置、独立启停的。你可以把它理解成一个“开发环境的应用商店”,需要什么就装什么,装完在界面里点一下就能启动。
这种设计带来的直接好处有三个。一是版本切换零成本,装多个 PHP 版本后,在界面里点一下就能切换全局默认版本,不需要改 PATH 或重启终端。二是配置可视化,Nginx 的 vhost、PHP 的 ini、MySQL 的 my.cnf 都能在界面里直接编辑,改完点保存就生效,不用去记那些深埋在/usr/local/etc里的路径。三是服务状态一目了然,哪个服务在跑、占哪个端口、日志在哪,界面上直接看得到。
2.3 和其他方案的横向对比
| 方案 | 版本切换 | 扩展管理 | 服务管理 | 上手成本 | 适合场景 |
|---|---|---|---|---|---|
| Homebrew 手动装 | 麻烦 | 需编译 | 分散 | 高 | 喜欢完全掌控的资深用户 |
| Docker | 简单 | 镜像内解决 | 集中 | 中 | 需要环境隔离的团队 |
| MAMP | 简单 | 受限 | 集中 | 低 | 只跑简单项目 |
| FlyEnv | 简单 | 界面化 | 集中 | 低 | 多版本、多项目的日常开发 |
Docker 方案虽然隔离性好,但在 Mac 上文件挂载的性能损耗比较明显,尤其是跑 Laravel 这种文件读写频繁的框架时,响应速度会慢一截。FlyEnv 是原生运行,没有这层损耗,这也是我最终选它的重要原因。
3. Mac 上安装 FlyEnv 的完整流程
3.1 安装前的系统检查和准备
在装 FlyEnv 之前,有几个前置条件需要确认。首先是 macOS 版本,建议在 macOS 12 及以上,老版本系统可能会遇到运行时库不兼容的问题。其次是磁盘空间,FlyEnv 本身不大,但如果你打算装多个 PHP 版本加 MySQL,建议预留至少 10GB 空间。
检查系统版本可以直接在终端里跑:
sw_vers输出里ProductVersion就是当前系统版本。另外确认一下当前用户是否有管理员权限,因为 FlyEnv 安装某些组件时需要写入/Applications目录。
注意:如果你的 Mac 是 Apple Silicon 芯片(M1/M2/M3),下载时一定要选 arm64 版本,选成 x86 版本虽然能通过 Rosetta 运行,但性能会打折扣,而且某些扩展可能编译失败。
3.2 下载与首次启动的注意事项
FlyEnv 官网提供了 macOS 的 dmg 安装包,下载后直接拖进 Applications 文件夹即可。首次打开时,macOS 可能会提示“无法验证开发者”,这是因为应用没有走 App Store 签名流程。解决办法是在“系统设置 - 隐私与安全性”里找到对应的提示,点击“仍要打开”。
启动后,FlyEnv 会引导你选择数据目录。默认是在用户目录下的.flyenv文件夹,我建议保持默认,不要改到外置硬盘或者 iCloud 同步目录里,因为数据库文件放在同步目录下容易出问题。
首次启动还会让你选择要安装的基础组件。这里不用全选,按需来就行。我一般先装 PHP、Nginx、MySQL 这三个,Redis 和 Node.js 后面用到再装。
3.3 安装 PHP 时的版本选择逻辑
FlyEnv 的 PHP 版本列表覆盖了从 5.6 到 8.3 的主流版本。选哪个版本取决于你的项目需求。如果是维护老项目,可能需要在 PHP 7.4 上跑;如果是新项目,直接上 PHP 8.2 或 8.3。
我自己的做法是装三个版本:PHP 7.4 用来兼容老代码,PHP 8.1 作为主力开发版本,PHP 8.3 用来测试新特性。装完之后在 FlyEnv 的 PHP 管理界面里,可以给每个版本单独配置 ini 参数和扩展。
安装过程中 FlyEnv 会自动下载对应的运行时包,速度取决于网络情况。如果下载卡住,可以检查一下是否有其他程序占用了大量带宽。
3.4 配置 Nginx 和 MySQL 的关键参数
Nginx 装完后,默认会监听 80 端口。如果你的 Mac 上已经有其他程序占了 80 端口(比如系统自带的 Apache),需要先在 FlyEnv 里把 Nginx 的监听端口改成 8080 或者其他空闲端口。
MySQL 的默认配置里,max_connections是 151,对于本地开发来说够用了。但如果你经常跑大量并发测试,可以调到 500。另外innodb_buffer_pool_size默认值偏小,建议调到 256M 或 512M,能明显改善查询性能。
[mysqld] max_connections = 500 innodb_buffer_pool_size = 512M character-set-server = utf8mb4 collation-server = utf8mb4_unicode_ci改完配置后记得在界面上点“重启”让配置生效,直接关掉再开有时候不会重新加载配置文件。
4. 多版本 PHP 切换与扩展管理的实操细节
4.1 全局版本与项目级版本的切换方式
FlyEnv 的 PHP 版本切换分两个层级。全局切换是在 PHP 管理界面里点“设为默认”,这个操作会修改终端里php命令指向的版本。项目级切换则是通过 FlyEnv 生成的 vhost 配置来实现的,每个站点可以指定用哪个 PHP 版本处理。
举个例子,我有两个项目,一个跑在 PHP 7.4 上,一个跑在 PHP 8.2 上。在 FlyEnv 的“网站”模块里,分别给两个项目建 vhost,然后在每个 vhost 的 PHP 版本选项里选对应的版本。这样访问不同的本地域名时,Nginx 会自动把请求转发给对应版本的 PHP-FPM 处理,互不干扰。
这个机制的原理是 FlyEnv 为每个 PHP 版本单独启动了一个 php-fpm 进程,监听不同的 socket 或端口,Nginx 根据 vhost 配置里的fastcgi_pass指向不同的进程。
4.2 常用扩展的一键安装与手动编译
FlyEnv 的 PHP 扩展管理界面里,列出了常用的扩展如 redis、imagick、swoole、xdebug 等,勾选后点安装即可。它背后做的事情是下载预编译好的扩展包,放到对应 PHP 版本的扩展目录,然后在 ini 里加上extension=行。
但并不是所有扩展都有预编译包。遇到没有的扩展时,就需要手动编译。FlyEnv 提供了“手动安装扩展”的入口,你需要先把扩展源码下载到本地,然后在界面里指定源码路径,它会调用对应版本的phpize和php-config来编译。
提示:手动编译扩展前,确保已经通过 FlyEnv 安装了对应 PHP 版本的开发工具包(包含 phpize 和 php-config),否则编译会报找不到命令。
4.3 php.ini 的修改与生效验证
FlyEnv 里每个 PHP 版本都有独立的 php.ini 文件,可以在界面上直接打开编辑。常见的修改包括调整upload_max_filesize、post_max_size、memory_limit等。
改完之后怎么验证生效了?最直接的方法是在终端里跑:
php -i | grep memory_limit如果输出的值和你改的一致,说明生效了。但要注意,终端里的php命令用的是全局默认版本,如果你改的是非默认版本的 ini,需要在 FlyEnv 里切换到那个版本再验证,或者直接通过浏览器访问phpinfo()页面查看。
4.4 扩展加载顺序引发的典型问题
PHP 扩展的加载顺序有时候会影响功能。比如opcache必须在其他扩展之前加载,xdebug如果加载太早可能会和某些扩展冲突。FlyEnv 默认的加载顺序是经过测试的,一般不需要改。但如果你手动添加了扩展,发现某个功能异常,可以检查一下 ini 里extension=行的顺序。
我遇到过一次redis扩展和igbinary扩展的加载顺序问题,导致 redis 序列化数据时出错。把igbinary调到redis前面就好了。这类问题比较隐蔽,排查时可以先看 PHP 错误日志,里面通常会有扩展加载相关的警告。
5. 本地站点配置与数据库管理的实战操作
5.1 用 FlyEnv 建站:从域名到根目录
在 FlyEnv 里建一个本地站点,流程比手动改 Nginx 配置简单很多。点“新建网站”,填几个关键信息就行:域名(比如myproject.test)、根目录(指向项目的 public 目录)、PHP 版本、是否开启 HTTPS。
域名解析这块,FlyEnv 会自动帮你写 hosts 文件,把myproject.test指向 127.0.0.1。不需要手动去改/etc/hosts,省了一步操作。如果你用的域名后缀不是.test,比如.local,可能会和 macOS 的 Bonjour 服务冲突,建议统一用.test后缀。
根目录的选择有个细节:Laravel 项目要指向public目录,ThinkPHP 也是指向public,而 WordPress 则是指向项目根目录。选错了会导致 404 或者源码泄露,建站时注意一下。
5.2 HTTPS 本地证书的生成与信任
FlyEnv 支持给本地站点开启 HTTPS,它会自动生成自签名证书。但自签名证书浏览器默认不信任,访问时会弹安全警告。解决办法是把 FlyEnv 生成的根证书导入到 macOS 的钥匙串里,并设为“始终信任”。
具体操作是:在 FlyEnv 的证书管理界面里导出根证书,然后双击导入钥匙串,在“系统”钥匙串里找到这个证书,右键“显示简介”,在“信任” section 里把“使用此证书时”改为“始终信任”。这样之后所有由这个根证书签发的站点证书都会被浏览器信任。
5.3 MySQL 数据库的创建、导入与导出
FlyEnv 内置了数据库管理工具,可以直接在界面上创建数据库、执行 SQL、导入导出数据。对于日常开发来说,最常用的操作是导入生产环境的脱敏数据到本地。
导入时有个坑要注意:如果 SQL 文件比较大(超过 100MB),直接在界面里导入可能会超时。这时候可以用命令行方式:
mysql -h 127.0.0.1 -P 3306 -u root -p your_database < backup.sqlFlyEnv 的 MySQL 默认 root 密码是空的,如果你改了密码,记得在命令里用-p参数输入。另外导入前最好先确认一下 SQL 文件的字符集,如果是utf8mb4的,数据库创建时也要用utf8mb4,否则中文会乱码。
5.4 Redis 和 Node.js 的联动配置
现在很多 PHP 项目会用到 Redis 做缓存或队列,前端可能还用 Node.js 跑构建工具。FlyEnv 里这两个组件也是独立管理的。
Redis 装完后默认监听 6379 端口,PHP 项目里配置 Redis 连接时,host 写127.0.0.1,port 写6379就行。如果 Redis 设置了密码,记得在 PHP 的 Redis 配置里同步。
Node.js 的版本管理 FlyEnv 也支持,可以装多个版本并按项目切换。对于前端项目来说,这个功能很实用,因为不同项目依赖的 Node 版本可能不一样。
6. 常见问题排查与避坑经验实录
6.1 端口冲突的排查思路
端口冲突是本地开发环境最常见的问题。表现是服务启动失败,日志里提示Address already in use。排查方法是先用lsof查一下端口被谁占了:
lsof -i :80 lsof -i :3306 lsof -i :6379输出里会显示占用端口的进程 PID 和名称。如果是之前没关干净的进程,直接kill -9 PID干掉就行。如果是系统服务占用了,比如 macOS 自带的 Apache 占了 80 端口,可以在 FlyEnv 里把 Nginx 端口改成 8080。
6.2 PHP-FPM 启动失败的几种原因
PHP-FPM 启动失败通常有几个原因。一是配置文件语法错误,比如 ini 里多了一个引号或者少了一个分号。二是 socket 文件路径不存在或权限不对。三是依赖的扩展加载失败,导致 php-fpm 进程直接退出。
排查时先看 FlyEnv 里的错误日志,里面会显示具体的错误行。如果是配置文件问题,FlyEnv 通常会提示哪一行有语法错误。如果是扩展问题,可以先把可疑的扩展注释掉,再逐个启用来定位。
6.3 数据库连接被拒绝的解决路径
“Connection refused” 这个错误在 MySQL 上很常见。原因可能是 MySQL 服务没启动、端口不对、或者绑定地址限制。FlyEnv 的 MySQL 默认绑定127.0.0.1,如果你从 Docker 容器里连,需要改成0.0.0.0。
另一个常见原因是 MySQL 8.0 之后的认证插件变了,默认用caching_sha2_password,老版本的 PHP 客户端可能不支持。解决办法是在 MySQL 里把用户的认证方式改成mysql_native_password:
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'your_password'; FLUSH PRIVILEGES;6.4 系统数据清理与 FlyEnv 的磁盘占用
Mac 用久了“系统数据”会越来越大,FlyEnv 的运行时文件、日志、数据库文件也会占不少空间。定期清理是有必要的。
FlyEnv 的日志文件可以在界面上直接清空,数据库的 binlog 如果不需要也可以关掉。另外每个 PHP 版本的运行时目录里会有一些缓存文件,可以在切换版本时清理一下。我一般每个月检查一次,把不用的 PHP 版本和对应的数据库备份删掉,能释放好几个 G 的空间。
6.5 常见问题速查表
| 问题现象 | 可能原因 | 解决方式 |
|---|---|---|
| Nginx 启动报端口占用 | 80 端口被其他程序占用 | 改 Nginx 端口或关掉占用程序 |
| PHP-FPM 启动失败 | ini 语法错误或扩展加载失败 | 查看错误日志,注释可疑扩展 |
| MySQL 连接被拒绝 | 服务未启动或认证插件不兼容 | 启动服务,改认证方式为 native |
| 本地域名无法访问 | hosts 未生效或 DNS 缓存 | 检查 hosts,刷新 DNS 缓存 |
| HTTPS 证书不受信任 | 根证书未导入钥匙串 | 导出根证书并设为始终信任 |
| 扩展安装后不生效 | ini 未加载或加载顺序问题 | 检查 ini 中 extension 行顺序 |
7. 我个人的使用体会与几个实用建议
用 FlyEnv 这段时间,最大的感受是它把“环境管理”这件事从“需要专门花时间维护”变成了“几乎不用管”。以前每次换项目或者系统升级,都要花半天时间重新配环境,现在这些操作在界面上点几下就完成了。
有几个小建议可以分享。一是不要装太多 PHP 版本,虽然切换方便,但每个版本都占空间,而且扩展要分别装,维护成本会上升。一般两到三个版本足够覆盖大部分场景。二是定期备份数据库,FlyEnv 的数据库文件在数据目录里,虽然本地开发数据丢了影响不大,但有些测试数据积累起来也不容易。三是关注 FlyEnv 的更新,新版本通常会修复一些兼容性问题,尤其是 macOS 系统升级后,及时更新能避免很多莫名其妙的报错。
另外,如果你之前用 Homebrew 装过 PHP,装 FlyEnv 之前最好先把 Homebrew 的 PHP 卸载掉,避免 PATH 冲突。卸载命令是brew uninstall php,如果有多个版本就逐个卸载。卸载完记得检查一下~/.zshrc里有没有残留的 PHP 路径配置,有的话一并清理掉。
最后再分享一个小技巧:FlyEnv 的网站配置支持“复制”功能,建新站点时可以复制已有站点的配置,改一下域名和根目录就行,比从头建快很多。对于需要频繁建测试站点的场景,这个功能很省事。