Installing PHP 7 on macOS 12 Monterey

macOS 12 (Monterey) removed PHP7 in Apple’s ongoing effort to remove scripting languages from macOS. Any applications that required PHP 7 broke. MDS, our macOS deployment tool, hosts MunkiReport, which depends on the apache PHP module to function. macOS still includes Apache, but not PHP. In order to get MunkiReport to work, PHP 7 need to be installed

Considered Homebrew

The most obvious way to solve this is to use the macOS package manager Homebrew. However, this has some challenges. It required anyone using MDS to install PHP via Homebrew, which requires the Xcode command line tools. Even worse, after installing via Homebrew, it still did not work due to the new Apache requirements to only allow signed modules to be loaded:

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 issues, 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 since most are not included with macOS. It also required the Apache plug in system to build. To make this easier, homebrew was 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 and then all the software will then be installed in that location:

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

Any brew commands can then run and the packages will be installed in that location. There seems to be a missing dependency on curl so that had to be 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 send to a great script by Andy Duplain that 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 it would still work. However, apache does not allow rpath 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 into /usr/local/mds-php7 and all libraries are linked to the lib folder in that location.

Universal Binaries

Since MDS runs on both macOS on Intel and Apple Silicon, this same process was repeated on Apple Silicon. Homebrew does not cross compile packages easily, so the script was run on an M1 mac and the resulting binaries were combined using 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”) was then packaged up and the files in that folder were 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 then be installed on any macOS system and doesn’t require Homebrew to be installed and doesn’t have any external dependencies. It uses the built-in Apache on macOS, doesn’t require SIP to be disabled, and uses codesigning to validate the loaded modules for Apache. MDS builds as of 48054 now includes PHP 7 installed into /usr/local/mds-php.

Smart Card Utility Beta

Sign up to receive information about Smart Card Utility Beta and related news. Once you confirm your subscription, a link will be sent to download the current beta via TestFlight.
Name(Required)
Contact Permission(Required)