Switch PHP and Drush versions in developer environment

Easily toggle between PHP and Drush versions in a Debian based developer environment

Posted on February 23, 2021 · 9 mins read

Motivation

Most hosting control panels have the option to set a different PHP version per site. This great functionality is not that easy to replicate in a development local environment. I will explain here the method I have been using for recent years. It is based on preparing some tiny scripts which load and unload Apache modules and set cli defaults. This way, the change between PHP versions in a 3 seconds execution time one-liner command. This method only applies to Debian-based systems and changes PHP version for the whole system, not on a per-site basis as hosting control panels do. On the other hand, PHP is loaded as a module so its administration is the simplest available. No PHP-CGI or PHP-FPM. We keep these technologies for our production and integration environments where they are better needed.

Install multiple PHP versions

The installation of the various PHP versions is done via the superb Sury repository. Install instructions can be found in the repository readme. Once the repository is added it’s just a matter of choosing the PHP versions you want. Nice thing of this repository is that its maintained by the same team than Debian PHP packages so they fit into your system very nicely, specifically in terms of:

  • They can be installed simultaneously as an Apache module.
  • Cli defaults are already declared against update-alternatives.
  • The same XDebug version works with all of them. For example you might need 7.0, 7.4 (Debian Bullseye default) and 8.0. Then to install all them is something like
sudo aptitude install php7.0 php7.0-cli php7.4 php7.4-cli php8.0 php8.0-cli php-xdebug

Custom configuration for each version

Each php version gets its configuration from /etc/php/[version_num]/[apache2,cli] so you can drop your modifications to default configuration creating a new file at /etc/php/7.4/apache2/conf.d/zz-local-custom.conf for Apache and /etc/php/7.4/cli/conf.d/zz-local-custom.conf for cli. Something like this might be useful on a dev environment:

memory_limit = 512M
upload_max_filesize = 24M
post_max_size = 24M
max_execution_time = 60

xdebug.remote_enable=On
xdebug.file_link_format = pstorm://%f:%l
display_errors=1

Personally I prefer setting the first 4 items on a per site basis at the corresponding Apache virtual host but hey, you are free to set it here and forget.

Toggle Apache module PHP version

You need to disable unwanted module and enable the new one, which can be done with

sudo a2dismod php7.4;
sudo a2enmod php8.0;

Toggle default cli

It’s nice to understand how your system organizes its binaries for your day-to-day most used tool. It’s something like this:

$ ls -la /usr/bin/php*
lrwxrwxrwx 1 root root      21 24 abr.  2019 /usr/bin/php -> /etc/alternatives/php
-rwxr-xr-x 1 root root 4495400 26 ag. 18:06 /usr/bin/php7.0
-rwxr-xr-x 1 root root 4793272 26 ag. 18:09 /usr/bin/php7.3
-rwxr-xr-x 1 root root 4928688 26 ag. 18:06 /usr/bin/php8.0
lrwxrwxrwx 1 root root      28 16 febr.  2021 /usr/bin/php-config -> /etc/alternatives/php-config
-rwxr-xr-x 1 root root    4243 26 ag. 18:09 /usr/bin/php-config7.3
lrwxrwxrwx 1 root root       6 21 juny 22:02 /usr/bin/php.default -> php8.0
lrwxrwxrwx 1 root root      24 16 febr.  2021 /usr/bin/phpize -> /etc/alternatives/phpize
-rwxr-xr-x 1 root root    4984 26 ag. 18:09 /usr/bin/phpize7.3

So being /usr/bin in your PATH, you can call your scripts easily with php7.0, php7.3, php8.0 commands but usually you will spend some hours with a version so why not toggle your system defaults with update-alternatives so things like ./yii, drush, symfony work out of the box for you? That’s easily done with the command

sudo update-alternatives --config php

and choosing the right version for each moment.

Multiple drush commands

And can’t we do the same update-alternatives trick with Drupal command line super-tool? Obviously the answer is yes, but it will take just an extra step for it. Debian brings the drush command as a repository package but nobody has done the job Sury has been doing for the last years with PHP of packaging multiple versions and making them available to work together. For this to work on first place we will need to install those multiple drush commands, for example at /home/myuser/my-drushes:+

$ mkdir my-drushes; mkdir my-drushes/drush8; mkdir my-drushes/drush9; mkdir my-drushes/drush10;
$ cd my-drushes/drush8; composer require drush/drush ^8; vendor/bin/drush --version
$ cd my-drushes/drush9; composer require drush/drush ^9; vendor/bin/drush --version
$ cd my-drushes/drush10; composer require drush/drush ^10; vendor/bin/drush –version

Now we need to declare to our system those alternatives for the drush command:

sudo update-alternatives --install /usr/bin/drush8 drush /home/myuser/my-drushes/vendor/bin/drush 8
sudo update-alternatives --install /usr/bin/drush9 drush /home/myuser/my-drushes/vendor/bin/drush 9
sudo update-alternatives --install /usr/bin/drush10 drush /home/myuser/my-drushes/vendor/bin/drush 10

Last number is just the weight for update-alternatives. You can set it as 10, 20, 30 or whatever you want. From this moment we can toggle between our drush versions with

sudo update-alternatives –config drush

and we will be asked for which version we want to use.

The scripts that put all together

It’s not very comfortable writing down all those commands or at least it’s not what I promised at the beginning of a 3 seconds execution one liner. Funny part is to put it all together in a script and call it from whatever situation we find ourselves in. You usually need to change PHP versions for cli and Apache together. Usually Drush 8 or 9 will fit in your Drupal 7 websites which are currently fully compatible with PHP 7.4 but maybe you have some legacy code and still require 7.0. On the other side, your most recent projects work just fine with Drush 10 and PHP 8.0 so the scripts could be something like this:

Script 1: PHP 7.0 + Drush 8

/home/myuser/php70.sh:

#!/bin/bash
sudo a2dismod php7.4 php8.0
sudo a2enmod php7.0
sudo systemctl restart apache2
sudo update-alternatives --set php /usr/bin/php7.0
sudo update-alternatives --set drush /home/myuser/my-drushes/vendor/bin/drush 8

Script 2: PHP 7.4 + Drush 9

/home/myuser/php74.sh:

#!/bin/bash
sudo a2dismod php7.0 php8.0
sudo a2enmod php7.4
sudo systemctl restart apache2
sudo update-alternatives --set php /usr/bin/php7.4
sudo update-alternatives --set drush /home/myuser/my-drushes/vendor/bin/drush 9

Script 1: PHP 8.0 + Drush 10

/home/myuser/php80.sh:

#!/bin/bash
sudo a2dismod php7.0 php7.4
sudo a2enmod php8.0
sudo systemctl restart apache2
sudo update-alternatives --set php /usr/bin/php8.0
sudo update-alternatives --set drush /home/myuser/my-drushes/vendor/bin/drush 10

Yes, those scripts are a bit verbose but you will set up them once and don’t change until a new version of PHP or major version of Drush is released so no more than once a year, and changing it is just a matter of issuing following command from whatever place you are in your machine, easily recoverable from your history via Ctrl+R shortcut:

~/php7.0.sh
~/php7.4.sh
~/php8.0.sh

Comment on this post