If you have been working with PHP for a while, chances are that you have come across with a project, extension or script that requires to be tested on multiple PHP versions, for simple CLI scripts this seems easy enough but what happens when you are working with complex applications, developing for frameworks or multiple versions of them ?
Let's say that like me, a Magento developer that regularly develops extensions, there is a need to run on multiple versions of Magento. The initial approach that most developers take is to create a virtual machine with the right environment. For an application like Magento that means you need two virtual machines one for PHP5.2 and one for PHP5.3.
Or at least that used to be the case because PHP5.3 is officially being deprecated, now we have to include PHP5.4 into the mix; with out current setup that means creating another virtual machine setting up all our sites again, deploying our changes to each of the virtual machines.
This setup can quickly become cumbersome and it is not easily scalable: what happens if we want to test individual patch version or test code on all 3 versions, or add PHP5.5 depending of the host machine you use, it might not be able to run more than virtual machine at the same time.
I ran into all these problems while working at Demac Media where I constantly need to work on multiple environments and specially when testing extensions for commercial distribution. In order to work more efficiently I came up with the following setup than can be ran locally or on a single Virtual Machine if you are on windows.
Getting multiple PHP versions running side by side can be challenging and over the year devs have released multiple solutions like PHPEnv or the new VirtPHP, personally I use PHPFarm which works very well with my workflow and is extremely easy to use, as well working seamlessly with Apache.
The basic idea behind PHPFarm is that we can install several version of PHP side by side, in addition we can select one as current or reference specific version directly. Another extremely nice feature about PHPFarm is that we can run patch versions side by side.
Installation and Configuration
$ cd /opt/ $ git clone https://github.com/cweiske/phpfarm.git phpfarm $ cd phpfarm/src/
So at this point we have everything that we need to install any PHP version, so let's go ahead and install PHP5.3.0:
$ cd /opt/phpfarm/src/ $ ./compile 5.3.1
At this point PHPFarm will take care of downloading the source files for the specified version and compiling for our system, while compiling from source might be a little slower than using the distribution binaries, we have much more flexibility over the configuration and options if the installed PHP version.
As I mentioned before we can control each individual version compilation options and default php.ini settings; both are controller by configuration files inside the src/ directory.
The default configuration options are in src/options.sh. And we can create version specific option files like:
custom-options.sh custom-options-5.sh custom-options-5.3.sh custom-options-5.3.1.sh
This structure give us very granular control over the PHP options, and this one the nicest features PHPFarm has to offer. Like wise we can do the same with the php.ini values:
custom-php.ini custom-php-5.ini custom-php-5.3.ini custom-php-5.3.1.ini
An example options and ini file might look something like the following:
Installing Multiple Versions of PHP
Previously, we installed one of the older versions of PHP5.3 but in order the get the hang of working with multiple versions of php, let's go ahead and install the latest versions of PHP5.4 and PHP5.5:
$ cd /opt/phpfarm/src/ $ ./compile.sh 5.4.27 $ ./compile.sh 5.5.11
In order to confirm that everything was installed correctly let's verify each of the php versions we installed works:
$ /opt/phpfarm/inst/php-5.3.1/bin/php -v $ /opt/phpfarm/inst/php-5.4.27/bin/php -v $ /opt/phpfarm/inst/php-5.5.11/bin/php -v
After running each one of those commands you should see each version's full information, but is highly inconvenient if we have to type the full path to each of the php versions, right ? Well no worries PHPFarm actually has our back on this one, PHPFarm comes included with a command called with switch-phpfarm.
Let's try it out:
$ switch-phpfarm 5.4.27
After running that we can go ahead and try to get php information by doing the following:
$ php -v PHP 5.4.27 (cli) (built: Apr 18 2014 10:41:43) (DEBUG) Copyright (c) 1997-2014 The PHP Group Zend Engine v2.4.0, Copyright (c) 1998-2014 Zend Technologies
It's as simple as that, switch-phpfarm takes care of creating the necessary symlinks need to run php; switching back to PHP5.3 is as simple as too:
$ switch-phpfarm 5.3.1 $ php -v PHP 5.3.1 (cli) (built: Feb 9 2014 16:11:15) (DEBUG) Copyright (c) 1997-2013 The PHP Group Zend Engine v2.3.0, Copyright (c) 1998-2013 Zend Technologies
Adding Apache to the Mix
So we have multiple PHP versions running side by side and we can execute code by calling the php version directly or by changing the current version, however this is not very useful, real world PHP development goes well beyond single scripts executed through the shell. We need to be able to execute and test complex applications, and for that, we need a web server.
Personally, I like to use modproxyfcgi and PHP-FPM on my development and production stacks, however modproxyfcgi is only available on Apache2.4 which unfortunately is not an option for some developers out there, but no need to worry we can still setup older versions of Apache to run specific versions of PHP per VirtualHost.
For this article's example purposes we will use mod_fastcgi in order to run different version of PHP per VirtualHost. The first thing we need to do is make sure that mod_fastcgi is installed and running; in Ubuntu this can easily be done by running:
$ a2enmod fastcgi actions suexec $ service apache2 restart
Next we need to tell Apache where it can find the configuration for the multiple fastcgi servers, open the apache configuration file located in /etc/apache2/apache2.conf and add the following lines at the end of it:
# Include FastCGI configuration for PHPFarm IncludeOptional cgi-servers/*.conf
Now we need to create the cgi-servers/ directory and the corresponding file:
$ sudo mkdir /etc/apache2/cgi-servers/ $ cd /etc/apache2/cgi-servers/
Inside create a file named php-cgi-5.3.28 and copy the following content:
FastCgiServer /srv/www/cgi-bin/php-cgi-5.3.28 ScriptAlias /cgi-bin-php/ /srv/www/cgi-bin/
Next let's create the file /srv/www/cgi-bin/php-cgi-5.3.28:
#!/bin/sh PHPRC="/etc/php5/cgi/5.3.28/" export PHPRC PHP_FCGI_CHILDREN=3 export PHP_FCGI_CHILDREN PHP_FCGI_MAX_REQUESTS=5000 export PHP_FCGI_MAX_REQUESTS PHP_FCGI_IDLE_TIMEOUT=5000 export PHP_FCGI_IDLE_TIMEOUT exec /opt/phpfarm/inst/bin/php-cgi-5.3.28
We also need to make sure the file is actually executable:
$ sudo chmod +x /srv/www/cgi-bin/php-cgi-5.3.28
Finally, we can go ahead and create a virtual host that points to the newly create php-cgi server:
Let's now enable our VirtualHost and restart the Apache server:
$ a2ensite test.mydomain.com.conf $ service apache2 restart
Now let's make sure our Virtual Host is loading the proper PHP correctly, to do this we can create a phpinfo script on the root directory of our VirtualHost, with the following content:
<?php phpinfo(); ?>
With this setup we can install and use as many PHP version as we want, side by side and without any conflicts.
Although this setup is tailored to my work flow, I'm sure that other developers will find useful or at the very least will give you ideas to improve your own development environment and work flow.
Also if you spot any errors, have any suggestions or want to share your own setup please feel free to leave a comment on the section below.