In a previous article I explained how to install PHP extensions from source for PEAR is no longer available starting PHP 7.4 and PECL is bundled with it. I think I found a simpler solution that works with PECL packages and still doesn't require the PECL command.

The issue I had installing extensions from source is that some are tricky, especially when they use Git submodules (I'm looking at you mongodb). What is nice about PECL packages is that the source is ready to be compiled and the URLs to get the packages are consistent. So I started investigating that route.

A script to get packages from the PECL repository

To help me in the endeavor, I created a shell script, similar to the script already available in the PHP base image. I called it docker-php-ext-get, following the naming pattern.

#!/usr/bin/env sh
set -e

dir=/usr/src/php

if [ ! -f "$dir/.docker-extracted" ]; then
    echo >&2 "error: PHP source required, run 'docker-php-source extract' first"
    exit 1
fi

dir="$dir/ext"

usage() {
    echo "usage: $0 module-name module-version"
    echo "   ie: $0 redis 4.3.0"
}

name=$1
version=$2

if [ -z "$name" ]; then
    usage >&2
    exit 1
fi

if [ -z "$version" ]; then
    usage >&2
    exit 1
fi

mkdir -p "$dir/$name"
curl -fsSL "https://pecl.php.net/get/$name-$version.tgz" | tar xvz -C "$dir/$name" --strip 1

Using docker-php-ext-get in a Dockerfile

docker-php-ext-get needs to be copied inside the Docker image to be used, which is easy to do if it sits next to the Dockerfile, like the official scripts are. It needs to run between docker-docker-php-source extract and docker-docker-php-source delete, it works with one package at a time, and the exact version needs to be provided (which has the side effect of keeping the Dockerfile reproducible).

The following example demonstrates how to get and install mongodb v1.5.2:

FROM php:7.4.0alpha1-fpm-stretch

COPY docker-php-ext-get /usr/local/bin/

RUN docker-php-source extract &&\
    docker-php-ext-get mongodb 1.5.2 &&\
    docker-php-ext-install mongodb &&\
    docker-php-source delete

To check if the extension is installed properly you can run the following commands:

$ docker build -t php74-fpm-with-mongodb .
$ docker run php74-fpm-with-mongodb php -i | grep mongodb

You should get the following result:

Additional .ini files parsed => /usr/local/etc/php/conf.d/docker-php-ext-mongodb.ini,
mongodb
mongodb.debug => no value => no value

Updating our Dockerfile

This is the Dockerfile we got with the previous method:

# This is the base image for FPM containers, to speed up building.
# https://hub.docker.com/_/php/
FROM php:7.3.2-fpm-stretch AS ext-mongodb

ENV EXT_MONGODB_VERSION=1.5.2

RUN docker-php-source extract \
    && apt-get update && apt-get install git -y \
    && git clone --branch $EXT_MONGODB_VERSION --depth 1 https://github.com/mongodb/mongo-php-driver.git /usr/src/php/ext/mongodb \
    && cd /usr/src/php/ext/mongodb && git submodule update --init \
    && docker-php-ext-install mongodb

FROM php:7.3.2-fpm-stretch

# ext-mongodb
COPY --from=ext-mongodb /usr/local/etc/php/conf.d/docker-php-ext-mongodb.ini /usr/local/etc/php/conf.d/docker-php-ext-mongodb.ini
COPY --from=ext-mongodb /usr/local/lib/php/extensions/no-debug-non-zts-20180731/mongodb.so /usr/local/lib/php/extensions/no-debug-non-zts-20180731/mongodb.so

ENV EXT_APCU_VERSION=5.1.17 EXT_REDIS_VERSION=4.3.0 EXT_IGBINARY_VERSION=3.0.1

RUN docker-php-source extract \
    # ext-opache
    && docker-php-ext-enable opcache \
    # ext-igbinary
    && mkdir -p /usr/src/php/ext/igbinary \
    &&  curl -fsSL https://github.com/igbinary/igbinary/archive/$EXT_IGBINARY_VERSION.tar.gz | tar xvz -C /usr/src/php/ext/igbinary --strip 1 \
    && docker-php-ext-install igbinary \
    # ext-redis
    && mkdir -p /usr/src/php/ext/redis \
    && curl -fsSL https://github.com/phpredis/phpredis/archive/$EXT_REDIS_VERSION.tar.gz | tar xvz -C /usr/src/php/ext/redis --strip 1 \
    && docker-php-ext-configure redis --enable-redis-igbinary \
    && docker-php-ext-install redis \
    # ext-apcu
    && mkdir -p /usr/src/php/ext/apcu \
    && curl -fsSL https://github.com/krakjoe/apcu/archive/v$EXT_APCU_VERSION.tar.gz | tar xvz -C /usr/src/php/ext/apcu --strip 1 \
    && docker-php-ext-install apcu \
    # ext-bcmath, ext-sockets
    && docker-php-ext-install bcmath sockets

And this is the Dockerfile we get with the new method:

# This is the base image for FPM containers, to speed up building.
# https://hub.docker.com/_/php/
FROM php:7.3.2-fpm-stretch

COPY docker-php-ext-get /usr/local/bin/

RUN docker-php-source extract &&\
    docker-php-ext-enable opcache &&\
    docker-php-ext-get igbinary 3.0.1 &&\
    docker-php-ext-install igbinary &&\
    docker-php-ext-get redis 4.3.0 &&\
    docker-php-ext-configure redis --enable-redis-igbinary &&\
    docker-php-ext-install redis &&\
    docker-php-ext-get apcu 5.1.17 &&\
    docker-php-ext-install apcu &&\
    docker-php-ext-get mongodb 1.5.2 &&\
    docker-php-ext-install mongodb &&\
    docker-php-ext-install bcmath sockets

Much simpler, don't you agree?