🐧 Distro Detection & PHP
Identify Linux distribution, match package manager, install PHP as needed, install and uninstall Composer by version — all low-level capabilities in the installer package related to "Linux environment" and "PHP runtime" are here.
Package path: github.com/scagogogo/composer-skills/pkg/installer
DistroInfo
Linux distribution information.
type DistroInfo struct {
ID string // Distro identifier: ubuntu / centos / arch / alpine ...
Name string // Human-readable name: Ubuntu / CentOS ...
Version string // Version number: 22.04 / 9 ...
PackageManager string // Package manager: apt / yum / dnf / pacman / apk / zypper / emerge
}DetectLinuxDistro
Detect current Linux distribution.
func DetectLinuxDistro() (*DistroInfo, error)Returns not running on Linux error on non-Linux systems. Detection falls back in the following priority order:
| Order | Detection Method | Description |
|---|---|---|
| 1️⃣ | parseOSRelease() | Read /etc/os-release (modern standard), parse ID/NAME/VERSION_ID |
| 2️⃣ | detectViaLSBRelease() | Execute lsb_release -a, parse Distributor ID/Release |
| 3️⃣ | detectViaKnownFiles() | Check distro-specific files (/etc/redhat-release, /etc/debian_version, /etc/arch-release, /etc/alpine-release, /etc/SuSE-release, /etc/gentoo-release) |
| 4️⃣ | detectViaAvailablePkgManager() | Probe which of apt-get/dnf/yum/pacman/apk/zypper/emerge is available, return *-like identifier |
Last level fallback returns &DistroInfo{ID: "unknown", Name: "Unknown Linux", PackageManager: "unknown"}, never returns nil.
Distro → Package Manager Mapping
determinePkgManager(distroID) maps distro ID to package manager:
| Distro ID | Package Manager |
|---|---|
ubuntu/debian/linuxmint/pop/elementary/kubuntu/xubuntu/linuxlite | apt |
fedora | dnf |
centos/rhel/redhat/oracle/amzn/almalinux/rocky | dnf (if available) otherwise yum |
arch/manjaro/endeavouros/garuda | pacman |
alpine | apk |
opensuse/opensuse-leap/opensuse-tumbleweed/sles | zypper |
gentoo | emerge |
void | xbps |
solus | eopkg |
| Other | unknown |
InstallComposerViaPackageManager
Install Composer using system-native package manager.
func InstallComposerViaPackageManager(distro *DistroInfo, useSudo bool) (bool, error)| Parameter | Type | Description |
|---|---|---|
distro | *DistroInfo | Distro info, nil or PackageManager=="unknown" returns (false, nil) |
useSudo | bool | Whether to prefix package manager command with sudo |
Returns (attempted bool, err error):
attempted=truemeans matched package manager found and command executed (regardless of success or failure).attempted=falsemeans no matching package manager (no error, caller should fallback to other methods).
Commands executed per package manager:
| Package Manager | Command |
|---|---|
apt | apt-get install -y composer |
dnf | dnf install -y composer |
yum | yum install -y composer |
pacman | pacman -S --noconfirm composer |
apk | apk add composer |
zypper | zypper -n in composer |
HasPHP
Check if PHP is available on the system.
func HasPHP() boolFirst exec.LookPath("php"), on failure checks common paths /usr/bin/php, /usr/local/bin/php, /opt/homebrew/bin/php (macOS Homebrew), returns true if any exists.
GetPHPVersion
Get PHP version string.
func GetPHPVersion() (string, error)Executes php -r "echo PHP_VERSION;", returns trimmed version number. On failure returns failed to get PHP version wrapped error.
InstallPHP
Install PHP and common extensions via system package manager.
func InstallPHP(distro *DistroInfo, useSudo bool) errorReturns cannot install PHP: unknown package manager when distro is nil or `PackageManager=="unknown". Packages installed per package manager:
| Package Manager | Packages Installed |
|---|---|
apt | php php-cli php-mbstring php-xml php-curl |
dnf | php php-cli php-mbstring php-xml php-curl |
yum | php php-cli php-mbstring php-xml php-curl |
pacman | php |
apk | php81 php81-cli php81-mbstring php81-xml php81-curl |
zypper | php7 php7-cli |
apk installs php81
Alpine PHP package names carry version numbers (e.g., php81), InstallPHP hardcodes to php81. If Alpine source has a different default version, manual adjustment may be needed.
CheckComposerVersion
Execute composer --version and return output.
func CheckComposerVersion(composerPath string) (string, error)Returns raw --version output (e.g., Composer version 2.7.1 ...), no parsing. On failure returns failed to check composer version wrapped error.
InstallComposerVersion
Install specified Composer version (direct curl fetch phar + generate wrapper script).
func InstallComposerVersion(version string, installPath string, useSudo bool) error| Parameter | Type | Description |
|---|---|---|
version | string | Version alias or specific version number |
installPath | string | Installation directory, phar lands at <installPath>/composer.phar |
useSudo | bool | Whether to use sudo for file writing and chmod |
version to download URL mapping:
| version | Download URL |
|---|---|
1 / 1.x | https://getcomposer.org/composer-1.phar |
2 / 2.x / latest / "" | https://getcomposer.org/composer.phar |
preview | https://getcomposer.org/composer-preview.phar |
stable | https://getcomposer.org/composer.phar |
Other (e.g., 2.5.1) | https://getcomposer.org/download/<version>/composer.phar |
Process:
curl -fsSL -o <installPath>/composer.phar <url>download phar.createComposerWrappergenerates executable wrapper: on Windows writescomposer.bat(@php "<phar>" %*); on other systems writescomposer(#!/bin/sh\nphp "<phar>" "$@") andchmod +x(withsudo chmodifuseSudo).
UninstallComposer
Delete Composer executable and composer.phar in same directory.
func UninstallComposer(composerPath string, useSudo bool) errorDeletes two files:
composerPathitself.composerPathwith trailing/composerremoved +/composer.pharappended (i.e., phar in same directory).
useSudo=true uses sudo rm -f, otherwise os.Remove. Skips without error if file doesn't exist.
Quick Examples
Detect Distro and Print
package main
import (
"fmt"
"log"
"github.com/scagogogo/composer-skills/pkg/installer"
)
func main() {
info, err := installer.DetectLinuxDistro()
if err != nil {
log.Fatalf("Not Linux or detection failed: %v", err)
}
fmt.Printf("Distro: %s %s (%s)\nPackage manager: %s\n",
info.Name, info.Version, info.ID, info.PackageManager)
}Auto-install PHP When Missing
if !installer.HasPHP() {
distro, _ := installer.DetectLinuxDistro()
if err := installer.InstallPHP(distro, true); err != nil {
log.Fatal(err)
}
}
fmt.Println("PHP version:", must(installer.GetPHPVersion()))Install Specific Composer Version
// Install Composer 2.5.1 to /usr/local/bin, using sudo
err := installer.InstallComposerVersion("2.5.1", "/usr/local/bin", true)
if err != nil {
log.Fatal(err)
}Install Composer via Package Manager
distro, _ := installer.DetectLinuxDistro()
attempted, err := installer.InstallComposerViaPackageManager(distro, true)
if !attempted {
log.Println("No available package manager, fallback to direct download")
} else if err != nil {
log.Printf("Package manager install failed: %v\n", err)
}Uninstall
if err := installer.UninstallComposer("/usr/local/bin/composer", true); err != nil {
log.Fatal(err)
}Advanced
- Why DetectLinuxDistro never returns nil: Worst case returns
unknown, letting caller checkPackageManager == "unknown"for "unrecognized", avoiding nil dereference.InstallComposerViaPackageManagerandInstallPHPboth handle this unknown case. - PHP version retrieval reliability:
GetPHPVersiondepends onphpin PATH; extreme case whereHasPHP()returns true butGetPHPVersionfails (e.g., broken php), caller should handle error. - CheckComposerVersion doesn't parse: It returns raw
--versionoutput string; for structured version numbers, parse at caller side, or usepkg/composer'sGetVersion/GetVersionInfo. - InstallComposerVersion's curl dependency: This function uses
curlto download, requires curl installed on system.GetSystemInfocheckscurl_availablefor diagnostics. - Relationship with SmartInstaller:
SmartInstaller.doInstallon Linux first callsInstallComposerViaPackageManager, on failure fallback toinstaller.Install()(internally includesInstallComposerVersionpath), forming "package manager → Homebrew → direct download" three-level fallback.