Starting with PHP 7.4, PEAR is disabled by default, which means PECL is no longer available to install extensions. There's a number of reason to that decision, so we'll have to make do. It took me a few hours to update my Dockerfile and replace PECL calls with manual installations, that MongoDB one was quite tricky. I wrote this article because I wanted to play with PHP 7.4 but hit a wall with installing extensions. I hope it will be of some help.

Initial Dockerfile, with PECL

This is the Dockerfile of the base image of one of my services. It will be our starting point. We have a bunch of extensions in there. Some are bundled with PHP like sockets or opcache. Some need to be installed manually like apcu, redis, or mongodb.

FROM php:7.3.2-fpm-stretch

RUN apt-get update && \
    pecl channel-update pecl.php.net && \
    pecl install apcu igbinary mongodb && \
    # compile Redis with igbinary support
    pecl bundle redis && cd redis && phpize && ./configure --enable-redis-igbinary && make && make install && \
    docker-php-ext-install bcmath sockets && \
    docker-php-ext-enable apcu igbinary mongodb opcache redis && \
    docker-php-source delete && \
    rm -r /tmp/* /var/cache/* /var/www/html/*

RUN echo '\
opcache.interned_strings_buffer=16\n\
opcache.load_comments=Off\n\
opcache.max_accelerated_files=16000\n\
opcache.save_comments=Off\n\
' >> /usr/local/etc/php/conf.d/docker-php-ext-opcache.ini

Scripts available in the docker image

Before we get started let's have a quick overview of the scripts available in the Docker image.

First we have docker-php-source, that extracts PHP source required to build the extensions, and also deletes it. Next up we have docker-php-ext-configure, that configures an extension before it gets installed. And finally we have docker-php-ext-install, that installs extension(s). Basically, almost everything we do with extensions needs to happen between docker-php-source extract and docker-php-source delete.

The scripts are described in the How to install more PHP extensions section of the official image README.

Installing extensions

Installing extensions manually follow the same pattern, mostly:

  1. Create the corresponding directory in /usr/src/php/ext.
  2. Extract the source in that directory. The source is usually available on GitHub.
  3. Invoke docker-php-ext-install to install the extension.

Some extensions require a bit more work than others, but this is the gist of it. Let's begging with a simple installation such as ext-apcu.

Installing ext-apcu

We'll get ext-apcu source from GitHub. We'll use ENV to define the version we want so it's easy to spot and tweak.

FROM php:7.3.2-fpm-stretch

ENV EXT_APCU_VERSION=5.1.17

RUN docker-php-source extract \
    # 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 \
    # cleanup
    && docker-php-source delete

Installing ext-redis with ext-igbinary

Now things get a little more complicated, we want to install ext-redis with ext-igbinary as serializer. We'll use docker-php-ext-configure to configure ext-redis before its installation. Other than that, it's the same as ext-apcu.

FROM php:7.3.2-fpm-stretch

ENV EXT_REDIS_VERSION=4.3.0 EXT_IGBINARY_VERSION=3.0.1

RUN docker-php-source extract \
    # 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 \
    # 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 \
    # cleanup
    && docker-php-source delete

Installing ext-mongodb

Now things get a little hairy, compiling ext-mongodb requires a tad more work. We'll use multi-stage build to keep our result image as clean as possible. The repository uses submodules, and sadly they are not included in the archive, so we'll have to clone the repository, which is not ideal. Regarding the installation, I mostly followed the instructions in the manual.

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

Note: If your base image is phpdaily/php:7.4.0-dev-fpm-stretch you need to replace no-debug-non-zts-20180731 with no-debug-non-zts-20190529.

Final Dockerfile, without PECL

This is our final Dockerfile, without PECL. It's quite verbose, but now we can play with PHP 7.4 or even PHP 8.0.

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 \
    ## cleanup
    && docker-php-source delete

RUN echo '\
opcache.interned_strings_buffer=16\n\
opcache.load_comments=Off\n\
opcache.max_accelerated_files=16000\n\
opcache.save_comments=Off\n\
' >> /usr/local/etc/php/conf.d/docker-php-ext-opcache.ini