Manage Multiple Versions of PHP with Homebrew
Tags: php, homebrew, macos
Published: 2022-10-15 22:09:12

Using [Homebrew](https://brew.sh/) is a handy way to install multiple versions of PHP on MacOS. For example, to have both `php` 7.4 and 8.0 installed, use the `brew install` command.

First install the 7.4 version, like so:

```bash
% brew install php@7.4
```

The syntax for specifying a specific version of a package with Homebrew is `brew install <packageName>@<version>`. In this case the package name is `php` and the version is `7.4` (defaulting to `7.4.32` as of this writing). If you leave off the `@<version>` part entirely, Homebrew will install whatever the latest version available is (`8.1` as of this writing).

To check that version 7.4 of `php` is correctly installed:

```bash
% /opt/homebrew/opt/php@7.4/bin/php -v  
  PHP 7.4.32 (cli) (built: Sep 29 2022 10:45:51) ( NTS )
  Copyright (c) The PHP Group
  Zend Engine v3.4.0, Copyright (c) Zend Technologies
    with Zend OPcache v7.4.32, Copyright (c), by Zend Technologies
```

Ideally we would like to refer to `/opt/homebrew/opt/php@7.4/bin/php` simply as `php` on our command line. This requires another step:

```bash
% brew link --force --overwrite php@7.4
  Linking /opt/homebrew/Cellar/php@7.4/7.4.32... 812 symlinks created.

  If you need to have this software first in your PATH instead consider running:
  echo 'export PATH="/opt/homebrew/opt/php@7.4/bin:$PATH"' >> ~/.zshrc
  echo 'export PATH="/opt/homebrew/opt/php@7.4/sbin:$PATH"' >> ~/.zshrc
```

As a result of running `brew link` we can now find the linked version of `php` at this path: `/opt/homebrew/bin/php`.

```bash
% ls -l /opt/homebrew/bin/php
  lrwxr-xr-x  1 myuser  admin  32 15 Oct 18:34 /opt/homebrew/bin/php -> ../Cellar/php@7.4/7.4.32/bin/php
% /opt/homebrew/bin/php -v
  PHP 7.4.32 (cli) (built: Sep 29 2022 10:45:51) ( NTS )
  Copyright (c) The PHP Group
  Zend Engine v3.4.0, Copyright (c) Zend Technologies
    with Zend OPcache v7.4.32, Copyright (c), by Zend Technologies
```

The output of running `brew link` has a suggestion that we append two lines to the end of our `~/.zshrc` file to tell our terminal that when we refer to `php` it should look for it first in the `/opt/homebrew/opt/php@7.4/bin` folder. You can do this but every time you `brew link` to a different version of `php` you will have to edit those lines. Instead I will take a slightly modified approach:

```bash
% echo 'export PATH="/opt/homebrew/sbin:/opt/homebrew/bin:$PATH"' >> ~/.zshrc
% source ~/.zshrc
```

Now our terminal will find the linked version of `php` at `/opt/homebrew/bin` first whenever we refer to `php`.

```bash
% which php    
  /opt/homebrew/bin/php
% php -v
  PHP 7.4.32 (cli) (built: Sep 29 2022 10:45:51) ( NTS )
  Copyright (c) The PHP Group
  Zend Engine v3.4.0, Copyright (c) Zend Technologies
    with Zend OPcache v7.4.32, Copyright (c), by Zend Technologies
```

Now that we have `php` version 7.4 installed we can continue by installing `php` version 8.0 too.

```bash
% brew install php@8.0
```

Now we tell brew to link to the new version of `php`:

```bash
% brew link --force --overwrite php@8.0
  Unlinking /opt/homebrew/Cellar/php@7.4/7.4.32... 328 symlinks removed.
  Linking /opt/homebrew/Cellar/php@8.0/8.0.24... 232 symlinks created.
```

In the output from this command we again see the suggestion to run two commands containing the command `export PATH...` but due to the steps we took above, this won't be necessary.

```bash
% which php    
  /opt/homebrew/bin/php
% php -v
  PHP 8.0.24 (cli) (built: Sep 30 2022 08:39:20) ( NTS )
  Copyright (c) The PHP Group
  Zend Engine v4.0.24, Copyright (c) Zend Technologies
    with Xdebug v3.1.5, Copyright (c) 2002-2022, by Derick Rethans
    with Zend OPcache v8.0.24, Copyright (c), by Zend Technologies
```

Now we can switch from versions of 7.4 to 8.0 by relinking from one to the other: `brew link --force --overwrite php@7.4` or `brew link --force --overwrite php@8.0` for example.

If you're not sure what versions are available to link to, try this command:

```bash
% brew ls --versions | grep '^php[ @]'
  php@7.4 7.4.32
  php@8.0 8.0.24
```

Finally, below is the source code of a more full featured script that I call `phpswitch`. This script can be set to be [executable](https://support.apple.com/en-gb/guide/terminal/apdd100908f-06b3-4e63-8a87-32e71241bab4/mac) and then run from the terminal like `phpswitch 8.0` or `phpswitch 7.4`.

```bash
#!/bin/bash

if [ $# -ne 1 ]; then
    echo 1>&2 "USAGE: $0 <phpVersion>"
    exit 2
fi

INSTALLED_VERSIONS=`find /opt/homebrew/opt | grep 'php@' | sed 's/\/opt\/homebrew\/opt\/php@//'`

if [[ ! -f /opt/homebrew/opt/php@${1}/bin/php ]]; then
    echo 1>&2 "/opt/homebrew/opt/php@${1}/bin/php was not found"
    printf 'valid options:\n%s\n' "${INSTALLED_VERSIONS[*]}"
    exit 2
fi

for VERSION in ${INSTALLED_VERSIONS[*]}; do
    brew unlink php@$VERSION > /dev/null 2>&1
    brew services stop php@$VERSION > /dev/null 2>&1
done

brew link --force --overwrite php@$1 > /dev/null 2>&1
brew services start php@$1 > /dev/null 2>&1
```