Managing Multiple PHP versions via the ondrej/php PPA

Last week, I did some system updates, and then decided to compile the most
recent PHP releases. I’ve used phpbrew to
manage multiple PHP releases for a number of years, and having it install a new
version is fairly routine.

Except this time, it wasn’t. Due to updates I installed, I was getting errors
first with compiling the GD extension, then with ext-intl:

If you want Freetype support in ext-gd, you are expected to install the
package libfreetype-dev. On Ubuntu, this now installs libfreetype6-dev, which
no longer includes the freetype-config binary that PHP’s configure script
uses to determine what features it supports.

Similarly, ext-intl depends on the package libicu-dev. Ubuntu’s package now
omits the icu-config binary used by PHP to determine feature support.

I searched for quite some time to find packages that would resolve these
problems. I could have found the source code and compiled it and linked to that,
but that would mean keeping that up-to-date on top of my PHP installs.

I even looked in the ondrej/php PPA, as that repository
has multiple PHP versions already, including source packages.

And then I thought: why not try using those instead of phpbrew?

The rest of this post is how I made that work.

I use Ubuntu for my operating system. The instructions I present here should
work on any Debian-based system, but your mileage may vary. If you are using
an RPM-based system, yum will be your friend, but I have no idea how to add
repositories in that system, nor if update-alternatives is available. As
such, these instructions may or may not help you.

Which is okay. I mainly wrote them to help future me.

Register the PPA

First, I had to add the PPA to the system:

$ sudo add-apt-repository ppa:ondrej/php

Then the usual:

$ sudo apt update

Approach to installation

I first identified the extensions I typically install, matched them to
packages in the PPA, and made a list. I knew I’d be installing the same
extensions across all PHP versions I wanted, so I figured I could script it a
bit.

From there, I executed the following from the CLI:

$ for VERSION in 5.6 7.0 7.1 7.2 7.3;do
for> for EXTENSION in {listed all extensions here};do
for for> sudo apt install php${VERSION}-${EXTENSION}
for for> done
for> done

This grabbed and installed each PHP I needed along with all extensions I wanted.

Switching between versions

To switch between versions, you have two options:

Use the version-specific binaries: /usr/bin/php5.6, /usr/bin/php7.0, etc.

Set a default via update-alternatives:

$ sudo update-alternatives –set php /usr/bin/php5.6

If you’re not sure what you have installed, use:

$ sudo update-alternatives –config php

which will give you a listing, and an ability to select the one to use.

Rootless alternatives

What if you’d rather not be root to switch the default version, though?
Fortunately, update-alternatives allows specifying alternate config and admin
directories.

Define the following alias in your shell’s configuration:

alias update-my-alternatives=’update-alternatives
–altdir ~/.local/etc/alternatives
–admindir ~/.local/var/lib/alternatives’

Additionally, make sure you add $HOME/.local/bin to your $PATH; since
defining $PATH varies based on the shell you use, I’ll leave that for you to
accomplish.

If you open a new shell, the alias will now be available; alternately, source
the file in which you defined it to have it take effect immediately.

Once you’ve done that, you can run the following, based on the PHP versions
you’ve installed:

