Fast PHP version manager for macOS, written in Rust. Inspired by fnm.
phm manages Homebrew-installed PHP versions with per-shell switching and automatic version detection from .php-version files and composer.json. Switching is instant — it just repoints symlinks, no process restarts or shims.
brew tap Rovasch/phm
brew install phmOr build from source:
git clone https://github.com/Rovasch/phm.git
cd phm
cargo build --release
cp target/release/phm "$(brew --prefix)/bin/phm"Add to your shell config:
Zsh (~/.zshrc):
eval "$(phm env --shell zsh --use-on-cd)"If your prompt already shows the active PHP version, you can opt into a fully silent session:
eval "$(phm env --shell zsh --use-on-cd --silent)"Bash (~/.bashrc):
eval "$(phm env --shell bash --use-on-cd)"Fish (~/.config/fish/config.fish):
phm env --shell fish --use-on-cd | sourceThen install PHP versions via Homebrew:
phm install 8.4
phm install 8.2
phm default 8.4When your shell starts, phm env creates a per-shell directory with symlinks pointing to your default PHP version's binaries:
~/.local/state/phm/multishells/<shell-id>/bin/
php -> $(brew --prefix)/opt/php@8.4/bin/php
phpize -> $(brew --prefix)/opt/php@8.4/bin/phpize
pecl -> $(brew --prefix)/opt/php@8.4/bin/pecl
...
This directory is prepended to your PATH. Switching versions just repoints the symlinks — there are no shims, no process restarts, and no global state changes.
Each terminal session gets its own symlink directory. Running phm use 8.2 in one terminal does not affect other terminals. This means you can work on two projects requiring different PHP versions simultaneously.
With --use-on-cd, phm hooks into your shell's directory change event. When you cd into a project, it looks for:
.php-version— a plain text file containing the version (e.g.,8.2). Takes priority.composer.json— reads therequire.phpconstraint and resolves it to the lowest matching installed version.
The search walks up parent directories, so a .php-version at the repo root covers all subdirectories.
Constraint examples:
| composer.json require | Resolved version |
|---|---|
>=8.2 |
8.2 |
^8.2 |
8.2 |
~8.2 |
8.2 |
^7.4 || ^8.0 |
8.0 |
8.2.* |
8.2 |
When the version doesn't change between directories, phm exits silently with no overhead. If you enabled phm env --silent, phm also suppresses successful switch messages for that shell session while still showing warnings, prompts, and errors.
Switch the current shell to a specific PHP version. Without a version argument, auto-detects from .php-version or composer.json.
phm use 8.2 # Switch to PHP 8.2
phm use # Auto-detect from project files
phm use --silent 8.2 # Suppress success output for this invocationSet or show the default PHP version used for new shells.
phm default 8.4 # Set default
phm default # Show current defaultList all installed PHP versions. Marks the current and default versions.
$ phm list
7.4
* 8.2 (current)
8.4 (default)
8.5
Install a PHP version via Homebrew. Automatically taps shivammathur/php for older versions.
phm install 8.3 # brew install php@8.3
phm install 7.4 # brew tap shivammathur/php && brew install ...Uninstall a PHP version via Homebrew. Prevents uninstalling the default version.
phm uninstall 7.4Run a command with a specific PHP version without switching the shell.
phm exec 8.1 -- php -v
phm exec 8.1 -- composer installPrint the active PHP version.
Print the resolved path to the active php binary. Useful for IDE configuration.
$ phm which
$(brew --prefix)/opt/php@8.2/bin/php
Diagnose common issues: missing versions, stale state, PATH conflicts, composer availability.
$ phm doctor
✓ 3 PHP version(s) found: 7.4, 8.2, 8.5
✓ Default version: 8.5
✓ Shell integration active
✓ No Herd conflict
✓ Composer found
✓ No stale multishell directories
All checks passed!
Generate shell completions.
# Zsh (add to .zshrc)
eval "$(phm completions zsh)"
# Bash
phm completions bash > "$(brew --prefix)/etc/bash_completion.d/phm"
# Fish
phm completions fish > ~/.config/fish/completions/phm.fish| phm | Herd | brew-php-switcher | |
|---|---|---|---|
| Switch speed | ~1ms (symlink swap) | ~100ms | ~2s (brew link/unlink) |
| Per-shell versions | Yes | No (global) | No (global) |
| Auto-switch on cd | Yes | No | No |
| Multi-terminal | Yes | No | No |
| Written in | Rust | PHP/Electron | Bash/Ruby |
- macOS (Apple Silicon or Intel)
- Homebrew
- PHP installed via Homebrew (
brew install php@8.2)