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
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;
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
- Set the
listenoption to a version-specific path file
listen.mode = 0660so that Apache and your user can access the socket
pm.status_pathto a version-specific path such as
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:
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
If it starts, make sure you can load your sites and/or status pages, and you're done!
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!