Compile your own PHP version from source and install it on MAMP PRO on macOS. It's possible -- despite what MAMP's developers want you to believe.
I wanted to test my software against PHP 7.3 on macOS instead of just my Linux machines and / or Docker containers. According to MAMP's support "You cannot compile your own entire versions of PHP in MAMP PRO". This is of course a ridiculous lie; they do it themselves so surely I can! This article walks you step by step to my process of adding PHP 7.3.0 to MAMP 5.2 on this lazy Saturday afternoon.
"It's possible" does not mean "it's easy". This procedure is not for the faint of heart, if you don't have experience building stuff from source (and troubleshoot cryptic compile error message) or if you're in a mad dash to deliver software. You will stumble. You will fall. You will curse at your computer. You will be frustrated. You will be searching online cryptic error messages only to find a million pages from five years ago which do not apply. YOU WILL SUFFER. Is it worth it? Hell yeah. That's how you get to learn stuff. If you want quick results just wait a few months for MAMP PRO to include a new version of PHP. It will save you time, money and frustration (and keep you blissfully unaware of the gruesome process of compiling software from scratch). Proceed at your own risk and peril.
Appsolute, the makers of MAMP, make it unnecessarily complicated – read: impossible – to compile your own Apache modules by not shipping the build headers for Apache. Therefore we are only building PHP CLI and CGI/FastCGI, not as an Apache module. This is not a big deal, though. The recommended way to run PHP 5.3 and later is through FastCGI. Just select the "Individual PHP version for every host (CGI mode)" option. FastCGI is also the only way PHP works on NginX.
Likewise, they do not ship build headers for anything except PHP itself – definitely not for any of the build dependencies. We are going to use Homebrew to install the required PHP build dependencies. The actual caveat is that you can't just pluck the compiled PHP from your computer and install it on your mate's. Not unless you also install the dependencies outlined below with Homebrew.
I compiled most PHP modules but I left some out because I don't use them and don't have the time to troubleshoot their build dependencies. This should not be an issue for most people.
Finally, you will need to edit your PHP version's php.ini template file and manually enable additional PHP extensions. Unlike MAMP, I try to build most of the extensions as "shared", i.e. you have to load them through php.ini. I sometimes need to disable stuff to see if my code fails gracefully.
I have two Macs at my disposal. One Mac Mini which was upgraded from macOS Sierra to High Sierra to Mojave; and a MacBook Pro which was freshly installed with Mojave. I used the former to come up with the instructions and the second, "virgin" machine to verify that everything works. If you get compilation errors check what you have installed in Homebrew and where you have linked it to. This is the source of all problems and please don't ask me to help you; just like you, I search the error message I am getting and plow through the results until I find something which seems like a promising solution. OK. Let's get started, then!
Before you begin your quest to a custom PHP version in MAMP you need a sane build environment with all the PHP dependencies installed.
First, install XCode from the App Store. This is Apple's all-in-one development package which includes system headers and the all important build toolchain (C compiler, automake, autoconf and so on and so forth).
Now open XCode and accept the license. It will take a while. This is the part where XCode installs the software and header files we need to build PHP. If you forget to do that you will get compilation errors when building PHP. After it's done installing you can quit XCode. We don't need the actual application running.
Apple has made it unnecessarily complicated to build software from the command line by tucking away all of the libraries and headers in a non-standard folder. For macOS Mojave you need to run this from a Terminal
sudo installer -pkg /Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.14.pkg -target /
This puts everything into the standard folders, e.g. /usr/include. Please check that you have the /usr/include folder after running this command. If you don't, PHP compilation WILL fail and you'll spend two hours trying to figure out what the heck is going on. Been there. Obviously, if you do not have Mojave you need to replace the 10.14 in the command above with your macOS version number.
Next up, install Homebrew. If you're not absolutely new to macOS you already know about it. For everyone else, Homebrew is the de facto package manager for macOS. It allows us to install and upgrade libraries and tools in a simple and sensible manner. Follow the instructions on the link above to install Homebrew. At the time of this writing (December 2018) you need to open Terminal and enter:
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
Now we will have to install the dependencies for building PHP and a bunch of its extensions. Open a Terminal and enter the following:
brew install argon2 brew install aspell brew install autoconf brew install curl-openssl brew install enchant brew install freetds brew install freetype brew link --overwrite freetype brew install gd brew install gdbm brew install gettext brew install glib brew install gmp brew install icu4c brew install imap-uw brew install jpeg brew install libpng brew install libpq brew install libsodium brew install libssh2 brew install libxml2 brew install libxslt brew install libzip brew install mhash brew install net-snmp brew install openldap brew install openssl brew install pcre brew install pkgconfig brew link pkgconfig brew install readline brew install sqlite brew install tidy-html5 brew install unixodbc brew install webp
Please note that I have only tried compiling PHP 7.1, 7.2 and 7.3. If you are compiling an older or newer version of PHP you may have to add more build dependencies.
MAMP's directory structure
Let's pause for a second and understand what we are doing. MAMP stores its files in several different places. These locations are not documented but they are not that hard to reverse engineer.
Every PHP version is stored in /Applications/MAMP/bin/php/phpX.Y.Z where X.Y.Z is your PHP version. Major caveat, X, Y and Z must all be integers without anything else. This means you cannot install alphas, betas or RC versions unless you lie. For example, if you were to install PHP 7.3.0RC6 you'd be installing it as php7.3.0. Anyway. This folder contains a mostly standard PHP installation folder layout. The exceptions are two directories.
The /Applications/MAMP/bin/php/phpX.Y.Z/conf folder contains the pear.conf file (which we don't use), the default php.ini file (which is not used) and the default php.ini.temp MAMP-specific template file (which is NOT used). This folder must exist but its contents are not used during MAMP's regular operation, they are only used when MAMP is installing a new PHP version.
The /Applications/MAMP/bin/php/phpX.Y.Z/modules folder is where MAMP expects to find the PHP Apache module file (libphp7.so). Major caveat! If the file does not exist MAMP will not show this PHP version in its interface. But we can't build that file. So... are we screwed? Well, no. We can just create a zero byte file and MAMP will work happily.
There's another important location: /Users/your_user/Library/Application Support/appsolute/MAMP PRO/templates/phpX.Y.Z.ini.temp. This is the MAMP-specific php.ini template file you edit through MAMP's File, Edit Template, PHP (php.ini) submenu. This file is parsed when MAMP is starting the servers, creating the real php.ini file in /Library/Application Support/appsolute/MAMP PRO/conf/phpX.Y.Z.ini. Note that the REAL file is in /Library, NOT /Users/your_user/Library.
Remember the location of the real, generated php.ini file when you want to call PHP from the command line. If you do not specify this configuration file with the -c switch then the default /Applications/MAMP/bin/php/phpX.Y.Z/conf/php.ini file will be used instead which is NOT the one you configure through MAMP PRO's interface! That is, call PHP CLI as:
php -c /Library/Application\ Support/appsolute/MAMP\ PRO/conf/phpX.Y.Z.ini your-script.php
Compile your own PHP version
Go ahead and download the PHP version you want from the official PHP site. I downloaded and extracted PHP 7.3.0 inside ~/php, a subfolder I created for this reason.
You can blindly use the following code in Terminal changing just the first line. Replace 7.3.0 with your PHP version.
MY_PHP_VERSION=7.3.0 # Download and extract PHP mkdir ~/php cd ~/php curl "http://it2.php.net/get/php-$MY_PHP_VERSION.tar.bz2/from/this/mirror" -o "php-$MY_PHP_VERSION.tar.bz2" -L tar xjf php-$MY_PHP_VERSION.tar.bz2 cd ~/php/php-$MY_PHP_VERSION # Important! We need to tell the system to use HomeBrew's pkg-config # to get the correct dependencies during the build process. export PATH=/usr/local/bin/:$PATH # Compile the new PHP version and install it under MAMP's folder ./configure \ --prefix=/Applications/MAMP/bin/php/php$MY_PHP_VERSION \ --sysconfdir=/Applications/MAMP/bin/php/php$MY_PHP_VERSION/conf \ --enable-phpdbg \ --enable-phpdbg-webhelper \ --with-layout=GNU \ --with-config-file-path=/Library/Application\ Support/appsolute/MAMP\ PRO/conf \ --enable-cli \ --enable-cgi \ --enable-ipv6 \ --enable-libxml \ --with-openssl=$(brew --prefix openssl) \ --with-kerberos \ --with-pcre-regex \ --with-pcre-jit \ --with-zlib \ --with-bz2 \ --enable-bcmath \ --enable-calendar \ --enable-ctype \ --with-curl=shared,$(brew --prefix curl-openssl) \ --enable-dba=shared \ --with-gdbm=$(brew --prefix gdbm) \ --enable-inifile \ --enable-flatfile \ --enable-dom \ --enable-exif \ --enable-fileinfo \ --enable-filter \ --enable-ftp \ --with-openssl-dir=$(brew --prefix openssl) \ --with-gd=shared \ --with-gettext=$(brew --prefix gettext) \ --with-gmp=$(brew --prefix gmp) \ --with-mhash=$(brew --prefix mhash) \ --with-iconv \ --with-imap=shared,$(brew --prefix imap-uw) \ --with-imap-ssl=$(brew --prefix openssl) \ --enable-intl=shared \ --enable-json \ --with-ldap=shared,$(brew --prefix openldap) \ --enable-mbstring \ --enable-mbregex \ --with-mysqli=shared,mysqlnd \ --with-unixODBC=$(brew --prefix unixodbc) \ --enable-opcache \ --enable-opcache-file \ --enable-pcntl \ --enable-pdo \ --with-pdo=shared \ --with-pdo-sqlite=shared \ --with-pdo-mysql=shared,mysqlnd \ --with-pdo-odbc=shared,unixODBC,$(brew --prefix unixodbc) \ --with-pdo-pgsql=$(brew --prefix libpq) \ --with-pgsql=$(brew --prefix libpq) \ --enable-phar \ --enable-posix \ --with-pspell=shared,$(brew --prefix aspell) \ --enable-shmop=shared \ --enable-soap=shared \ --enable-sockets \ --with-sodium=$(brew --prefix libsodium) \ --with-password-argon2=$(brew --prefix argon2) \ --enable-sysvmsg=shared \ --enable-sysvsem=shared \ --enable-sysvshm=shared \ --enable-tokenizer \ --enable-wddx \ --enable-xml \ --with-xmlrpc=shared \ --with-xsl \ --with-pear \ && make -j 8 \ && make install -j 8 # Create the expected configuration folder mkdir /Applications/MAMP/bin/php/php$MY_PHP_VERSION/conf # Create a php.ini template, copying the one used by MAMP's PHP 7.2.10 cp /Applications/MAMP/bin/php/php7.2.10/conf/php.ini.temp /Applications/MAMP/bin/php/php$MY_PHP_VERSION/conf/php.ini.temp # Copy the php.ini template to its expected folder cp /Applications/MAMP/bin/php/php$MY_PHP_VERSION/conf/php.ini.temp ~/Library/Application\ Support/appsolute/MAMP\ PRO/templates/php$MY_PHP_VERSION.ini.temp # Create a default php.ini file cp php.ini-development /Applications/MAMP/bin/php/php$MY_PHP_VERSION/conf/php.ini # Copy the default php.ini file to its expected folder mkdir /Applications/MAMP/conf/php$MY_PHP_VERSION cp /Applications/MAMP/bin/php/php$MY_PHP_VERSION/conf/php.ini /Applications/MAMP/conf/php$MY_PHP_VERSION/php.ini # Create the CGI helper cat << EOF > /Applications/MAMP/fcgi-bin/php$MY_PHP_VERSION.fcgi #!/bin/sh export PHP_FCGI_CHILDREN=4 export PHP_FCGI_MAX_REQUESTS=200 exec /Applications/MAMP/bin/php/php$MY_PHP_VERSION/bin/php-cgi -c "/Library/Application Support/appsolute/MAMP PRO/conf/php$MY_PHP_VERSION.ini" EOF # Fake installation of Apache PHP module. THIS IS JUST TO TRICK MAMP. mkdir /Applications/MAMP/bin/php/php$MY_PHP_VERSION/modules touch mkdir /Applications/MAMP/bin/php/php$MY_PHP_VERSION/modules/libphp7.so
Tip: The make commands have a -j 8 switch. This means "run up to 8 tasks in parallel". That's because I have a Mac Mini with a 4-core Intel Core i7 processor with Hyperthreading. This means each core can run two threads at the same time, thus 4 cores x 2 threads per core = 8 threads simultaneously. If you have a dual-core processor, as in with most MacBook Pro and Air machines, set this to -j 4. Note that this utilises your processor optimally, reduces the compilation time but will make your Mac run hot.
At this point you need to stop MAMP's servers, quit MAMP PRO completely and then start it again. Now go to the PHP tab. Here's your new PHP version!
Before you get all too excited you should go to File, Edit Template, PHP (php.ini) and select your PHP version. You need to edit the extension=... lines. You can find the extensions you have compiled under the /Applications/MAMP/bin/php/php7.3.0/lib/php/20180731 folder. Remember that 7.3.0 and 20180731 in that path depend on which PHP version you have compiled. You are interested only in the .so files.
My section of extension loads looks like this:
;MAMP_apc_MAMPextension=apcu.so ;MAMP_apc_MAMPextension=apc.so extension=curl.so extension=dba.so extension=gd.so extension=imap.so extension=intl.so extension=ldap.so extension=mysqli.so extension=pdo_mysql.so extension=pdo_odbc.so extension=pdo_sqlite.so extension=pspell.so extension=shmop.so extension=soap.so extension=sysvmsg.so extension=sysvmem.so extension=sysvshm.so extension=xmlrpc.so ;MAMP_Imagick_MAMPextension=imagick.so ;MAMP_Tidy_MAMPextension=tidy.so ;MAMP_Oauth_MAMPextension=oauth.so ;MAMP_Igbinary_MAMPextension=igbinary.so ;MAMP_Memcached_MAMPextension=memcached.so ;MAMP_Redis_MAMPextension=redis.so ;extension=uploadprogress.so ;extension=yaml.so
Note that all of the extensions controlled by MAMP are actually not built since they are either not part of the PHP distribution (they are installed through PECL) or I didn't bother building them. You will see how to build them in the next section.
Compiling additional PHP extensions
You can do that by typing the following in Terminal (change the first line to reflect your PHP version):
MY_PHP_VERSION=7.3.0 export PATH=/usr/local/bin/:$PATH export PATH=/Applications/MAMP/bin/php/php$MY_PHP_VERSION/bin:$PATH export PHP_PEAR_PHP_BIN="/Applications/MAMP/bin/php/php$MY_PHP_VERSION/bin/php" # You only need to run channel-update after the first time you install a new PHP version pecl channel-update pecl.php.net
Now on the same Terminal session you can install PHP extensions using
pecl install. For example:
pecl install redis
pecl install igbinary
For some extensions you need to pass an explicit version number. For example, for the all important XDebug debugging extension you need to run:
pecl install xdebug-2.7.0beta1
Some extensions may need prerequisites to be installed. For example:
brew install pcre brew link pcre pecl install oauth
At the time of this writing not all PHP extensions are compatible with PHP 7.3. I was unable to compile memcached, apc, apcu and ssh2. That's what you get for trying the bleeding edge. This is also my response to the people asking "why are you not using PHP 7.3 in production yet?". It's simply not ready if you rely on certain extensions. That's why there's an overlap of actively maintained releases, to give time to developers to adapt their extensions and PHP software developers to test before migrating to an infrastructure of unknown compatibility status. That's the whole point of self-compiling the very latest PHP version: get our software ready.