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.
Apache configuration
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 php@5.6
brew install php@7.2
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:
Include /usr/local/etc/httpd/extra/php-vhosts.conf
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):
mkcert -install
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!
It works! bro
I needed a part with HTTPS. Worked like a charm. Thanks dude
does not work. I followed everything. check everything 10x but I still get this error:
Service Unavailable
The server is temporarily unable to service your request due to maintenance downtime or capacity problems. Please try again later.
you should check apache and php error logs, the apache frontend error is a bit too vague to debug
This is not work anymore for php-5.6 (may be 7.0 as well), since they need libicu4c v64.2 (max), and php-7.3++ need latest libicu4c (currently is v66.1)
Any work arround for this problem?
What exactly doesn’t work? If it’s a php install problem you should search on brew’s github