{{ def version_id: # https://www.php.net/phpversion # $version_id = $major_version * 10000 + $minor_version * 100 + $release_version; sub("[a-zA-Z].*$"; "") | split(".") | ( (.[0] // 0 | tonumber) * 10000 + (.[1] // 0 | tonumber) * 100 + (.[2] // 0 | tonumber) ) ; def is_alpine: env.from | startswith("alpine") -}} FROM {{ env.from }} {{ if is_alpine then "" else ( -}} # prevent Debian's PHP packages from being installed # https://github.com/docker-library/php/pull/542 RUN set -eux; \ { \ echo 'Package: php*'; \ echo 'Pin: release *'; \ echo 'Pin-Priority: -1'; \ } > /etc/apt/preferences.d/no-debian-php {{ ) end -}} # dependencies required for running "phpize" {{ if is_alpine then ( -}} # these get automatically installed and removed by "docker-php-ext-*" (unless they're already installed) {{ ) else ( -}} # (see persistent deps below) {{ ) end -}} ENV PHPIZE_DEPS \ autoconf \ dpkg-dev {{ if is_alpine then "dpkg " else "" end }}\ file \ g++ \ gcc \ libc-dev \ make \ {{ if is_alpine then "pkgconf" else "pkg-config" end }} \ re2c # persistent / runtime deps {{ if is_alpine then ( -}} RUN apk add --no-cache \ ca-certificates \ curl \ tar \ xz \ # https://github.com/docker-library/php/issues/494 openssl # ensure www-data user exists RUN set -eux; \ {{ if env.alpineVer == "3.13" then ( -}} addgroup -g 82 -S www-data; \ {{ ) else "" end -}} adduser -u 82 -D -S -G www-data www-data # 82 is the standard uid/gid for "www-data" in Alpine # https://git.alpinelinux.org/aports/tree/main/apache2/apache2.pre-install?h=3.14-stable # https://git.alpinelinux.org/aports/tree/main/lighttpd/lighttpd.pre-install?h=3.14-stable # https://git.alpinelinux.org/aports/tree/main/nginx/nginx.pre-install?h=3.14-stable {{ ) else ( -}} RUN set -eux; \ apt-get update; \ apt-get install -y --no-install-recommends \ $PHPIZE_DEPS \ ca-certificates \ curl \ xz-utils \ ; \ rm -rf /var/lib/apt/lists/* {{ ) end -}} ENV PHP_INI_DIR /usr/local/etc/php RUN set -eux; \ mkdir -p "$PHP_INI_DIR/conf.d"; \ # allow running as an arbitrary user (https://github.com/docker-library/php/issues/743) [ ! -d /var/www/html ]; \ mkdir -p /var/www/html; \ chown www-data:www-data /var/www/html; \ chmod 777 /var/www/html {{ if env.variant == "apache" then ( -}} ENV APACHE_CONFDIR /etc/apache2 ENV APACHE_ENVVARS $APACHE_CONFDIR/envvars RUN set -eux; \ apt-get update; \ apt-get install -y --no-install-recommends apache2; \ rm -rf /var/lib/apt/lists/*; \ \ # generically convert lines like # export APACHE_RUN_USER=www-data # into # : ${APACHE_RUN_USER:=www-data} # export APACHE_RUN_USER # so that they can be overridden at runtime ("-e APACHE_RUN_USER=...") sed -ri 's/^export ([^=]+)=(.*)$/: ${\1:=\2}\nexport \1/' "$APACHE_ENVVARS"; \ \ # setup directories and permissions . "$APACHE_ENVVARS"; \ for dir in \ "$APACHE_LOCK_DIR" \ "$APACHE_RUN_DIR" \ "$APACHE_LOG_DIR" \ ; do \ rm -rvf "$dir"; \ mkdir -p "$dir"; \ chown "$APACHE_RUN_USER:$APACHE_RUN_GROUP" "$dir"; \ # allow running as an arbitrary user (https://github.com/docker-library/php/issues/743) chmod 777 "$dir"; \ done; \ \ # delete the "index.html" that installing Apache drops in here rm -rvf /var/www/html/*; \ \ # logs should go to stdout / stderr ln -sfT /dev/stderr "$APACHE_LOG_DIR/error.log"; \ ln -sfT /dev/stdout "$APACHE_LOG_DIR/access.log"; \ ln -sfT /dev/stdout "$APACHE_LOG_DIR/other_vhosts_access.log"; \ chown -R --no-dereference "$APACHE_RUN_USER:$APACHE_RUN_GROUP" "$APACHE_LOG_DIR" # Apache + PHP requires preforking Apache for best results RUN a2dismod mpm_event && a2enmod mpm_prefork # PHP files should be handled by PHP, and should be preferred over any other file type RUN { \ echo ''; \ echo '\tSetHandler application/x-httpd-php'; \ echo ''; \ echo; \ echo 'DirectoryIndex disabled'; \ echo 'DirectoryIndex index.php index.html'; \ echo; \ echo ''; \ echo '\tOptions -Indexes'; \ echo '\tAllowOverride All'; \ echo ''; \ } | tee "$APACHE_CONFDIR/conf-available/docker-php.conf" \ && a2enconf docker-php {{ ) else "" end -}} # Apply stack smash protection to functions using local buffers and alloca() # Make PHP's main executable position-independent (improves ASLR security mechanism, and has no performance impact on x86_64) # Enable optimization (-O2) # Enable linker optimization (this sorts the hash buckets to improve cache locality, and is non-default) # https://github.com/docker-library/php/issues/272 # -D_LARGEFILE_SOURCE and -D_FILE_OFFSET_BITS=64 (https://www.php.net/manual/en/intro.filesystem.php) ENV PHP_CFLAGS="-fstack-protector-strong -fpic -fpie -O2 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64" ENV PHP_CPPFLAGS="$PHP_CFLAGS" ENV PHP_LDFLAGS="-Wl,-O1 -pie" ENV GPG_KEYS {{ .gpgKeys }} ENV PHP_VERSION {{ .version }} ENV PHP_URL="{{ .url }}" PHP_ASC_URL="{{ .ascUrl // "" }}" ENV PHP_SHA256="{{ .sha256 // "" }}" RUN set -eux; \ \ {{ if is_alpine then ( -}} apk add --no-cache --virtual .fetch-deps gnupg; \ {{ ) else ( -}} savedAptMark="$(apt-mark showmanual)"; \ apt-get update; \ apt-get install -y --no-install-recommends gnupg dirmngr; \ rm -rf /var/lib/apt/lists/*; \ {{ ) end -}} \ mkdir -p /usr/src; \ cd /usr/src; \ \ curl -fsSL -o php.tar.xz "$PHP_URL"; \ \ if [ -n "$PHP_SHA256" ]; then \ echo "$PHP_SHA256 *php.tar.xz" | sha256sum -c -; \ fi; \ \ if [ -n "$PHP_ASC_URL" ]; then \ curl -fsSL -o php.tar.xz.asc "$PHP_ASC_URL"; \ export GNUPGHOME="$(mktemp -d)"; \ for key in $GPG_KEYS; do \ gpg --batch --keyserver keyserver.ubuntu.com --recv-keys "$key"; \ done; \ gpg --batch --verify php.tar.xz.asc php.tar.xz; \ gpgconf --kill all; \ rm -rf "$GNUPGHOME"; \ fi; \ \ {{ if is_alpine then ( -}} apk del --no-network .fetch-deps {{ ) else ( -}} apt-mark auto '.*' > /dev/null; \ apt-mark manual $savedAptMark > /dev/null; \ apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false {{ ) end -}} COPY docker-php-source /usr/local/bin/ RUN set -eux; \ {{ if is_alpine then ( -}} apk add --no-cache --virtual .build-deps \ {{ ) else ( -}} \ savedAptMark="$(apt-mark showmanual)"; \ apt-get update; \ apt-get install -y --no-install-recommends \ {{ ) end -}} {{ [ [ if is_alpine then # alpine packages "$PHPIZE_DEPS", "argon2-dev", "coreutils", "curl-dev", "libsodium-dev", "libxml2-dev", "openssl-dev", "readline-dev", "sqlite-dev", # https://github.com/docker-library/php/issues/888 if (.version | version_id) >= ("7.4" | version_id) then "linux-headers" else empty end, # oniguruma is part of mbstring in php 7.4+ if (.version | version_id) >= ("7.4" | version_id) then "oniguruma-dev" else empty end else # debian packages if env.variant == "apache" then "apache2-dev" else empty end, "libargon2-dev", "libcurl4-openssl-dev", "libreadline-dev", "libsodium-dev", "libsqlite3-dev", "libssl-dev", "libxml2-dev", "zlib1g-dev", # oniguruma is part of mbstring in php 7.4+ if (.version | version_id) >= ("7.4" | version_id) then "libonig-dev" else empty end end ] | sort[] | ( -}} {{ . }} \ {{ ) ] | add -}} ; \ \ export \ CFLAGS="$PHP_CFLAGS" \ CPPFLAGS="$PHP_CPPFLAGS" \ LDFLAGS="$PHP_LDFLAGS" \ ; \ docker-php-source extract; \ cd /usr/src/php; \ gnuArch="$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)"; \ {{ if is_alpine then "" else ( -}} debMultiarch="$(dpkg-architecture --query DEB_BUILD_MULTIARCH)"; \ # https://bugs.php.net/bug.php?id=74125 if [ ! -d /usr/include/curl ]; then \ ln -sT "/usr/include/$debMultiarch/curl" /usr/local/include/curl; \ fi; \ {{ ) end -}} ./configure \ --build="$gnuArch" \ --with-config-file-path="$PHP_INI_DIR" \ --with-config-file-scan-dir="$PHP_INI_DIR/conf.d" \ \ # make sure invalid --configure-flags are fatal errors instead of just warnings --enable-option-checking=fatal \ \ # https://github.com/docker-library/php/issues/439 --with-mhash \ \ # https://github.com/docker-library/php/issues/822 --with-pic \ \ # --enable-ftp is included here because ftp_ssl_connect() needs ftp to be compiled statically (see https://github.com/docker-library/php/issues/236) --enable-ftp \ # --enable-mbstring is included here because otherwise there's no way to get pecl to use it properly (see https://github.com/docker-library/php/issues/195) --enable-mbstring \ # --enable-mysqlnd is included here because it's harder to compile after the fact than extensions are (since it's a plugin for several extensions, not an extension in itself) --enable-mysqlnd \ # https://wiki.php.net/rfc/argon2_password_hash --with-password-argon2 \ # https://wiki.php.net/rfc/libsodium --with-sodium=shared \ # always build against system sqlite3 (https://github.com/php/php-src/commit/6083a387a81dbbd66d6316a3a12a63f06d5f7109) --with-pdo-sqlite=/usr \ --with-sqlite3=/usr \ \ --with-curl \ --with-openssl \ --with-readline \ --with-zlib \ \ {{ if (.version | version_id) >= ("7.4" | version_id) then ( -}} # in PHP 7.4+, the pecl/pear installers are officially deprecated (requiring an explicit "--with-pear") --with-pear \ \ {{ ) else "" end -}} # bundled pcre does not support JIT on s390x # https://manpages.debian.org/bullseye/libpcre3-dev/pcrejit.3.en.html#AVAILABILITY_OF_JIT_SUPPORT {{ if is_alpine then ( -}} $(test "$gnuArch" = 's390x-linux-musl' && echo '--without-pcre-jit') \ {{ ) else ( -}} $(test "$gnuArch" = 's390x-linux-gnu' && echo '--without-pcre-jit') \ --with-libdir="lib/$debMultiarch" \ {{ ) end -}} {{ # https://github.com/docker-library/php/issues/280 -}} {{ if env.variant == "cli" then "" else ( -}} \ --disable-cgi \ {{ ) end -}} {{ if (env.variant == "cli" or env.variant == "zts") and (is_alpine | not) then ( -}} \ # https://github.com/docker-library/php/pull/939#issuecomment-730501748 --enable-embed \ {{ ) else "" end -}} {{ if env.variant == "apache" then ( -}} \ --with-apxs2 \ {{ ) elif env.variant == "fpm" then ( -}} \ --enable-fpm \ --with-fpm-user=www-data \ --with-fpm-group=www-data \ {{ ) elif env.variant == "zts" then ( -}} \ {{ if (.version | version_id) >= ("8" | version_id) then ( -}} --enable-zts \ {{ ) else ( -}} --enable-maintainer-zts \ {{ ) end -}} {{ ) else "" end -}} ; \ make -j "$(nproc)"; \ find -type f -name '*.a' -delete; \ make install; \ find /usr/local/bin /usr/local/sbin -type f -perm +0111 -exec strip --strip-all '{}' + || true; \ make clean; \ \ # https://github.com/docker-library/php/issues/692 (copy default example "php.ini" files somewhere easily discoverable) cp -v php.ini-* "$PHP_INI_DIR/"; \ \ cd /; \ docker-php-source delete; \ \ {{ if is_alpine then ( -}} runDeps="$( \ scanelf --needed --nobanner --format '%n#p' --recursive /usr/local \ | tr ',' '\n' \ | sort -u \ | awk 'system("[ -e /usr/local/lib/" $1 " ]") == 0 { next } { print "so:" $1 }' \ )"; \ apk add --no-cache $runDeps; \ \ apk del --no-network .build-deps; \ {{ ) else ( -}} # reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies apt-mark auto '.*' > /dev/null; \ [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; \ find /usr/local -type f -executable -exec ldd '{}' ';' \ | awk '/=>/ { print $(NF-1) }' \ | sort -u \ | xargs -r dpkg-query --search \ | cut -d: -f1 \ | sort -u \ | xargs -r apt-mark manual \ ; \ apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \ rm -rf /var/lib/apt/lists/*; \ {{ ) end -}} \ # update pecl channel definitions https://github.com/docker-library/php/issues/443 pecl update-channels; \ rm -rf /tmp/pear ~/.pearrc; \ \ # smoke test php --version COPY docker-php-ext-* docker-php-entrypoint /usr/local/bin/ # sodium was built as a shared module (so that it can be replaced later if so desired), so let's enable it too (https://github.com/docker-library/php/issues/598) RUN docker-php-ext-enable sodium {{ # https://github.com/docker-library/php/issues/865 # https://bugs.php.net/bug.php?id=76324 # https://github.com/php/php-src/pull/3632 # https://github.com/php/php-src/commit/2d03197749696ac3f8effba6b7977b0d8729fef3 if (is_alpine | not) and (.version | version_id) < ("7.4" | version_id) then ( -}} # temporary "freetype-config" workaround for https://github.com/docker-library/php/issues/865 (https://bugs.php.net/bug.php?id=76324) RUN { echo '#!/bin/sh'; echo 'exec pkg-config "$@" freetype2'; } > /usr/local/bin/freetype-config && chmod +x /usr/local/bin/freetype-config {{ ) else "" end -}} ENTRYPOINT ["docker-php-entrypoint"] {{ if env.variant == "apache" then ( -}} # https://httpd.apache.org/docs/2.4/stopping.html#gracefulstop STOPSIGNAL SIGWINCH COPY apache2-foreground /usr/local/bin/ WORKDIR /var/www/html EXPOSE 80 {{ ) elif env.variant == "fpm" then ( -}} WORKDIR /var/www/html RUN set -eux; \ cd /usr/local/etc; \ if [ -d php-fpm.d ]; then \ # for some reason, upstream's php-fpm.conf.default has "include=NONE/etc/php-fpm.d/*.conf" sed 's!=NONE/!=!g' php-fpm.conf.default | tee php-fpm.conf > /dev/null; \ cp php-fpm.d/www.conf.default php-fpm.d/www.conf; \ else \ # PHP 5.x doesn't use "include=" by default, so we'll create our own simple config that mimics PHP 7+ for consistency mkdir php-fpm.d; \ cp php-fpm.conf.default php-fpm.d/www.conf; \ { \ echo '[global]'; \ echo 'include=etc/php-fpm.d/*.conf'; \ } | tee php-fpm.conf; \ fi; \ { \ echo '[global]'; \ echo 'error_log = /proc/self/fd/2'; \ {{ if (.version | version_id) >= ("7.3" | version_id) then ( -}} echo; echo '; https://github.com/docker-library/php/pull/725#issuecomment-443540114'; echo 'log_limit = 8192'; \ {{ ) else "" end -}} echo; \ echo '[www]'; \ echo '; if we send this to /proc/self/fd/1, it never appears'; \ echo 'access.log = /proc/self/fd/2'; \ echo; \ echo 'clear_env = no'; \ echo; \ echo '; Ensure worker stdout and stderr are sent to the main error log.'; \ echo 'catch_workers_output = yes'; \ {{ if (.version | version_id) >= ("7.3" | version_id) then ( -}} echo 'decorate_workers_output = no'; \ {{ ) else "" end -}} } | tee php-fpm.d/docker.conf; \ { \ echo '[global]'; \ echo 'daemonize = no'; \ echo; \ echo '[www]'; \ echo 'listen = 9000'; \ } | tee php-fpm.d/zz-docker.conf # Override stop signal to stop process gracefully # https://github.com/php/php-src/blob/17baa87faddc2550def3ae7314236826bc1b1398/sapi/fpm/php-fpm.8.in#L163 STOPSIGNAL SIGQUIT EXPOSE 9000 {{ ) else "" end -}} CMD {{ env.cmd }}