💻 Platform Installer
Dispatch "install Composer" to different platform implementations by OS — Linux prefers package manager, macOS prefers Homebrew, Windows directly downloads phar, other Unix-like systems use generic script.
Package path: github.com/scagogogo/composer-skills/pkg/installer
PlatformInstaller Interface
All platform installers implement the same interface, with only one Install() method.
type PlatformInstaller interface {
Install() error
}GetPlatformInstaller
Return corresponding platform installer based on runtime.GOOS.
func GetPlatformInstaller(config Config) (PlatformInstaller, error)| GOOS | Return Type | Description |
|---|---|---|
windows | *WindowsInstaller | Direct download phar + generate .bat |
darwin | *MacOSInstaller | Prefer Homebrew, fallback to direct download |
linux | *LinuxInstaller | Prefer package manager, fallback to direct download |
freebsd/openbsd/netbsd/dragonfly | *UnixInstaller | Generic Unix script install |
| Other | nil | Return ErrUnsupportedPlatform wrapped error |
Installer.Install() internally does PHP check and Linux package manager attempt first, then calls GetPlatformInstaller to get platform implementation and execute.
Each Platform Installer
🐧 LinuxInstaller
type LinuxInstaller struct {
config Config
}
func NewLinuxInstaller(config Config) *LinuxInstaller
func (i *LinuxInstaller) Install() errorInstall Strategy:
- If
PreferPackageManager=true:DetectLinuxDistro()→InstallComposerViaPackageManager(apt/dnf/yum/pacman/apk/zypper), success and passexec.LookPath("composer")verification then return. - Otherwise or package manager fails: enter
installDirect():- Check
InstallPathwriteability, unwritable andUseSudo=falsereturnsErrInsufficientRights. - If
TargetVersionnon-empty and notlatest: callInstallComposerVersionto fetch specified version phar. - Otherwise: download
composer-setup.phpto temp directory →php composer-setup.php --install-dir=<InstallPath> --filename=composer.phar→ generatecomposershell wrapper script andchmod 755.
- Check
Linux's Two-layer Package Manager Attempt
Installer.Install() has already tried package manager once before calling GetPlatformInstaller, LinuxInstaller.Install() internally tries again. Both attempts fail before going to direct download, maximizing "use package manager if available" priority.
🍎 MacOSInstaller
type MacOSInstaller struct {
config Config
}
func NewMacOSInstaller(config Config) *MacOSInstaller
func (i *MacOSInstaller) Install() errorInstall Strategy:
- If
PreferBrewOnMac=true: check ifbrewis in PATH, if yes executebrew install composer, success then return. - Otherwise or brew fails: check
InstallPathwriteability → downloadcomposer-setup.php→php composer-setup.phpgeneratecomposer.phar→ generatecomposershell wrapper script.
Under macOS path it doesn't use UseSudo to write wrapper script (directly CreateFileWithContent), so InstallPath must be writable by current user — Homebrew install usually lands in /opt/homebrew/bin or /usr/local/bin (brew group writable).
🪟 WindowsInstaller
type WindowsInstaller struct {
config Config
}
func NewWindowsInstaller(config Config) *WindowsInstaller
func (i *WindowsInstaller) Install() errorInstall Strategy:
EnsureDirectoryExists(InstallPath)ensure directory exists.- Download
composer-setup.phpto temp directory. php composer-setup.php --install-dir=<InstallPath> --filename=composer.phargenerate phar.- Generate
composer.bat, content@php "<phar>" %*. - Print reminder: need to manually add
InstallPathto PATH environment variable.
Windows Doesn't Auto-modify PATH
For permission and security reasons, Windows installer only creates files and reminds user to manually add PATH, doesn't directly modify system environment variables. After installation, composer can be found in new terminal.
🐧 UnixInstaller (Generic)
type UnixInstaller struct {
config Config
}
func NewUnixInstaller(config Config) *UnixInstaller
func (i *UnixInstaller) Install() errorInstall Strategy (for FreeBSD/OpenBSD/NetBSD/DragonFly etc.):
- Check
InstallPathwriteability, unwritable andUseSudo=falsereturnsErrInsufficientRights. - Download
composer-setup.phpto temp directory. php composer-setup.php --install-dir=<InstallPath> --filename=composer.phar(prefixsudoifUseSudo=true).- Generate
composershell wrapper script:UseSudo=trueusesecho ... | sudo teeto write andsudo chmod 755; otherwise directlyCreateFileWithContent.
Generic Unix installer doesn't attempt any package manager, because these BSD distros have varied package managers and Composer usually isn't in official sources.
Platform Strategy Comparison
| Dimension | 🐧 Linux | 🍎 macOS | 🪟 Windows | 🐧 Generic Unix |
|---|---|---|---|---|
| First Choice | Package manager (apt/dnf/...) | Homebrew (brew install composer) | Direct download phar | Direct download phar |
| Fallback | Direct download setup.php | Direct download setup.php | — | — |
| Output | composer.phar + composer(sh) | composer.phar + composer(sh) | composer.phar + composer.bat | composer.phar + composer(sh) |
| sudo Support | ✅ UseSudo | ❌ Doesn't read UseSudo | ❌ No sudo concept | ✅ UseSudo |
| Auto-add PATH | Writes to /usr/local/bin etc. already in PATH | Same as Linux | ❌ Need manual add | Same as Linux |
| Version Spec | TargetVersion uses InstallComposerVersion | Uses setup.php (no version spec support) | Uses setup.php | Uses setup.php |
Quick Examples
Directly Get Platform Installer
package main
import (
"log"
"github.com/scagogogo/composer-skills/pkg/installer"
)
func main() {
pi, err := installer.GetPlatformInstaller(installer.SmartConfig())
if err != nil {
log.Fatalf("Unsupported platform: %v", err)
}
if err := pi.Install(); err != nil {
log.Fatalf("Installation failed: %v", err)
}
}Explicitly Use LinuxInstaller and Disable Package Manager
package main
import (
"log"
"github.com/scagogogo/composer-skills/pkg/installer"
)
func main() {
cfg := installer.DefaultConfig()
cfg.PreferPackageManager = false // Force direct download
cfg.UseSudo = true
li := installer.NewLinuxInstaller(cfg)
if err := li.Install(); err != nil {
log.Fatalf("Installation failed: %v", err)
}
}macOS Force No Homebrew
cfg := installer.DefaultConfig()
cfg.PreferBrewOnMac = false
mi := installer.NewMacOSInstaller(cfg)
err := mi.Install()Advanced
- Why use interface:
PlatformInstallerinterface letsInstallernot care about specific platform, convenient for injecting mock implementations in tests. - Custom platform: Implementing
PlatformInstallerinterface can't directly inject intoInstaller(it internally hardcodes callingGetPlatformInstaller), but can directly use your own implementation to replace the last step ofInstaller.Install(). - Two direct download paths:
composer-setup.php(official installer, needs PHP execution) andInstallComposerVersion(direct curl fetch phar + generate wrapper script, for specified version). Linux uses latter whenTargetVersionnon-empty, other platforms fixed to former. - Temp file cleanup: All platforms do
defer os.Remove(scriptPath)to clean up downloadedcomposer-setup.php.