Automatically Switch PHP Version on cd

Auto-switching PHP versions

After using phpbrew to manage my local PHP versions for a while, I got tired of re-compiling PHP after every release and decided to install multiple PHP versions side-by-side with Ondřej Surý's PPA. One of the features I missed from phpbrew was the ability to run a command like phpbrew use php-7.2.8 to automatically change the php command to that version, so I ended up implementing this feature myself using symlinks and shell aliases.

Switching PHP Versions

By default, Ubunutu creates a symlink at /etc/alternative/php which points to an executable like /usr/bin/php5.6. I therefore wanted to create my own shell alias to change that symlink by simply running a command like php72

However, /etc/alternative/php is for the whole system, and I'd have to use sudo everytime I wanted to change that symlink (or change permissions in /etc/alternative). But, since I'm the only user on my laptop, I instead hard-coded a symlink from /etc/alternative/php to another symlink in my home directory:

Symlinks

Now my aliases can simply update that /home/codell/.php symlink without needing sudo! Here's the full list of aliases which I dropped inside of my ~/.zshrc file:

alias php56="ln -sf /usr/bin/php5.6 /home/codell/.php && php -v"
alias php70="ln -sf /usr/bin/php7.0 /home/codell/.php && php -v"
alias php71="ln -sf /usr/bin/php7.1 /home/codell/.php && php -v"
alias php72="ln -sf /usr/bin/php7.2 /home/codell/.php && php -v"

Now I can simply run php7.2 or any other version number to make php alias to that specific version!

Switching between PHP versions

(This approach also works perfectly fine if you use bash instead of zsh - just drop your aliases into ~/.bashrc instead.)

Automating This Based on composer.json

But let's take this one step further - most of the projects I work on have a PHP constraint in their composer.json file:

{
    "name": "some/project",
    "require": {
        "php": ">=7.1"
    }
}

It would be awesome if my shell could automatically switch PHP versions for me based on what that project uses - so that's exactly what I implemented for zsh!

autoload -U add-zsh-hook
change-php-version() {
  if [ -f composer.json ]; then
    PHPVERSION=$(cat composer.json | jq '.require.php' | grep -o '[0-9].[0-9]')
    if [ ! -z "$PHPVERSION" ] && [ -e "/usr/bin/php$PHPVERSION" ]; then
      ln -sf "/usr/bin/php$PHPVERSION" /home/codell/.php
      php -v | head -n 1 | awk '{print $1 " " $2}'
    fi
  fi
}

add-zsh-hook chpwd change-php-version

I simply dropped that into my ~/.zshrc file. Now, whenever I cd into a different project directory, zsh checks my composer.json and switches to that PHP version automatically! It also outputs which version it switched to:

Automatically switching PHP versions

It's not perfect though, as it doesn't actually parse the constraint - it does a dumb regex match and assumes whatever x.y version it sees is what you want. If your constraint was something like >7.0 it would think you wanted 7.0! However, none of my projects have constraints like that, so I'm not too concerned. Perhaps in the future I'll make this smarter.

Enjoy this article?

About Colin O'Dell

Colin O'Dell

Colin O'Dell is a Lead Software Engineer at SeatGeek. In addition to being an active member of the PHP League and maintainer of the league/commonmark project, Colin is also a PHP docs contributor, conference speaker, and author of the PHP 7 Migration Guide.