macOS 12 (Monterey) removed PHP 7 in Apple’s ongoing effort to remove scripting languages from macOS; this broke macOS applications that require PHP 7.

MDS, our macOS deployment tool, hosts MunkiReport, which itself depends on the Apache PHP module to function. macOS still includes Apache, but not PHP. In order to get MunkiReport to function, we needed to find a way to install PHP 7 via the MDS Installer Package.

Considering Homebrew

The most obvious way to solve this is to use Homebrew, a macOS package manager. However, this has some challenges. This method requires anyone using MDS to install PHP via Homebrew, which itself requires the Xcode command line tools. Even worse, after installing via Homebrew, new Apache requirements (that only allow signed modules to be loaded) prevent it from working, even after installation:

No code signing authority for module at libexec/libphp7.so specified in LoadModule directive.

Code signing absent - not loading module at: libexec/libphp7.so

Compiling and Signing

To resolve this issue, PHP 7 needs to be compiled, signed, and distributed with MDS.

PHP links to many external libraries:

libphp7.so:
libaprutil-1.0.dylib
libexpat.1.dylib
libiconv.2.dylib
libapr-1.0.dylib
libSystem.B.dylib
libtidy.58.dylib
libargon2.1.dylib
libresolv.9.dylib
libncurses.5.4.dylib
libaspell.15.dylib
libpspell.15.dylib
libpq.5.dylib
libsybdb.5.dylib
libldap.2.dylib
liblber.2.dylib
libc++.1.dylib
libgmp.10.dylib
libintl.8.dylib
libbz2.1.0.dylib
libxml2.2.dylib
libgssapi_krb5.2.2.dylib
libkrb5.3.3.dylib
libk5crypto.3.1.dylib
libcom_err.3.0.dylib
libssl.1.1.dylib
libcrypto.1.1.dylib
libpcre2-8.0.dylib
libsqlite3.0.dylib
libz.1.dylib
libcurl.4.dylib
libffi.8.dylib
libgd.3.dylib
libicuio.69.dylib
libicui18n.69.dylib
libicuuc.69.dylib
libicudata.69.dylib
libonig.5.dylib
libodbc.2.dylib
libedit.3.dylib
libsodium.23.dylib
libxslt.1.dylib
libicucore.A.dylib
libexslt.0.dylib
libzip.5.dylib

To compile PHP, most of these libraries would need to be included, as most are not included with macOS. This also requires the Apache plug-in system to build.

To make this easier, we used Homebrew used to install PHP 7 and the libraries in a temporary location and then all the libraries we collected to a single folder for easy deployment. For this to work, all the references in PHP and linked libraries need to be updated to point to the new folder.

Building with Homebrew in Non-Standard Location

To build PHP in a non standard location, Homebrew needs to be installed in target folder. Subsequently, all the software is installed in that location…

git clone https://github.com/Homebrew/brew.git

…and any brew commands can run.

There seems to be a missing dependency on curl, so that was installed separately:

#!/bin/sh -x

PHP_BUILD_FOLDER="php_build"
if [ ! -e "${PHP_BUILD_FOLDER}" ]; then
	mkdir -p "${PHP_BUILD_FOLDER}"
fi

pushd ${PHP_BUILD_FOLDER}

if [ ! -e "brew" ];  then
	echo "cloning brew"
	git clone https://github.com/Homebrew/brew.git
fi

cd brew
echo "updating brew"
bin/brew update

echo "installing curl otherwise php install will fail"
bin/brew install curl

echo "setting tap"

bin/brew tap shivammathur/php

echo "installing php7.4"
bin/brew install shivammathur/php/php@7.4

echo "make relocatable"

cd .. 

if [ ! -e  "mds-php" ]; then
	
	mkdir -p mds-php/bin
	mkdir -p mds-php/lib
	
fi
cp -f brew/Cellar/php@7.4/7.4.27/bin/php mds-php/bin/
./php_script.py brew/Cellar/php@7.4/7.4.27/lib/httpd/modules/libphp7.so
./php_script.py mds-php/bin/php

echo "done"

When the script is run, PHP 7 will be installed in the custom brew folder. The libraries required by the libphp7.so module and the PHP binary are sent to a great script by Andy Duplain. His script looks at all the linked libraries, collects them up into a single folder, and updates the link references.

Once that script is run, the destination folder (“mds-php”) contains a bin and lib folder containing the PHP binary, the apache module (libphp7.so), and all required libraries.

The script originally updated the libraries to use relative links (rpath) so that the folder could be moved around on the destination and still work. However, Apache does not allow use of path unless SIP is disabled. The Python relocate script was then updated to hardcode the path to where the destination PHP would be installed. Instead of including the install files inside the app, it is installed in /usr/local/mds-php7. All libraries are linked to the lib folder in that location.

Universal Binaries

Since MDS runs on both macOS on Intel and Apple Silicon, the same process was repeated on Apple Silicon. Homebrew does not cross-compile packages easily, so the script was run on an M1 Mac with the resulting binaries combined via lipo. The files were also signed and hardened so both Apache and notarization would accept them:

#!/bin/bash

if [ ! -e  "mds-php-universal/lib" ]; then
  mkdir -p mds-php-universal/lib
  
fi

if [ ! -e  "mds-php-universal/bin" ]; then
  mkdir -p mds-php-universal/bin
  
fi
cd "mds-php/lib"

for file in *; do
  if [ -f "$file" ]; then 
    echo "combining $file with lipo"
    lipo -create -output ../../mds-php-universal/lib/"$file" "$file" ../../mds-php-arm/lib/"$file"
    echo "signing $file"
    codesign -v --force -o runtime --sign "Developer ID Application: Twocanoes Software, Inc. (UXP6YEHSPW)" ../../mds-php-universal/lib/"$file"

  fi
done

cd ../bin

if [ -e "php" ]; then
  echo "combining $file with lipo"
  lipo -create -output ../../mds-php-universal/bin/php "php" ../../mds-php-arm/bin/php
  codesign -v --force -o runtime --sign "Developer ID Application: Twocanoes Software, Inc. (UXP6YEHSPW)" ../../mds-php-universal/bin/php

fi

The resulting folder (“mds-php-universal”) is then packaged up, with the files in that folder installed to /usr/local/mds-php with the MDS package installer.

Using the PHP Apache module

Using the Apache PHP module requires updating the Apache config file to reference the new module. The common name of the certificate that signed it must be specified as well:

LoadModule php7_module "/usr/local/mds-php/lib/libphp7.so" "Developer ID Application: Twocanoes Software, Inc. (UXP6YEHSPW)"

Result

The resulting package can now be installed on any macOS system; it doesn’t require Homebrew to be installed, nor does it have any external dependencies. It uses the built-in Apache on macOS, does not require disabling SIP, and uses codesigning to validate the loaded modules for Apache. MDS builds as of 48054 now include PHP 7 installed into /usr/local/mds-php.

Stay Informed on macOS Utilities and Tools

Sign up for open source/Mac-related updates, news, and information.
Name
Contact Permission