💻 平台安装器
按操作系统把「装 Composer」这件事分发到不同的平台实现——Linux 优先包管理器、macOS 优先 Homebrew、Windows 直接下 phar、其他类 Unix 走通用脚本。
包路径:github.com/scagogogo/composer-skills/pkg/installer
PlatformInstaller 接口
所有平台安装器实现同一接口,只有一个 Install() 方法。
type PlatformInstaller interface {
Install() error
}GetPlatformInstaller
根据 runtime.GOOS 返回对应的平台安装器。
func GetPlatformInstaller(config Config) (PlatformInstaller, error)| GOOS | 返回类型 | 说明 |
|---|---|---|
windows | *WindowsInstaller | 直接下载 phar + 生成 .bat |
darwin | *MacOSInstaller | 优先 Homebrew,失败回退直接下载 |
linux | *LinuxInstaller | 优先包管理器,失败回退直接下载 |
freebsd/openbsd/netbsd/dragonfly | *UnixInstaller | 通用 Unix 脚本安装 |
| 其他 | nil | 返回 ErrUnsupportedPlatform 包装错误 |
Installer.Install() 内部正是先做 PHP 检查与 Linux 包管理器尝试,再调 GetPlatformInstaller 拿到平台实现执行。
各平台安装器
🐧 LinuxInstaller
type LinuxInstaller struct {
config Config
}
func NewLinuxInstaller(config Config) *LinuxInstaller
func (i *LinuxInstaller) Install() errorInstall 策略:
- 若
PreferPackageManager=true:DetectLinuxDistro()→InstallComposerViaPackageManager(apt/dnf/yum/pacman/apk/zypper),成功并通过exec.LookPath("composer")验证即返回。 - 否则或包管理器失败:进入
installDirect():- 检查
InstallPath可写性,不可写且UseSudo=false返回ErrInsufficientRights。 - 若
TargetVersion非空且非latest:调InstallComposerVersion拉指定版本 phar。 - 否则:下载
composer-setup.php到临时目录 →php composer-setup.php --install-dir=<InstallPath> --filename=composer.phar→ 生成composershell 包装脚本并chmod 755。
- 检查
Linux 的两层包管理器尝试
Installer.Install() 在调 GetPlatformInstaller 之前已经试过一次包管理器,LinuxInstaller.Install() 内部又会试一次。两次尝试都失败才会走直接下载,保证「能用包管理器就用包管理器」的优先级最大化。
🍎 MacOSInstaller
type MacOSInstaller struct {
config Config
}
func NewMacOSInstaller(config Config) *MacOSInstaller
func (i *MacOSInstaller) Install() errorInstall 策略:
- 若
PreferBrewOnMac=true:检查brew是否在 PATH,是则执行brew install composer,成功即返回。 - 否则或 brew 失败:检查
InstallPath可写 → 下载composer-setup.php→php composer-setup.php生成composer.phar→ 生成composershell 包装脚本。
macOS 路径下不会用 UseSudo 写包装脚本(直接 CreateFileWithContent),因此 InstallPath 必须当前用户可写——Homebrew 安装时通常落到 /opt/homebrew/bin 或 /usr/local/bin(brew 用户组可写)。
🪟 WindowsInstaller
type WindowsInstaller struct {
config Config
}
func NewWindowsInstaller(config Config) *WindowsInstaller
func (i *WindowsInstaller) Install() errorInstall 策略:
EnsureDirectoryExists(InstallPath)确保目录存在。- 下载
composer-setup.php到临时目录。 php composer-setup.php --install-dir=<InstallPath> --filename=composer.phar生成 phar。- 生成
composer.bat,内容@php "<phar>" %*。 - 打印提示:需手动把
InstallPath加到 PATH 环境变量。
Windows 不自动改 PATH
出于权限与安全考虑,Windows 安装器只创建文件并提示用户手动加 PATH,不直接修改系统环境变量。安装后在新终端里 composer 才能被找到。
🐧 UnixInstaller(通用)
type UnixInstaller struct {
config Config
}
func NewUnixInstaller(config Config) *UnixInstaller
func (i *UnixInstaller) Install() errorInstall 策略(适用于 FreeBSD/OpenBSD/NetBSD/DragonFly 等):
- 检查
InstallPath可写性,不可写且UseSudo=false返回ErrInsufficientRights。 - 下载
composer-setup.php到临时目录。 php composer-setup.php --install-dir=<InstallPath> --filename=composer.phar(UseSudo=true时前缀sudo)。- 生成
composershell 包装脚本:UseSudo=true用echo ... | sudo tee写入并sudo chmod 755;否则直接CreateFileWithContent。
通用 Unix 安装器不尝试任何包管理器,因为这些 BSD 系发行版的包管理器各异且 Composer 通常不在官方源里。
各平台策略对比
| 维度 | 🐧 Linux | 🍎 macOS | 🪟 Windows | 🐧 通用 Unix |
|---|---|---|---|---|
| 第一选择 | 包管理器(apt/dnf/...) | Homebrew (brew install composer) | 直接下载 phar | 直接下载 phar |
| 回退 | 直接下载 setup.php | 直接下载 setup.php | — | — |
| 产物 | composer.phar + composer(sh) | composer.phar + composer(sh) | composer.phar + composer.bat | composer.phar + composer(sh) |
| sudo 支持 | ✅ UseSudo | ❌ 不读 UseSudo | ❌ 无 sudo 概念 | ✅ UseSudo |
| 自动加 PATH | 写入 /usr/local/bin 等已在 PATH 的目录 | 同 Linux | ❌ 需手动加 | 同 Linux |
| 版本指定 | TargetVersion 走 InstallComposerVersion | 走 setup.php(不支持指定版本) | 走 setup.php | 走 setup.php |
快速示例
直接拿平台安装器
package main
import (
"log"
"github.com/scagogogo/composer-skills/pkg/installer"
)
func main() {
pi, err := installer.GetPlatformInstaller(installer.SmartConfig())
if err != nil {
log.Fatalf("不支持的平台: %v", err)
}
if err := pi.Install(); err != nil {
log.Fatalf("安装失败: %v", err)
}
}显式用 LinuxInstaller 并禁用包管理器
package main
import (
"log"
"github.com/scagogogo/composer-skills/pkg/installer"
)
func main() {
cfg := installer.DefaultConfig()
cfg.PreferPackageManager = false // 强制走直接下载
cfg.UseSudo = true
li := installer.NewLinuxInstaller(cfg)
if err := li.Install(); err != nil {
log.Fatalf("安装失败: %v", err)
}
}macOS 强制不用 Homebrew
cfg := installer.DefaultConfig()
cfg.PreferBrewOnMac = false
mi := installer.NewMacOSInstaller(cfg)
err := mi.Install()进阶
- 为什么用接口:
PlatformInstaller接口让Installer不关心具体平台,便于测试时注入 mock 实现。 - 自定义平台:实现
PlatformInstaller接口后无法直接注入Installer(它内部硬编码调GetPlatformInstaller),但可以直接用自己的实现替代Installer.Install()的最后一步。 - 直接下载的两条路径:
composer-setup.php(官方安装器,需 PHP 执行)与InstallComposerVersion(直接 curl 拉 phar + 生成包装脚本,用于指定版本)。Linux 在TargetVersion非空时走后者,其他平台固定走前者。 - 临时文件清理:所有平台都会
defer os.Remove(scriptPath)清理下载的composer-setup.php。