Switching PHP Versions on macOS
Professional PHP developers often maintain multiple projects requiring different PHP versions. This page covers the cleanest strategies for switching between PHP 8.1, 8.2, and 8.3 on macOS.
The Challenge
Homebrew can only "link" one PHP version at a time as the global php command. When you run brew link php@8.3, it places symlinks in /opt/homebrew/bin/php pointing to that version.
To switch versions, you must unlink the current version and link the new one.
Method 1: Manual brew link/unlink (Simple)
# Switch from 8.3 to 8.2
brew unlink php@8.3
brew link --overwrite --force shivammathur/php/php@8.2
# Verify
php --version
# PHP 8.2.x
This is the manual approach — explicit and reliable, but verbose for frequent switches.
Method 2: Shell Function (Recommended)
Add this function to your ~/.zshrc (or ~/.bashrc):
phpswitch() {
local version="$1"
if [[ -z "$version" ]]; then
echo "Usage: phpswitch 8.1|8.2|8.3"
return 1
fi
echo "Switching to PHP $version..."
# Unlink all known versions
for v in 8.1 8.2 8.3; do
brew unlink "php@$v" 2>/dev/null
done
# Link the requested version
brew link --overwrite --force "shivammathur/php/php@$version"
echo "Active PHP version:"
php --version | head -1
}
Reload and use:
source ~/.zshrc
phpswitch 8.2
# Switching to PHP 8.2...
# PHP 8.2.x (cli)
phpswitch 8.3
# Switching to PHP 8.3...
# PHP 8.3.x (cli)
Method 3: Per-Directory PHP Version with .php-version
For project-level PHP version pinning (similar to .nvmrc for Node), use phpenv or a shell hook:
Using a shell hook in .zshrc
# Auto-switch PHP based on .php-version file
autoload -U add-zsh-hook
_phpswitch_on_chdir() {
if [[ -f ".php-version" ]]; then
local version
version=$(cat .php-version | tr -d '[:space:]')
if [[ "$version" != "$(php -r 'echo PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION;')" ]]; then
phpswitch "$version"
fi
fi
}
add-zsh-hook chpwd _phpswitch_on_chdir
Place a .php-version file in your project root:
8.2
Now when you cd into the project directory, PHP switches automatically.
Checking Which Version Is Active
# Current linked version
php --version
# All installed PHP versions
ls $(brew --prefix)/opt | grep php
# Full binary paths
which php
ls -la $(which php) # Shows the symlink target
PHP Versions for Apache/Nginx (php-fpm)
If you also run Apache or Nginx locally via Homebrew, each PHP version ships its own php-fpm service:
# Start PHP-FPM for 8.3
brew services start shivammathur/php/php@8.3
# Stop PHP-FPM for 8.2
brew services stop shivammathur/php/php@8.2
# Check running services
brew services list | grep php
Configure your virtual host to use the correct FPM socket:
# Nginx config for PHP 8.3
fastcgi_pass unix:/opt/homebrew/var/run/php/php8.3-fpm.sock;
# Nginx config for PHP 8.2
fastcgi_pass unix:/opt/homebrew/var/run/php/php8.2-fpm.sock;
Composer and PHP Versions
Composer respects the php binary in your $PATH. After switching versions:
phpswitch 8.3
composer install # Uses PHP 8.3 resolver
You can also force Composer to use a specific PHP binary:
/opt/homebrew/opt/php@8.2/bin/php /opt/homebrew/bin/composer install
Version Compatibility Matrix
| PHP Version | Status (2025) | Use Case |
|---|---|---|
| 8.3 | Active support | New projects, production |
| 8.2 | Active support | Stable production |
| 8.1 | Security fixes only | Legacy project maintenance |
| 8.0 | End of Life | Avoid — upgrade immediately |
Best practice: Start new projects on PHP 8.3. Run CI/CD against multiple versions using GitHub Actions matrix to catch compatibility issues early.
Troubleshooting
php still shows wrong version after brew link
hash -r # Reset shell's command hash table
exec $SHELL -l # Reload shell
brew link fails with "already linked" error
brew unlink php # Unlink the plain "php" formula if installed
brew link --overwrite --force shivammathur/php/php@8.3
Extensions missing after version switch
Extensions are version-specific. You must install them per version:
phpswitch 8.2
pecl install redis # Installs for 8.2
phpswitch 8.3
pecl install redis # Installs for 8.3