I have my development machines set up to be able to run sites in any version of PHP from 5.3 through the upcoming PHP 7.2 release, and easily switch between them. This solution uses Apache and real instances of PHP-FPM running on my machine, so there's no virtual machine overheads like with Vagrant. If you want something similar, read on…
I'm using macOS, so have stuck to using the built-in Apache 2.4, since it's what I know best. I don't doubt you could achieve the same results using nginx, but since I don't know that as well, I've not tried. I also work on a number of legacy projects that use .htaccess
files, so sticking to Apache was the easy choice.
Also of note is that I have Dnsmasq configured to allow me to use wildcard domains like project-name.php53.localhost
. You can follow a guide to set up Dnsmasq for local development, though these days you should not use the .dev
gTLD since it's a real gTLD as of 2014. Various places online recommend you use .localhost
instead.
Install all the PHPs
This guide uses the excellent homebrew to install the various versions of PHP, so you'll need to install that if you've not already.
# PHP version are in a brew "tap"
brew tap homebrew/php;
# Unlink all installed versions of PHP first
for php in php53 php54 php55 php56 php70 php71 php72; do
brew unlink $php;
done;
# Install each version, link it, add xdebug, then unlink ready for the next one
for php in php53 php54 php55 php56 php70 php71 php72; do
brew install $php;
brew link $php;
brew install $php-xdebug;
brew unlink $php $php-xdebug;
done;
# Clean up after ourselves
brew cleanup;
PHP-FPM Configuration
Make a directory for your unix sockets to live in with useful permissions
mkdir -p /usr/local/var/run/php-fpm
sudo chown $(whoami):_www /usr/local/var/run/php-fpm
sudo chmod 0770 /usr/local/var/run/php-fpm
A word of warning: do not use /var/run
on macOS since it is mounted as tmpfs, and it is emptied on each reboot.
Homebrew puts all the configuration files in /usr/local/etc/php
, grouped by version number. PHP >= 7.0 will have a www.conf
file within a php-fpm.d
directory, earlier versions will have the options directly in php-fpm.conf
.
- Set the
listen
option to a version-specific path file/usr/local/var/run/php-fpm/php53.sock
- Uncomment
listen.mode = 0660
so that Apache and your user can access the socket - Set
pm.status_path
to a version-specific path such as/status-php53
Finally, you can start up each PHP-FPM daemon:
for php in php53 php54 php55 php56 php70 php71 php72; do
brew services start $php;
done
If your services don't stay up, you can run them directly to see what the error is:
/usr/local/opt/php53/sbin/php53-fpm start
Apache Configuration
At a minimum, you will need to define a Proxy
per version you want to run, and enable the relevant proxy modules.
I also define status entries, and a default handler to my most-commonly used version.
First, ensure the following lines in your httpd.conf
are uncommented:
LoadModule proxy_module libexec/apache2/mod_proxy.so
LoadModule proxy_fcgi_module libexec/apache2/mod_proxy_fcgi.so
Then, you can start defining your proxy entries:
# Define a Proxy entry for each version
<Proxy "fcgi://php53">
ProxyPass "unix:/usr/local/var/run/php-fpm/php53.sock|fcgi://php53"
</Proxy>
# Optionally set up the status handler
<Location "/status-php53">
SetHandler "proxy:fcgi://php53"
</Location>
# ...
<Proxy "fcgi://php71">
ProxyPass "unix:/usr/local/var/run/php-fpm/php71.sock|fcgi://php71"
</Proxy>
<Location "/status-php71">
SetHandler "proxy:fcgi://php71"
</Location>
# Use index.php by default over html files
DirectoryIndex /index.php index.php index.html
# This is the "default" version to use when not using a version-specific hostname
<FilesMatch "\.php$">
SetHandler "proxy:fcgi://php71"
</FilesMatch>
Combined with the wildcard DNS setup mentioned at the start of this post, we can now set up one VirtualHost per version of PHP. Ensure you use the correct path in VirtualDocumentRoot
for your particular setup.
<VirtualHost *:80>
ServerAlias *.php53.localhost
VirtualDocumentRoot /Users/YOUR_USERNAME/Sites/%1/web
<FilesMatch "\.php$">
SetHandler "proxy:fcgi://php53"
</FilesMatch>
</VirtualHost>
# ...
<VirtualHost *:80>
ServerAlias *.php71.localhost
VirtualDocumentRoot /Users/YOUR_USERNAME/Sites/%1/web
<FilesMatch "\.php$">
SetHandler "proxy:fcgi://php71"
</FilesMatch>
</VirtualHost>
You'll need to restart Apache before these changes take effect: sudo apachectl restart
.
If Apache fails to start, double-check your paths and included modules. You can check your config files' syntax with httpd -t
.
If it starts, make sure you can load your sites and/or status pages, and you're done!
Conclusion
Whilst this set-up takes a little time, it has proved massively useful for me many times over. I work on many different sites, running on different versions of PHP, and setting up each site now takes only a few moments.
This setup has also been invaluable when working to upgrade a site to run on a new version. Getting the site working on its existing version, then opening the exact same site code in a new tab running on the latest stable version of PHP easily is extremely useful.
If you have any questions or corrections, please let me know!