Skip to content

💻 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.

go
type PlatformInstaller interface {
    Install() error
}

GetPlatformInstaller

Return corresponding platform installer based on runtime.GOOS.

go
func GetPlatformInstaller(config Config) (PlatformInstaller, error)
GOOSReturn TypeDescription
windows*WindowsInstallerDirect download phar + generate .bat
darwin*MacOSInstallerPrefer Homebrew, fallback to direct download
linux*LinuxInstallerPrefer package manager, fallback to direct download
freebsd/openbsd/netbsd/dragonfly*UnixInstallerGeneric Unix script install
OthernilReturn 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

go
type LinuxInstaller struct {
    config Config
}

func NewLinuxInstaller(config Config) *LinuxInstaller
func (i *LinuxInstaller) Install() error

Install Strategy:

  1. If PreferPackageManager=true: DetectLinuxDistro()InstallComposerViaPackageManager (apt/dnf/yum/pacman/apk/zypper), success and pass exec.LookPath("composer") verification then return.
  2. Otherwise or package manager fails: enter installDirect():
    • Check InstallPath writeability, unwritable and UseSudo=false returns ErrInsufficientRights.
    • If TargetVersion non-empty and not latest: call InstallComposerVersion to fetch specified version phar.
    • Otherwise: download composer-setup.php to temp directory → php composer-setup.php --install-dir=<InstallPath> --filename=composer.phar → generate composer shell wrapper script and chmod 755.

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

go
type MacOSInstaller struct {
    config Config
}

func NewMacOSInstaller(config Config) *MacOSInstaller
func (i *MacOSInstaller) Install() error

Install Strategy:

  1. If PreferBrewOnMac=true: check if brew is in PATH, if yes execute brew install composer, success then return.
  2. Otherwise or brew fails: check InstallPath writeability → download composer-setup.phpphp composer-setup.php generate composer.phar → generate composer shell 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

go
type WindowsInstaller struct {
    config Config
}

func NewWindowsInstaller(config Config) *WindowsInstaller
func (i *WindowsInstaller) Install() error

Install Strategy:

  1. EnsureDirectoryExists(InstallPath) ensure directory exists.
  2. Download composer-setup.php to temp directory.
  3. php composer-setup.php --install-dir=<InstallPath> --filename=composer.phar generate phar.
  4. Generate composer.bat, content @php "<phar>" %*.
  5. Print reminder: need to manually add InstallPath to 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)

go
type UnixInstaller struct {
    config Config
}

func NewUnixInstaller(config Config) *UnixInstaller
func (i *UnixInstaller) Install() error

Install Strategy (for FreeBSD/OpenBSD/NetBSD/DragonFly etc.):

  1. Check InstallPath writeability, unwritable and UseSudo=false returns ErrInsufficientRights.
  2. Download composer-setup.php to temp directory.
  3. php composer-setup.php --install-dir=<InstallPath> --filename=composer.phar (prefix sudo if UseSudo=true).
  4. Generate composer shell wrapper script: UseSudo=true uses echo ... | sudo tee to write and sudo chmod 755; otherwise directly CreateFileWithContent.

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 ChoicePackage manager (apt/dnf/...)Homebrew (brew install composer)Direct download pharDirect download phar
FallbackDirect download setup.phpDirect download setup.php
Outputcomposer.phar + composer(sh)composer.phar + composer(sh)composer.phar + composer.batcomposer.phar + composer(sh)
sudo SupportUseSudo❌ Doesn't read UseSudo❌ No sudo conceptUseSudo
Auto-add PATHWrites to /usr/local/bin etc. already in PATHSame as Linux❌ Need manual addSame as Linux
Version SpecTargetVersion uses InstallComposerVersionUses setup.php (no version spec support)Uses setup.phpUses setup.php

Quick Examples

Directly Get Platform Installer

go
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

go
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

go
cfg := installer.DefaultConfig()
cfg.PreferBrewOnMac = false
mi := installer.NewMacOSInstaller(cfg)
err := mi.Install()

Advanced

  • Why use interface: PlatformInstaller interface lets Installer not care about specific platform, convenient for injecting mock implementations in tests.
  • Custom platform: Implementing PlatformInstaller interface can't directly inject into Installer (it internally hardcodes calling GetPlatformInstaller), but can directly use your own implementation to replace the last step of Installer.Install().
  • Two direct download paths: composer-setup.php (official installer, needs PHP execution) and InstallComposerVersion (direct curl fetch phar + generate wrapper script, for specified version). Linux uses latter when TargetVersion non-empty, other platforms fixed to former.
  • Temp file cleanup: All platforms do defer os.Remove(scriptPath) to clean up downloaded composer-setup.php.

Released under the MIT License