---
date: 2022-06-15
title: A simple PHP version manager
description: A small ZSH function to easily switch PHP versions for CLI usage
tags: php, scripts
---

# A simple PHP version manager

<div class="tracking-wide uppercase opacity-70">
  <small>15/06/2022 in #PHP #ZSH #scripts #terminal</small>
</div>

I have multiple versions of PHP installed with Homebrew on my computer and it has always been a nightmare to use a specific version in the terminal for different commands. For example, installing Composer dependencies with a specific PHP version:

```sh
/usr/bin/local/php73 $(which composer) install
```

I ended up writing a small ZSH function to easily switch from one version to another à la `nvm`. It is quite simple under the hood as it simply changes the content of the `$PATH` variable so that the `php` command points to the desired version.

The function can be used as follows:

```sh
# Use a specific version
pvm use 7.4

# Use the system default version
pvm use system

# Try to guess a version from a `composer.json` file up in the folder tree
pvm use

# Display the current version
pvm current

# List available versions
pvm list
pvm ls
```

The `pvm use` command can be used without specifying a version, it will try to find a `composer.json` file up in the folder tree and guess a PHP version from its dependencies.

## Disclaimer

This is a script created specificly to work on my local environment, I can not guarantee that it will work correctly on any other environment. Use it at your own risk.

## The script

You will find the script below, to start using it, add it to your `~/.zshrc` file or create a separate file and add the following to your `~/.zshrc`:

```sh
source /path/to/pvm.zsh
```

You also will need to have multiple versions of PHP installed with [Homebrew](https://brew.sh/) as well as the [`jq`](https://stedolan.github.io/jq/) CLI:

```sh
brew install jq \
    shivammathur/php/php \
    shivammathur/php/php@5.6 \
    shivammathur/php/php@7.0 \
    shivammathur/php/php@7.1 \
    shivammathur/php/php@7.2 \
    shivammathur/php/php@7.3 \
    shivammathur/php/php@7.4 \
    shivammathur/php/php@8.0
```

Here is the script:

```sh
#!/bin/zsh

# Alias PHP version to Brew versions
function pvm() {
    zparseopts -D -E -a opts -verbose::=verbose v::=version -version::=version
    VERSION='0.0.2'
    brew_prefix=$(brew --prefix)

    function log() {
        if [[ $verbose ]]; then
            echo $1
        fi
    }

    function get_php_bin_path() {
        ls -d $brew_prefix/Cellar/php*/$1*/bin
    }

    function get_php_versions() {
        ls -d $brew_prefix/Cellar/php*/* | grep -E -o '/[0-9]+.[0-9]+' | sed -E 's#/##'
    }

    function set_php_version() {
        _current_php_version=$(pvm current)
        _current_path=$(get_php_bin_path "$_current_php_version")
        _old_path=$(php -r "echo str_replace('$_current_path:', '', '$PATH');")

        if [[ -z $1 ]]; then
            export PATH="$_old_path"
            return
        fi

        PHP_VERSIONS="$(get_php_versions)"

        _version_exists="$(php -r "echo preg_match('/^$1$/m', '$PHP_VERSIONS');")"

        if [[ $_version_exists == "0" ]]; then
            echo "Could not find PHP version $1, is it installed?";
            return
        fi

        _new_path=$(get_php_bin_path $1)
        _old_path=$(php -r "echo str_replace('$_new_path:', '', '$_old_path');")
        export PATH="$_new_path:$_old_path"
    }

    log "pvm@$VERSION"

    if [[ $1 == 'current' ]]; then
        local PHP_VERSION=$(php -v | grep -Eo 'PHP [0-9]+.[0-9]+')
        echo ${PHP_VERSION/PHP /};
        return;
    fi

    if [[ $1 == 'use' ]]; then
        if [[ $2 == 'system' ]]; then
            log 'use system'
            set_php_version
        elif [[ -n $2 ]]; then
            log "use $2"
            set_php_version $2
        else
            composerjson_path=$(_find_up composer.json | tr -d '[:space:]')
            if [[ -a $composerjson_path/composer.json ]]; then
                pvm_version=$(cat $composerjson_path/composer.json | jq '.require.php' | grep -Eo '[0-9](.[0-9])?' | head -n 1)
                log "use v$pvm_version from composer.json"
                if [[ -n $pvm_version ]]; then
                    set_php_version $pvm_version
                    return
                fi
            fi
        fi
        return
    fi

    if [[ $1 == 'list' || $1 == 'ls' ]]; then
        get_php_versions
        return
    fi

    if [[ $version == '--version' || $version == '-v' ]]; then
        echo $VERSION
        return
    fi;

    echo "pvm $VERSION
A fast and simple PHP manager

USAGE:
    pvm <SUBCOMMAND>

FLAGS:
    -h, --help
        Prints help information

    -v, --version
        Prints version information

SUBCOMMANDS:
    current       Print the current PHP version
    use [version] Change PHP version
    list          List all available PHP versions [alias: ls]
"
}
```
