Switching PHP Versions on macOS
High Contrast
Dark Mode
Light Mode
Sepia
Forest
2 min read305 words

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.


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


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