Skip to content

Rovasch/phm

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

phm

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.

Install

brew tap Rovasch/phm
brew install phm

Or build from source:

git clone https://github.com/Rovasch/phm.git
cd phm
cargo build --release
cp target/release/phm "$(brew --prefix)/bin/phm"

Setup

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 | source

Then install PHP versions via Homebrew:

phm install 8.4
phm install 8.2
phm default 8.4

How it works

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

Per-shell isolation

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.

Automatic version switching

With --use-on-cd, phm hooks into your shell's directory change event. When you cd into a project, it looks for:

  1. .php-version — a plain text file containing the version (e.g., 8.2). Takes priority.
  2. composer.json — reads the require.php constraint 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.

Commands

phm use [version]

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 invocation

phm default [version]

Set or show the default PHP version used for new shells.

phm default 8.4      # Set default
phm default          # Show current default

phm list

List all installed PHP versions. Marks the current and default versions.

$ phm list
  7.4
* 8.2 (current)
  8.4 (default)
  8.5

phm install <version>

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

phm uninstall <version>

Uninstall a PHP version via Homebrew. Prevents uninstalling the default version.

phm uninstall 7.4

phm exec <version> -- <command>

Run a command with a specific PHP version without switching the shell.

phm exec 8.1 -- php -v
phm exec 8.1 -- composer install

phm current

Print the active PHP version.

phm which

Print the resolved path to the active php binary. Useful for IDE configuration.

$ phm which
$(brew --prefix)/opt/php@8.2/bin/php

phm doctor

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!

phm completions <shell>

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

Why phm?

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

Requirements

  • macOS (Apple Silicon or Intel)
  • Homebrew
  • PHP installed via Homebrew (brew install php@8.2)

License

MIT

About

Fast PHP version manager for macOS, written in Rust

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages