Switching between PHP versions when using Homebrew

Berlin, Germany

Do you use Homebrew to install multiple versions of PHP on macOS? Do you often need to switch between PHP versions? Do you prefer short commands, especially when you need to run them often?

Since I maintain and contribute to a range of open-source projects, I certainly do! These projects often have different PHP version requirements. For running development tools in the terminal, I need to switch between these versions quickly.

Currently, when I want to switch from PHP 7.1 to PHP 7.4., I need to run the following commands:

$ brew unlink php@7.1
$ brew link php@7.4 --force --overwrite

Hmm. I need to know what the currently enabled version is, unlink it, then link PHP 7.4. That is a bit much.

What about running

$ 7.4

to switch to PHP 7.4, regardless of which version of PHP is currently enabled?

That would be better, right?

In the following, I will show how to achieve that.

First, we are going to determine which versions of PHP we installed on our system. Then we are going to create an alias for each. The alias will unlink all other versions, and then link the one we want to use.

:bulb: Because I use oh-my-zsh, I will add the script to ~/.zshrc.

Determine PHP versions

The first option could be to hard-code the versions we know into an array:

installedPhpVersions=('7.1' '7.2' '7.3' '7.4')

When we install or uninstall a minor PHP version, we need to update this variable manually. Otherwise, we risk creating aliases that will not work, or miss creating aliases that we need.

If you can live with that, you can skip to the next section.

Another option could be to hard-code the names of brew formulae used to install PHP, and then use brew ls to verify whether we installed a particular formula:

installedPhpVersions=()

brewFormulas=('php@7.1' 'php@7.2' 'php@7.3' 'php@7.4')

for brewFormula in ${brewFormulas[*]}; do
    if [[ -n "$(brew ls --versions "$brewFormula")" ]]; then
        phpVersion=${brewFormula#php@}

        installedPhpVersions+=("$phpVersion")
    fi
done

We now avoid creating aliases for versions of PHP that we have not previously installed on our system. This script, however, will run every time we open a new terminal. Since executing brew ls takes a bit of time, and we want to get to work quickly, this is not ideal. We also need to update the array of formulae whenever a new major or minor version of PHP comes out. That does not happen very often. Still, it would be nice to avoid manual work.

The next option would be to run brew ls once, and obtain the PHP versions from the output:

installedPhpVersions=($(brew ls --versions | ggrep -E 'php(@.*)?\s' | ggrep -oP '(?<=\s)\d\.\d' | uniq | sort))

A lot better, right?

We do not have to worry about updating a hard-coded list, and a new terminal will open a lot quicker.

:bulb: The use of ggrep instead of grep is not an accident.

The version of grep that ships with macOS has a smaller feature set, so I installed grep with Homebrew. This version uses the prefix g to avoid conflicts.

Create aliases

Now we can create the aliases:

for phpVersion in ${installedPhpVersions[*]}; do
    value="{"

    for otherPhpVersion in ${phpVersions[*]}; do
        if [ "$phpVersion" != "$otherPhpVersion" ]; then
            value="$value brew unlink php@$otherPhpVersion;"
        fi
    done

    value="$value brew link php@$phpVersion --force --overwrite; } &> /dev/null && php -v"

    alias "$phpVersion"="$value"
done

With this script in place, when I open a new terminal on my system and run

$ alias | grep php

I can see that the script created the following aliases:

7.1='{ brew unlink php@7.2; brew unlink php@7.3; brew unlink php@7.4; brew link php@7.1 --force --overwrite; } &> /dev/null && php -v'
7.2='{ brew unlink php@7.1; brew unlink php@7.3; brew unlink php@7.4; brew link php@7.2 --force --overwrite; } &> /dev/null && php -v'
7.3='{ brew unlink php@7.1; brew unlink php@7.2; brew unlink php@7.4; brew link php@7.3 --force --overwrite; } &> /dev/null && php -v'
7.4='{ brew unlink php@7.1; brew unlink php@7.2; brew unlink php@7.3; brew link php@7.4 --force --overwrite; } &> /dev/null && php -v'

Switch

To switch from any version of PHP, we now only need to run

$ 7.4

I hope it helps!