Skip to content

Repository files navigation

phpdiscover

CI

phpdiscover is a zero-dependency Go package that finds all PHP binaries installed on the current machine. Every candidate is collected, verified and deduplicated.

Usage

package main

import (
	"context"
	"fmt"

	"github.com/shyim/go-php-discover"
)

func main() {
	phps := phpdiscover.Discover(context.Background())

	for _, p := range phps {
		fmt.Printf("PHP %s at %s (%s)\n", p.Version, p.Path, p.Source)
	}
}

Result helpers:

system := phpdiscover.Default(phps)      // first $PATH binary, else newest
latest := phpdiscover.Latest(phps)       // newest version
php82  := phpdiscover.FindVersion(phps, "8.2") // newest matching "8", "8.2" or "8.2.3"
phpdiscover.Sort(phps)                   // ascending by version

Options:

phpdiscover.Discover(ctx,
	phpdiscover.WithTimeout(2*time.Second), // per-binary probe timeout (default 5s)
	phpdiscover.WithConcurrency(8),         // parallel probes (default 4)
	phpdiscover.WithLogger(log.Printf),     // progress messages
	phpdiscover.WithExtraDirs("/opt/my-php"),          // scan extra directories
	phpdiscover.WithSources(phpdiscover.SourcePath),   // only use $PATH
	phpdiscover.WithoutSource(phpdiscover.SourceNix),  // skip one source
)

How discovery works

Candidates come from four places:

  1. $PATH — every directory in $PATH is scanned for php, php8, php8.2, php8.2.3, php-cli, ... The first PHP found in $PATH is marked IsSystem.
  2. Known hoster locations — well-known locations used by hosting providers (/opt/plesk/php/*/bin/php, /opt/alt/php*/usr/bin/php, /usr/local/phpfarm/..., MAMP, ...). The {major}, {minor}, {release} placeholders are turned into glob patterns so that every installed version is matched.
  3. Well-known installation directories/usr/bin, /usr/local/bin, XAMPP, phpbrew, phpenv, Herd-lite, Homebrew (brew --cellar), Nix (builtins.storeDir), asdf-vm, Remi's RPM repo, and on macOS MAMP, MacPorts and Liip PHP; on Windows XAMPP, Cygwin, Chocolatey, WAMP, MAMP and Herd.
  4. Extra directories — supplied via WithExtraDirs.

Every candidate is verified by running php --version (with a timeout, in parallel) and parsing the PHP X.Y.Z version from its output. Results are deduplicated by the resolved (real) path, so symlinks like /usr/local/bin/php -> ../Cellar/php/8.5.9/bin/php collapse into a single entry. Sibling binaries of the same installation are attached when present:

p.FPMPath       // php-fpm
p.CGIPath       // php-cgi
p.PHPConfigPath // php-config
p.PHPizePath    // phpize
p.PHPdbgPath    // phpdbg

Example output

$ go run ./cmd/phpdiscover
PHP 8.3.33
  path:    /opt/homebrew/Cellar/php@8.3/8.3.33/bin/php
  source:  homebrew
  fpm:     /opt/homebrew/Cellar/php@8.3/8.3.33/sbin/php-fpm
  cgi:     /opt/homebrew/Cellar/php@8.3/8.3.33/bin/php-cgi
  config:  /opt/homebrew/Cellar/php@8.3/8.3.33/bin/php-config
  phpize:  /opt/homebrew/Cellar/php@8.3/8.3.33/bin/phpize
  phpdbg:  /opt/homebrew/Cellar/php@8.3/8.3.33/bin/phpdbg
PHP 8.4.24
  ...
PHP 8.5.9 (system)
  path:    /opt/homebrew/Cellar/php/8.5.9/bin/php
  via:     /opt/homebrew/bin/php
  source:  PATH
  ...

Design

  • Returns all binaries instead of a single "best" one and works without a running PHP (no PHP_BINARY-relative lookups).
  • Zero external dependencies.
  • Symlinks are resolved and deduplicated; sibling server binaries are attached to each version.
  • No disk cache: every call does a fresh, fast discovery.

Development

Linting

The project uses golangci-lint (v2) with all linters enabled except five, each with a documented reason in .golangci.yml:

  • wsl / gomodguard — deprecated, superseded by the enabled wsl_v5 / gomodguard_v2
  • forbidigo — its default rules forbid fmt.Print*, which the CLI legitimately uses
  • testpackage — the unit tests exercise unexported helpers
  • varnamelen — idiomatic short names (p, v, i, ok) in small scopes
golangci-lint run ./...        # lint everything

golangci-lint fmt              # apply gofumpt + goimports

Note: wsl_v5 and nlreturn enforce an airy whitespace style (blank lines between unrelated statements, before return/break/continue). Keep new code consistent with it, or drop those linters from disable: if you dislike the style.

Publishing

The module lives at github.com/shyim/go-php-discover (private). If you later publish it publicly or move it to another account, run:

go mod edit -module github.com/<owner>/go-php-discover

update the import path in cmd/phpdiscover/main.go and the depguard allowlist in .golangci.yml, and adjust the CI badge URL at the top of this README to match the new repository.

License

MIT, see LICENSE.

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages