macOS Mojave 10.14: local php development with multiple versions

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!

Multiple PHP versions on localhost - phpinfo() screenshot
PHP 5.6 and 7.2 on localhost

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!

Multiple PHP versions on localhost - phpinfo() screenshot
PHP 5.6 and 7.2 on localhost with valid https

Header picture by rawpixel on Unsplash

6 pensieri su “macOS Mojave 10.14: local php development with multiple versions

  1. 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.

  2. 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?

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *

Solve : *
10 × 5 =


Questo sito usa Akismet per ridurre lo spam. Scopri come i tuoi dati vengono elaborati.