While I found this wonderful guide on how to set up macOS for PHP web development, what I want to have is the ability to run multiple php versions simultanteously on my host.
As a bonus treat, valid https certificates for local development with mkcert.
For the benefit of completeness I’m going to paste all setup from scratch.
Install XCode and Homebrew
For the latter, always check the instructions on the official site of the project.
xcode-select --install /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
When done you can check everything is ok by running:
brew doctor Your system is ready to brew.
Install Apache web server
Differently from the aforementioned howto I didn’t have apache installed by default on Mojave. You can check by running this command in terminal:
ps -aef | grep httpd
If it returns some rows then you have to disable system provided apache.
When you’re ok, proceed by installing Apache2 via brew:
brew install httpd
When installed the service can be started. It’s always better to start the service with sudo, if you want to run httpd on privileged (standard web) ports 80 and 443. So:
sudo brew services start httpd
By default brew’s http listens on port 8080 (listed as alt-http on the ps command above), so you can check if it’s working by visiting http://localhost:8080 on your Mac.
To ease up development we’re going to change some Apache config, basically to run as the logged in user and avoid permission problems.
I use ~/work as my development area, so I’m going to set /Users/maxxer/work as my webserver root. Adjust the following commands to suit your environment, which basically is logged in username and project webroot.
Open up /usr/local/etc/httpd/httpd.conf with your favourite editor (vim?) and change the following lines:
Listen 80 DocumentRoot /Users/maxxer/work <Directory /Users/maxxer/work> AllowOverride All User your_user Group staff ServerName localhost
Also, uncomment the mod_rewrite line to allow url rewriting (hiding index.php):
LoadModule rewrite_module lib/httpd/modules/mod_rewrite.so
Now restart httpd and you should be ok:
sudo brew services restart httpd
Now apache should be listening on port 80, and ~/work should be your webroot. Check again with lsof:
lsof -nP -i4TCP:80 | grep LISTEN httpd 8211 maxxer 4u IPv6 0xc26131b9307ea245 0t0 TCP *:80 (LISTEN) httpd 8212 maxxer 4u IPv6 0xc26131b9307ea245 0t0 TCP *:80 (LISTEN) httpd 8213 maxxer 4u IPv6 0xc26131b9307ea245 0t0 TCP *:80 (LISTEN) httpd 8214 maxxer 4u IPv6 0xc26131b9307ea245 0t0 TCP *:80 (LISTEN) httpd 8215 maxxer 4u IPv6 0xc26131b9307ea245 0t0 TCP *:80 (LISTEN) httpd 8222 maxxer 4u IPv6 0xc26131b9307ea245 0t0 TCP *:80 (LISTEN) httpd 8224 maxxer 4u IPv6 0xc26131b9307ea245 0t0 TCP *:80 (LISTEN) httpd 8225 maxxer 4u IPv6 0xc26131b9307ea245 0t0 TCP *:80 (LISTEN)
PHP installation and configuration
In 2018 Homebrew deprecated the old installation method and introduced php@xx packages. With this change older and unmantained PHP versions have been removed from the official packages. As many out there we still have servers running applications with PHP 5.6 and 7.0, so we need to have those installed. Let’s add the eXolnet Homebrew Deprecated tap to brew:
brew tap exolnet/homebrew-deprecated
Now you can install any version of PHP, from 5.6 to 7.3 with the command
brew install email@example.com brew install firstname.lastname@example.org
I now usually link my custom php.ini into PHP’s conf.d with:
ln -s /Users/maxxer/etc/php/99-maxxer.ini /usr/local/etc/php/5.6/conf.d/
and so on for every PHP version you use. Out of curiosity here’s my 99-maxxer.ini file, which is generally what most of people have to adjust when setting up PHP:
display_startup_errors=off display_errors=off html_errors=off docref_root=0 docref_ext=0 ; enable PHP error logging log_errors=on error_log= /var/log/php_errors.log date.timezone = Europe/Rome upload_max_filesize = 20M post_max_size = 20M memory_limit = 1G max_execution_time = 600 max_input_time = 600
Note of Feb 10th 2019: there’s a bug in 5.6 tap which doesn’t create php-fpm.conf and php-fpm.d/www.conf. You can easily pick them from another directory and put them in /usr/local/etc/php/5.6. You just have to fix some lines for 5.6, but will work.
Now let’s change the port PHP-FPM is listening to, so we can have multiple of them running. Edit /usr/local/etc/php/5.6/php-fpm.d/www.conf and change the listen line like this:
listen = 127.0.0.1:9056
The default port is 9000, I usually change the latest two digits to the version, in this case 9056. For /usr/local/etc/php/7.2/php-fpm.d/www.conf use 9072.
Restart all the php@ services and that’s all, the PHP configuration is done. Let’s plug it into Apache.
Multiple apache2 host with different php versions
So the primary goal of this guide is tho setup our httpd to be able to serve multiple php versions simultaneously. We’ll accomplish this by adding multiple hostnames to our local server.
I use php56.localhost and php72.localhost, but you can choose whatever you prefer.
First edit /etc/hosts (with sudo!) and add our hostnames to the 127.0.0.1 line. The resulting one will look like this:
127.0.0.1 localhost php56.localhost php72.localhost
Now create a new file /usr/local/etc/httpd/extra/php-vhosts.conf with the following content:
<VirtualHost *:80> ServerName php56.localhost DocumentRoot "/Users/maxxer/work" ProxyPassMatch ^/(.*\.php(/.*)?)$ fcgi://127.0.0.1:9056/Users/maxxer/work/$1 timeout=1800 <Proxy fcgi://127.0.0.1:9056> ProxySet timeout=1800 </Proxy> </VirtualHost> <VirtualHost *:80> ServerName php72.localhost DocumentRoot "/Users/maxxer/work" ProxyPassMatch ^/(.*\.php(/.*)?)$ fcgi://127.0.0.1:9072/Users/maxxer/work/$1 timeout=1800 <Proxy fcgi://127.0.0.1:9072> ProxySet timeout=1800 </Proxy> </VirtualHost>
Enable fcgi module by decommenting the following lines in /usr/local/etc/httpd/httpd.conf:
LoadModule proxy_module lib/httpd/modules/mod_proxy.so LoadModule proxy_fcgi_module lib/httpd/modules/mod_proxy_fcgi.so
And add the following to the bottom of the file:
Finally restart apache:
sudo brew services restart httpd
Test the configuration by creating a phpinfo.php file in the webroot and then open http://php72.localhost/phpinfo.php and http://php56.localhost/phpinfo.php, they should show the different PHP versions!
We reached our first goal. Now a bonus treat: https!
Enable https with valid certificates on localhost with mkcert
mkcert is a project which makes it really trivial to create a local certification authority (CA), deploy this cert into the local browser and then generate as many trusted local certificates as you want.
First of all install mkcert with:
brew install nss mkcert
Then generate the CA and install into system’s trust store (and Firefox’s one):
Now our local CA is trusted! Move to the path where we want our certs to be created and generate our first cert with:
cd /usr/local/etc/httpd ; mkcert php56.localhost
We will now have php56.localhost-key.pem php56.localhost.pem respectively with key and cert. Let’s add them to apache. Open httpd.conf and enable ssl_module by uncommenting the following:
LoadModule ssl_module lib/httpd/modules/mod_ssl.so
Open up our created extra/php-vhosts.conf, duplicate the VirtualHost stanzas we created before and for each of them change the following:
Listen 443 <VirtualHost *:443> SSLEngine on SSLCertificateKeyFile "/usr/local/etc/httpd/php72.localhost-key.pem" SSLCertificateFile "/usr/local/etc/httpd/php72.localhost.pem" ServerName php72.localhost
That is adding SSLEngine, SSLCertificateKeyFile, SSLCertificateFile, changing the VirtualHost port and adding Listen directive (beware this one must be added only once in the file!).
Be sure to match certificates with ServerName, then restart httpd and you’re done!