$ for VERSION in 5.6 7.0 7.1 7.2 7.3;do
for> update-my-alternatives –install $HOME/.local/bin/php php /usr/bin/php${VERSION} ${VERSION//./0}
for> done

This will create alternatives entries local to your own user, prioritizing them
by version; as a result, the default, auto-selected version will be the most
recently installed.

You can verify this by running update-my-alternatives –config php:

There are 5 choices for the alternative php (providing $HOME/.local/bin/php).

Selection Path Priority Status
————————————————————
* 0 /usr/bin/php7.3 703 auto mode
1 /usr/bin/php5.6 506 manual mode
2 /usr/bin/php7.0 700 manual mode
3 /usr/bin/php7.1 701 manual mode
4 /usr/bin/php7.2 702 manual mode
5 /usr/bin/php7.3 703 manual mode

Press <enter> to keep the current choice[*], or type selection number:

To switch between versions using the alias:

Switch to a specific, known version:

$ update-my-alternatives –set php /usr/bin/php{VERSION}

Switch back to the default version (version with highest priority):

$ update-my-alternatives –auto php

List available versions:

$ update-my-alternatives –list php

Interactively choose a version when you’re not sure what’s available:

$ update-my-alternatives –config php

The above was cobbled together from:

https://serverfault.com/a/811377
https://williamdemeo.github.io/linux/update-alternatives.html

PECL

Compiling and installing your own extensions turns out to be a bit of a pain
when you have multiple PHP versions installed, mainly because there is exactly
one PECL binary installed.

First, you need to install a few packages, including the one containing PEAR
(PECL uses the PEAR installer), and the development packages for each PHP
version you use (as those contain the tools necessary to compile extensions,
including phpize and php-config):

$ sudo apt install php-pear
$ for VERSION in 5.6 7.0 7.1 7.2 7.3;do
for> sudo apt install php${VERSION}-dev
for> done

Compiling extensions

From there, you need to:

Ensure the correct phpize and php-config are selected.
Install the extension.
Tell PECL to deregister the extension in its own registry.

Normally, you would accomplish the first point by doing the following:

$ sudo update-alternatives –set php /usr/bin/php7.3
$ sudo update-alternatives –set php-config /usr/bin/php-config7.3
$ sudo update-alternatives –set phpize /usr/bin/phpize7.3

Note that the above is not using the update-my-alternatives alias
detailed in the previous section. This is because extensions must be installed
at the system level.

That said, the above won’t be necessary, as I detail below.

However, PECL now has a really nice configuration flag, php_suffix, that
allows specifying a string to append to each of the php, phpize, and php-config
binary names
. So, for example, if I specify pecl -d php_suffix=7.3, the
string 7.3 will be appended to those, so that they become php7.3,
phpize7.3, and php-config7.3, respectively. This ensures that the correct
scripts are called during the build process, and that the extension is installed
to the correct location.

As for the last point in that numbered list, it’s key to being able to install
an extension in multiple PHP versions; otherwise, each subsequent attempt, even
when using a different PHP version, will result in PECL indicating it’s already
installed. The -r switch tells PECL to remove the package from its own
registry, but not to remove any build artifacts.

As a complete example:

$ sudo pecl -d install php_suffix=7.3 swoole && sudo pecl uninstall -r swoole

Registering extensions

From there, you still have to register, and optionally configure, the extension.
To do this, drop a file named after the extension in
/etc/php/${PHP_VERSION}/mods-available/${EXTENSION}.ini, with the following
contents:

; configuration for php ${EXTENSION} module
; priority=20
extension=${EXTENSION}.so

Now that this is in place, enable it:

$ sudo phpenmod -v ${PHP_VERSION} -s cli ${EXTENSION}

(To disable it, use phpdismod instead.)

Thoughts

When thinking in terms of phpbrew:

Like phpbrew, you can temporarily choose an alternative PHP binary
by simply referring to its path. With phpbrew, this would be something like
~/.phpbrew/php/php-{PHP_VERSION}/bin/php. With the ondrej PPA, it becomes
/usr/bin/php{PHP_MINOR_VERSION}. (E.g. ~/.phpbrew/php/php-7.2.36/bin/php
would become just /usr/bin/php7.2.)

There is no equivalent to phpbrew use. That feature would change the symlink
only for the duration of the current terminal session. Opening a new terminal
session would revert to the previous selection. With update-alternatives,
it’s all or nothing. I mainly used phpbrew use to ensure my default PHP did
not change in case I forgot to call it again.

Usage of update-alternatives is more like phpbrew switch, as it affects
both the current and all later terminal sessions. Once switched, that selection
is in use until you switch it again. This means I have to remember to switch
to my default version. However, it’s relatively easy to add a line to my shell
profile to call update-my-alternatives –auto php.

Basically, if you can use the binary directly, but don’t want to use that one as
your default, refer to it by absolute path. If you are using a command that will
use the current environment’s PHP, use update-my-alternatives to switch PHP
versions first.

The other issue I see is that if I want to test against a specific PHP
version, I’ll still need to compile it myself — which leads me back to the
original problem that led me here in the first place. I’ll cross that bridge
when I get to it. Until then, I have a workable solution — and finally a
single document I can refer to when I need to remember again at a later date,
instead of cobbling it together from multiple sources!

Updates

2020-04-16: fixes reference to PPA in command that adds it to system.
2019-05-01: added breakpoint so that full post is not presented in lists.

Managing Multiple PHP versions via the ondrej/php PPA was originally
published 30 April 2019
on https://mwop.net by
Matthew Weier O’Phinney.

Flatlogic Admin Templates banner

Leave a Reply

Your email address will not be published. Required fields are marked *