diff --git a/Dockerfile.template b/Dockerfile.template index 017f0ed466..893694b9d9 100644 --- a/Dockerfile.template +++ b/Dockerfile.template @@ -95,6 +95,12 @@ RUN set -ex; \ curl -fL -o imagick.tgz 'https://pecl.php.net/get/imagick-3.7.0.tgz'; \ echo '5a364354109029d224bcbb2e82e15b248be9b641227f45e63425c06531792d3e *imagick.tgz' | sha256sum -c -; \ tar --extract --directory /tmp --file imagick.tgz imagick-3.7.0; \ +{{ if env.phpVersion == "8.4" then ( -}} + # Apply patch from this PR https://github.com/Imagick/imagick/pull/690 + sed -i 's/php_strtolower/zend_str_tolower/g' /tmp/imagick-3.7.0/imagick.c; \ + # Fix error "Parameter format has null default, but is not nullable" + sed -i 's/string \$format = null$/string|null \$format = null/' /tmp/imagick-3.7.0/Imagick.stub.php; \ +{{ ) else "" end -}} grep '^//#endif$' /tmp/imagick-3.7.0/Imagick.stub.php; \ test "$(grep -c '^//#endif$' /tmp/imagick-3.7.0/Imagick.stub.php)" = '1'; \ sed -i -e 's!^//#endif$!#endif!' /tmp/imagick-3.7.0/Imagick.stub.php; \ diff --git a/beta/php8.4/apache/Dockerfile b/beta/php8.4/apache/Dockerfile new file mode 100644 index 0000000000..308daa7d17 --- /dev/null +++ b/beta/php8.4/apache/Dockerfile @@ -0,0 +1,182 @@ +# +# NOTE: THIS DOCKERFILE IS GENERATED VIA "apply-templates.sh" +# +# PLEASE DO NOT EDIT IT DIRECTLY. +# + +FROM php:8.4-apache + +# persistent dependencies +RUN set -eux; \ + apt-get update; \ + apt-get install -y --no-install-recommends \ +# Ghostscript is required for rendering PDF previews + ghostscript \ + ; \ + rm -rf /var/lib/apt/lists/* + +# install the PHP extensions we need (https://make.wordpress.org/hosting/handbook/handbook/server-environment/#php-extensions) +RUN set -ex; \ + \ + savedAptMark="$(apt-mark showmanual)"; \ + \ + apt-get update; \ + apt-get install -y --no-install-recommends \ + libavif-dev \ + libfreetype6-dev \ + libicu-dev \ + libjpeg-dev \ + libmagickwand-dev \ + libpng-dev \ + libwebp-dev \ + libzip-dev \ + ; \ + \ + docker-php-ext-configure gd \ + --with-avif \ + --with-freetype \ + --with-jpeg \ + --with-webp \ + ; \ + docker-php-ext-install -j "$(nproc)" \ + bcmath \ + exif \ + gd \ + intl \ + mysqli \ + zip \ + ; \ +# https://pecl.php.net/package/imagick +# https://github.com/Imagick/imagick/commit/5ae2ecf20a1157073bad0170106ad0cf74e01cb6 (causes a lot of build failures, but strangely only intermittent ones 🤔) +# see also https://github.com/Imagick/imagick/pull/641 +# this is "pecl install imagick-3.7.0", but by hand so we can apply a small hack / part of the above commit + curl -fL -o imagick.tgz 'https://pecl.php.net/get/imagick-3.7.0.tgz'; \ + echo '5a364354109029d224bcbb2e82e15b248be9b641227f45e63425c06531792d3e *imagick.tgz' | sha256sum -c -; \ + tar --extract --directory /tmp --file imagick.tgz imagick-3.7.0; \ + # Apply patch from this PR https://github.com/Imagick/imagick/pull/690 + sed -i 's/php_strtolower/zend_str_tolower/g' /tmp/imagick-3.7.0/imagick.c; \ + # Fix error "Parameter format has null default, but is not nullable" + sed -i 's/string \$format = null$/string|null \$format = null/' /tmp/imagick-3.7.0/Imagick.stub.php; \ + grep '^//#endif$' /tmp/imagick-3.7.0/Imagick.stub.php; \ + test "$(grep -c '^//#endif$' /tmp/imagick-3.7.0/Imagick.stub.php)" = '1'; \ + sed -i -e 's!^//#endif$!#endif!' /tmp/imagick-3.7.0/Imagick.stub.php; \ + grep '^//#endif$' /tmp/imagick-3.7.0/Imagick.stub.php && exit 1 || :; \ + docker-php-ext-install /tmp/imagick-3.7.0; \ + rm -rf imagick.tgz /tmp/imagick-3.7.0; \ + \ +# some misbehaving extensions end up outputting to stdout 🙈 (https://github.com/docker-library/wordpress/issues/669#issuecomment-993945967) + out="$(php -r 'exit(0);')"; \ + [ -z "$out" ]; \ + err="$(php -r 'exit(0);' 3>&1 1>&2 2>&3)"; \ + [ -z "$err" ]; \ + \ + extDir="$(php -r 'echo ini_get("extension_dir");')"; \ + [ -d "$extDir" ]; \ +# reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies + apt-mark auto '.*' > /dev/null; \ + apt-mark manual $savedAptMark; \ + ldd "$extDir"/*.so \ + | awk '/=>/ { so = $(NF-1); if (index(so, "/usr/local/") == 1) { next }; gsub("^/(usr/)?", "", so); printf "*%s\n", so }' \ + | sort -u \ + | xargs -r dpkg-query --search \ + | cut -d: -f1 \ + | sort -u \ + | xargs -rt apt-mark manual; \ + \ + apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \ + rm -rf /var/lib/apt/lists/*; \ + \ + ! { ldd "$extDir"/*.so | grep 'not found'; }; \ +# check for output like "PHP Warning: PHP Startup: Unable to load dynamic library 'foo' (tried: ...) + err="$(php --version 3>&1 1>&2 2>&3)"; \ + [ -z "$err" ] + +# set recommended PHP.ini settings +# see https://secure.php.net/manual/en/opcache.installation.php +RUN set -eux; \ + docker-php-ext-enable opcache; \ + { \ + echo 'opcache.memory_consumption=128'; \ + echo 'opcache.interned_strings_buffer=8'; \ + echo 'opcache.max_accelerated_files=4000'; \ + echo 'opcache.revalidate_freq=2'; \ + } > /usr/local/etc/php/conf.d/opcache-recommended.ini +# https://wordpress.org/support/article/editing-wp-config-php/#configure-error-logging +RUN { \ +# https://www.php.net/manual/en/errorfunc.constants.php +# https://github.com/docker-library/wordpress/issues/420#issuecomment-517839670 + echo 'error_reporting = E_ERROR | E_WARNING | E_PARSE | E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_COMPILE_WARNING | E_RECOVERABLE_ERROR'; \ + echo 'display_errors = Off'; \ + echo 'display_startup_errors = Off'; \ + echo 'log_errors = On'; \ + echo 'error_log = /dev/stderr'; \ + echo 'log_errors_max_len = 1024'; \ + echo 'ignore_repeated_errors = On'; \ + echo 'ignore_repeated_source = Off'; \ + echo 'html_errors = Off'; \ + } > /usr/local/etc/php/conf.d/error-logging.ini + +RUN set -eux; \ + a2enmod rewrite expires; \ + \ +# https://httpd.apache.org/docs/2.4/mod/mod_remoteip.html + a2enmod remoteip; \ + { \ + echo 'RemoteIPHeader X-Forwarded-For'; \ +# these IP ranges are reserved for "private" use and should thus *usually* be safe inside Docker + echo 'RemoteIPInternalProxy 10.0.0.0/8'; \ + echo 'RemoteIPInternalProxy 172.16.0.0/12'; \ + echo 'RemoteIPInternalProxy 192.168.0.0/16'; \ + echo 'RemoteIPInternalProxy 169.254.0.0/16'; \ + echo 'RemoteIPInternalProxy 127.0.0.0/8'; \ + } > /etc/apache2/conf-available/remoteip.conf; \ + a2enconf remoteip; \ +# https://github.com/docker-library/wordpress/issues/383#issuecomment-507886512 +# (replace all instances of "%h" with "%a" in LogFormat) + find /etc/apache2 -type f -name '*.conf' -exec sed -ri 's/([[:space:]]*LogFormat[[:space:]]+"[^"]*)%h([^"]*")/\1%a\2/g' '{}' + + +RUN set -eux; \ + version='6.7.1'; \ + sha1='dfb745d4067368bb9a9491f2b6f7e8d52d740fd1'; \ + \ + curl -o wordpress.tar.gz -fL "https://wordpress.org/wordpress-$version.tar.gz"; \ + echo "$sha1 *wordpress.tar.gz" | sha1sum -c -; \ + \ +# upstream tarballs include ./wordpress/ so this gives us /usr/src/wordpress + tar -xzf wordpress.tar.gz -C /usr/src/; \ + rm wordpress.tar.gz; \ + \ +# https://wordpress.org/support/article/htaccess/ + [ ! -e /usr/src/wordpress/.htaccess ]; \ + { \ + echo '# BEGIN WordPress'; \ + echo ''; \ + echo 'RewriteEngine On'; \ + echo 'RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]'; \ + echo 'RewriteBase /'; \ + echo 'RewriteRule ^index\.php$ - [L]'; \ + echo 'RewriteCond %{REQUEST_FILENAME} !-f'; \ + echo 'RewriteCond %{REQUEST_FILENAME} !-d'; \ + echo 'RewriteRule . /index.php [L]'; \ + echo ''; \ + echo '# END WordPress'; \ + } > /usr/src/wordpress/.htaccess; \ + \ + chown -R www-data:www-data /usr/src/wordpress; \ +# pre-create wp-content (and single-level children) for folks who want to bind-mount themes, etc so permissions are pre-created properly instead of root:root +# wp-content/cache: https://github.com/docker-library/wordpress/issues/534#issuecomment-705733507 + mkdir wp-content; \ + for dir in /usr/src/wordpress/wp-content/*/ cache; do \ + dir="$(basename "${dir%/}")"; \ + mkdir "wp-content/$dir"; \ + done; \ + chown -R www-data:www-data wp-content; \ + chmod -R 1777 wp-content + +VOLUME /var/www/html + +COPY --chown=www-data:www-data wp-config-docker.php /usr/src/wordpress/ +COPY docker-entrypoint.sh /usr/local/bin/ + +ENTRYPOINT ["docker-entrypoint.sh"] +CMD ["apache2-foreground"] diff --git a/beta/php8.4/apache/docker-entrypoint.sh b/beta/php8.4/apache/docker-entrypoint.sh new file mode 100755 index 0000000000..1034f1dec1 --- /dev/null +++ b/beta/php8.4/apache/docker-entrypoint.sh @@ -0,0 +1,99 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +if [[ "$1" == apache2* ]] || [ "$1" = 'php-fpm' ]; then + uid="$(id -u)" + gid="$(id -g)" + if [ "$uid" = '0' ]; then + case "$1" in + apache2*) + user="${APACHE_RUN_USER:-www-data}" + group="${APACHE_RUN_GROUP:-www-data}" + + # strip off any '#' symbol ('#1000' is valid syntax for Apache) + pound='#' + user="${user#$pound}" + group="${group#$pound}" + ;; + *) # php-fpm + user='www-data' + group='www-data' + ;; + esac + else + user="$uid" + group="$gid" + fi + + if [ ! -e index.php ] && [ ! -e wp-includes/version.php ]; then + # if the directory exists and WordPress doesn't appear to be installed AND the permissions of it are root:root, let's chown it (likely a Docker-created directory) + if [ "$uid" = '0' ] && [ "$(stat -c '%u:%g' .)" = '0:0' ]; then + chown "$user:$group" . + fi + + echo >&2 "WordPress not found in $PWD - copying now..." + if [ -n "$(find -mindepth 1 -maxdepth 1 -not -name wp-content)" ]; then + echo >&2 "WARNING: $PWD is not empty! (copying anyhow)" + fi + sourceTarArgs=( + --create + --file - + --directory /usr/src/wordpress + --owner "$user" --group "$group" + ) + targetTarArgs=( + --extract + --file - + ) + if [ "$uid" != '0' ]; then + # avoid "tar: .: Cannot utime: Operation not permitted" and "tar: .: Cannot change mode to rwxr-xr-x: Operation not permitted" + targetTarArgs+=( --no-overwrite-dir ) + fi + # loop over "pluggable" content in the source, and if it already exists in the destination, skip it + # https://github.com/docker-library/wordpress/issues/506 ("wp-content" persisted, "akismet" updated, WordPress container restarted/recreated, "akismet" downgraded) + for contentPath in \ + /usr/src/wordpress/.htaccess \ + /usr/src/wordpress/wp-content/*/*/ \ + ; do + contentPath="${contentPath%/}" + [ -e "$contentPath" ] || continue + contentPath="${contentPath#/usr/src/wordpress/}" # "wp-content/plugins/akismet", etc. + if [ -e "$PWD/$contentPath" ]; then + echo >&2 "WARNING: '$PWD/$contentPath' exists! (not copying the WordPress version)" + sourceTarArgs+=( --exclude "./$contentPath" ) + fi + done + tar "${sourceTarArgs[@]}" . | tar "${targetTarArgs[@]}" + echo >&2 "Complete! WordPress has been successfully copied to $PWD" + fi + + wpEnvs=( "${!WORDPRESS_@}" ) + if [ ! -s wp-config.php ] && [ "${#wpEnvs[@]}" -gt 0 ]; then + for wpConfigDocker in \ + wp-config-docker.php \ + /usr/src/wordpress/wp-config-docker.php \ + ; do + if [ -s "$wpConfigDocker" ]; then + echo >&2 "No 'wp-config.php' found in $PWD, but 'WORDPRESS_...' variables supplied; copying '$wpConfigDocker' (${wpEnvs[*]})" + # using "awk" to replace all instances of "put your unique phrase here" with a properly unique string (for AUTH_KEY and friends to have safe defaults if they aren't specified with environment variables) + awk ' + /put your unique phrase here/ { + cmd = "head -c1m /dev/urandom | sha1sum | cut -d\\ -f1" + cmd | getline str + close(cmd) + gsub("put your unique phrase here", str) + } + { print } + ' "$wpConfigDocker" > wp-config.php + if [ "$uid" = '0' ]; then + # attempt to ensure that wp-config.php is owned by the run user + # could be on a filesystem that doesn't allow chown (like some NFS setups) + chown "$user:$group" wp-config.php || true + fi + break + fi + done + fi +fi + +exec "$@" diff --git a/beta/php8.4/apache/wp-config-docker.php b/beta/php8.4/apache/wp-config-docker.php new file mode 100644 index 0000000000..6df9809db8 --- /dev/null +++ b/beta/php8.4/apache/wp-config-docker.php @@ -0,0 +1,139 @@ +&1 1>&2 2>&3)"; \ + [ -z "$err" ]; \ + \ + extDir="$(php -r 'echo ini_get("extension_dir");')"; \ + [ -d "$extDir" ]; \ + runDeps="$( \ + scanelf --needed --nobanner --format '%n#p' --recursive "$extDir" \ + | tr ',' '\n' \ + | sort -u \ + | awk 'system("[ -e /usr/local/lib/" $1 " ]") == 0 { next } { print "so:" $1 }' \ + )"; \ + apk add --no-network --virtual .wordpress-phpexts-rundeps $runDeps; \ + apk del --no-network .build-deps; \ + \ + ! { ldd "$extDir"/*.so | grep 'not found'; }; \ +# check for output like "PHP Warning: PHP Startup: Unable to load dynamic library 'foo' (tried: ...) + err="$(php --version 3>&1 1>&2 2>&3)"; \ + [ -z "$err" ] + +# set recommended PHP.ini settings +# see https://secure.php.net/manual/en/opcache.installation.php +RUN set -eux; \ + docker-php-ext-enable opcache; \ + { \ + echo 'opcache.memory_consumption=128'; \ + echo 'opcache.interned_strings_buffer=8'; \ + echo 'opcache.max_accelerated_files=4000'; \ + echo 'opcache.revalidate_freq=2'; \ + } > /usr/local/etc/php/conf.d/opcache-recommended.ini +# https://wordpress.org/support/article/editing-wp-config-php/#configure-error-logging +RUN { \ +# https://www.php.net/manual/en/errorfunc.constants.php +# https://github.com/docker-library/wordpress/issues/420#issuecomment-517839670 + echo 'error_reporting = E_ERROR | E_WARNING | E_PARSE | E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_COMPILE_WARNING | E_RECOVERABLE_ERROR'; \ + echo 'display_errors = Off'; \ + echo 'display_startup_errors = Off'; \ + echo 'log_errors = On'; \ + echo 'error_log = /dev/stderr'; \ + echo 'log_errors_max_len = 1024'; \ + echo 'ignore_repeated_errors = On'; \ + echo 'ignore_repeated_source = Off'; \ + echo 'html_errors = Off'; \ + } > /usr/local/etc/php/conf.d/error-logging.ini + +RUN set -eux; \ + version='6.7.1'; \ + sha1='dfb745d4067368bb9a9491f2b6f7e8d52d740fd1'; \ + \ + curl -o wordpress.tar.gz -fL "https://wordpress.org/wordpress-$version.tar.gz"; \ + echo "$sha1 *wordpress.tar.gz" | sha1sum -c -; \ + \ +# upstream tarballs include ./wordpress/ so this gives us /usr/src/wordpress + tar -xzf wordpress.tar.gz -C /usr/src/; \ + rm wordpress.tar.gz; \ + \ +# https://wordpress.org/support/article/htaccess/ + [ ! -e /usr/src/wordpress/.htaccess ]; \ + { \ + echo '# BEGIN WordPress'; \ + echo ''; \ + echo 'RewriteEngine On'; \ + echo 'RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]'; \ + echo 'RewriteBase /'; \ + echo 'RewriteRule ^index\.php$ - [L]'; \ + echo 'RewriteCond %{REQUEST_FILENAME} !-f'; \ + echo 'RewriteCond %{REQUEST_FILENAME} !-d'; \ + echo 'RewriteRule . /index.php [L]'; \ + echo ''; \ + echo '# END WordPress'; \ + } > /usr/src/wordpress/.htaccess; \ + \ + chown -R www-data:www-data /usr/src/wordpress; \ +# pre-create wp-content (and single-level children) for folks who want to bind-mount themes, etc so permissions are pre-created properly instead of root:root +# wp-content/cache: https://github.com/docker-library/wordpress/issues/534#issuecomment-705733507 + mkdir wp-content; \ + for dir in /usr/src/wordpress/wp-content/*/ cache; do \ + dir="$(basename "${dir%/}")"; \ + mkdir "wp-content/$dir"; \ + done; \ + chown -R www-data:www-data wp-content; \ + chmod -R 1777 wp-content + +VOLUME /var/www/html + +COPY --chown=www-data:www-data wp-config-docker.php /usr/src/wordpress/ +COPY docker-entrypoint.sh /usr/local/bin/ + +ENTRYPOINT ["docker-entrypoint.sh"] +CMD ["php-fpm"] diff --git a/beta/php8.4/fpm-alpine/docker-entrypoint.sh b/beta/php8.4/fpm-alpine/docker-entrypoint.sh new file mode 100755 index 0000000000..1034f1dec1 --- /dev/null +++ b/beta/php8.4/fpm-alpine/docker-entrypoint.sh @@ -0,0 +1,99 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +if [[ "$1" == apache2* ]] || [ "$1" = 'php-fpm' ]; then + uid="$(id -u)" + gid="$(id -g)" + if [ "$uid" = '0' ]; then + case "$1" in + apache2*) + user="${APACHE_RUN_USER:-www-data}" + group="${APACHE_RUN_GROUP:-www-data}" + + # strip off any '#' symbol ('#1000' is valid syntax for Apache) + pound='#' + user="${user#$pound}" + group="${group#$pound}" + ;; + *) # php-fpm + user='www-data' + group='www-data' + ;; + esac + else + user="$uid" + group="$gid" + fi + + if [ ! -e index.php ] && [ ! -e wp-includes/version.php ]; then + # if the directory exists and WordPress doesn't appear to be installed AND the permissions of it are root:root, let's chown it (likely a Docker-created directory) + if [ "$uid" = '0' ] && [ "$(stat -c '%u:%g' .)" = '0:0' ]; then + chown "$user:$group" . + fi + + echo >&2 "WordPress not found in $PWD - copying now..." + if [ -n "$(find -mindepth 1 -maxdepth 1 -not -name wp-content)" ]; then + echo >&2 "WARNING: $PWD is not empty! (copying anyhow)" + fi + sourceTarArgs=( + --create + --file - + --directory /usr/src/wordpress + --owner "$user" --group "$group" + ) + targetTarArgs=( + --extract + --file - + ) + if [ "$uid" != '0' ]; then + # avoid "tar: .: Cannot utime: Operation not permitted" and "tar: .: Cannot change mode to rwxr-xr-x: Operation not permitted" + targetTarArgs+=( --no-overwrite-dir ) + fi + # loop over "pluggable" content in the source, and if it already exists in the destination, skip it + # https://github.com/docker-library/wordpress/issues/506 ("wp-content" persisted, "akismet" updated, WordPress container restarted/recreated, "akismet" downgraded) + for contentPath in \ + /usr/src/wordpress/.htaccess \ + /usr/src/wordpress/wp-content/*/*/ \ + ; do + contentPath="${contentPath%/}" + [ -e "$contentPath" ] || continue + contentPath="${contentPath#/usr/src/wordpress/}" # "wp-content/plugins/akismet", etc. + if [ -e "$PWD/$contentPath" ]; then + echo >&2 "WARNING: '$PWD/$contentPath' exists! (not copying the WordPress version)" + sourceTarArgs+=( --exclude "./$contentPath" ) + fi + done + tar "${sourceTarArgs[@]}" . | tar "${targetTarArgs[@]}" + echo >&2 "Complete! WordPress has been successfully copied to $PWD" + fi + + wpEnvs=( "${!WORDPRESS_@}" ) + if [ ! -s wp-config.php ] && [ "${#wpEnvs[@]}" -gt 0 ]; then + for wpConfigDocker in \ + wp-config-docker.php \ + /usr/src/wordpress/wp-config-docker.php \ + ; do + if [ -s "$wpConfigDocker" ]; then + echo >&2 "No 'wp-config.php' found in $PWD, but 'WORDPRESS_...' variables supplied; copying '$wpConfigDocker' (${wpEnvs[*]})" + # using "awk" to replace all instances of "put your unique phrase here" with a properly unique string (for AUTH_KEY and friends to have safe defaults if they aren't specified with environment variables) + awk ' + /put your unique phrase here/ { + cmd = "head -c1m /dev/urandom | sha1sum | cut -d\\ -f1" + cmd | getline str + close(cmd) + gsub("put your unique phrase here", str) + } + { print } + ' "$wpConfigDocker" > wp-config.php + if [ "$uid" = '0' ]; then + # attempt to ensure that wp-config.php is owned by the run user + # could be on a filesystem that doesn't allow chown (like some NFS setups) + chown "$user:$group" wp-config.php || true + fi + break + fi + done + fi +fi + +exec "$@" diff --git a/beta/php8.4/fpm-alpine/wp-config-docker.php b/beta/php8.4/fpm-alpine/wp-config-docker.php new file mode 100644 index 0000000000..6df9809db8 --- /dev/null +++ b/beta/php8.4/fpm-alpine/wp-config-docker.php @@ -0,0 +1,139 @@ +&1 1>&2 2>&3)"; \ + [ -z "$err" ]; \ + \ + extDir="$(php -r 'echo ini_get("extension_dir");')"; \ + [ -d "$extDir" ]; \ +# reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies + apt-mark auto '.*' > /dev/null; \ + apt-mark manual $savedAptMark; \ + ldd "$extDir"/*.so \ + | awk '/=>/ { so = $(NF-1); if (index(so, "/usr/local/") == 1) { next }; gsub("^/(usr/)?", "", so); printf "*%s\n", so }' \ + | sort -u \ + | xargs -r dpkg-query --search \ + | cut -d: -f1 \ + | sort -u \ + | xargs -rt apt-mark manual; \ + \ + apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \ + rm -rf /var/lib/apt/lists/*; \ + \ + ! { ldd "$extDir"/*.so | grep 'not found'; }; \ +# check for output like "PHP Warning: PHP Startup: Unable to load dynamic library 'foo' (tried: ...) + err="$(php --version 3>&1 1>&2 2>&3)"; \ + [ -z "$err" ] + +# set recommended PHP.ini settings +# see https://secure.php.net/manual/en/opcache.installation.php +RUN set -eux; \ + docker-php-ext-enable opcache; \ + { \ + echo 'opcache.memory_consumption=128'; \ + echo 'opcache.interned_strings_buffer=8'; \ + echo 'opcache.max_accelerated_files=4000'; \ + echo 'opcache.revalidate_freq=2'; \ + } > /usr/local/etc/php/conf.d/opcache-recommended.ini +# https://wordpress.org/support/article/editing-wp-config-php/#configure-error-logging +RUN { \ +# https://www.php.net/manual/en/errorfunc.constants.php +# https://github.com/docker-library/wordpress/issues/420#issuecomment-517839670 + echo 'error_reporting = E_ERROR | E_WARNING | E_PARSE | E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_COMPILE_WARNING | E_RECOVERABLE_ERROR'; \ + echo 'display_errors = Off'; \ + echo 'display_startup_errors = Off'; \ + echo 'log_errors = On'; \ + echo 'error_log = /dev/stderr'; \ + echo 'log_errors_max_len = 1024'; \ + echo 'ignore_repeated_errors = On'; \ + echo 'ignore_repeated_source = Off'; \ + echo 'html_errors = Off'; \ + } > /usr/local/etc/php/conf.d/error-logging.ini + +RUN set -eux; \ + version='6.7.1'; \ + sha1='dfb745d4067368bb9a9491f2b6f7e8d52d740fd1'; \ + \ + curl -o wordpress.tar.gz -fL "https://wordpress.org/wordpress-$version.tar.gz"; \ + echo "$sha1 *wordpress.tar.gz" | sha1sum -c -; \ + \ +# upstream tarballs include ./wordpress/ so this gives us /usr/src/wordpress + tar -xzf wordpress.tar.gz -C /usr/src/; \ + rm wordpress.tar.gz; \ + \ +# https://wordpress.org/support/article/htaccess/ + [ ! -e /usr/src/wordpress/.htaccess ]; \ + { \ + echo '# BEGIN WordPress'; \ + echo ''; \ + echo 'RewriteEngine On'; \ + echo 'RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]'; \ + echo 'RewriteBase /'; \ + echo 'RewriteRule ^index\.php$ - [L]'; \ + echo 'RewriteCond %{REQUEST_FILENAME} !-f'; \ + echo 'RewriteCond %{REQUEST_FILENAME} !-d'; \ + echo 'RewriteRule . /index.php [L]'; \ + echo ''; \ + echo '# END WordPress'; \ + } > /usr/src/wordpress/.htaccess; \ + \ + chown -R www-data:www-data /usr/src/wordpress; \ +# pre-create wp-content (and single-level children) for folks who want to bind-mount themes, etc so permissions are pre-created properly instead of root:root +# wp-content/cache: https://github.com/docker-library/wordpress/issues/534#issuecomment-705733507 + mkdir wp-content; \ + for dir in /usr/src/wordpress/wp-content/*/ cache; do \ + dir="$(basename "${dir%/}")"; \ + mkdir "wp-content/$dir"; \ + done; \ + chown -R www-data:www-data wp-content; \ + chmod -R 1777 wp-content + +VOLUME /var/www/html + +COPY --chown=www-data:www-data wp-config-docker.php /usr/src/wordpress/ +COPY docker-entrypoint.sh /usr/local/bin/ + +ENTRYPOINT ["docker-entrypoint.sh"] +CMD ["php-fpm"] diff --git a/beta/php8.4/fpm/docker-entrypoint.sh b/beta/php8.4/fpm/docker-entrypoint.sh new file mode 100755 index 0000000000..1034f1dec1 --- /dev/null +++ b/beta/php8.4/fpm/docker-entrypoint.sh @@ -0,0 +1,99 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +if [[ "$1" == apache2* ]] || [ "$1" = 'php-fpm' ]; then + uid="$(id -u)" + gid="$(id -g)" + if [ "$uid" = '0' ]; then + case "$1" in + apache2*) + user="${APACHE_RUN_USER:-www-data}" + group="${APACHE_RUN_GROUP:-www-data}" + + # strip off any '#' symbol ('#1000' is valid syntax for Apache) + pound='#' + user="${user#$pound}" + group="${group#$pound}" + ;; + *) # php-fpm + user='www-data' + group='www-data' + ;; + esac + else + user="$uid" + group="$gid" + fi + + if [ ! -e index.php ] && [ ! -e wp-includes/version.php ]; then + # if the directory exists and WordPress doesn't appear to be installed AND the permissions of it are root:root, let's chown it (likely a Docker-created directory) + if [ "$uid" = '0' ] && [ "$(stat -c '%u:%g' .)" = '0:0' ]; then + chown "$user:$group" . + fi + + echo >&2 "WordPress not found in $PWD - copying now..." + if [ -n "$(find -mindepth 1 -maxdepth 1 -not -name wp-content)" ]; then + echo >&2 "WARNING: $PWD is not empty! (copying anyhow)" + fi + sourceTarArgs=( + --create + --file - + --directory /usr/src/wordpress + --owner "$user" --group "$group" + ) + targetTarArgs=( + --extract + --file - + ) + if [ "$uid" != '0' ]; then + # avoid "tar: .: Cannot utime: Operation not permitted" and "tar: .: Cannot change mode to rwxr-xr-x: Operation not permitted" + targetTarArgs+=( --no-overwrite-dir ) + fi + # loop over "pluggable" content in the source, and if it already exists in the destination, skip it + # https://github.com/docker-library/wordpress/issues/506 ("wp-content" persisted, "akismet" updated, WordPress container restarted/recreated, "akismet" downgraded) + for contentPath in \ + /usr/src/wordpress/.htaccess \ + /usr/src/wordpress/wp-content/*/*/ \ + ; do + contentPath="${contentPath%/}" + [ -e "$contentPath" ] || continue + contentPath="${contentPath#/usr/src/wordpress/}" # "wp-content/plugins/akismet", etc. + if [ -e "$PWD/$contentPath" ]; then + echo >&2 "WARNING: '$PWD/$contentPath' exists! (not copying the WordPress version)" + sourceTarArgs+=( --exclude "./$contentPath" ) + fi + done + tar "${sourceTarArgs[@]}" . | tar "${targetTarArgs[@]}" + echo >&2 "Complete! WordPress has been successfully copied to $PWD" + fi + + wpEnvs=( "${!WORDPRESS_@}" ) + if [ ! -s wp-config.php ] && [ "${#wpEnvs[@]}" -gt 0 ]; then + for wpConfigDocker in \ + wp-config-docker.php \ + /usr/src/wordpress/wp-config-docker.php \ + ; do + if [ -s "$wpConfigDocker" ]; then + echo >&2 "No 'wp-config.php' found in $PWD, but 'WORDPRESS_...' variables supplied; copying '$wpConfigDocker' (${wpEnvs[*]})" + # using "awk" to replace all instances of "put your unique phrase here" with a properly unique string (for AUTH_KEY and friends to have safe defaults if they aren't specified with environment variables) + awk ' + /put your unique phrase here/ { + cmd = "head -c1m /dev/urandom | sha1sum | cut -d\\ -f1" + cmd | getline str + close(cmd) + gsub("put your unique phrase here", str) + } + { print } + ' "$wpConfigDocker" > wp-config.php + if [ "$uid" = '0' ]; then + # attempt to ensure that wp-config.php is owned by the run user + # could be on a filesystem that doesn't allow chown (like some NFS setups) + chown "$user:$group" wp-config.php || true + fi + break + fi + done + fi +fi + +exec "$@" diff --git a/beta/php8.4/fpm/wp-config-docker.php b/beta/php8.4/fpm/wp-config-docker.php new file mode 100644 index 0000000000..6df9809db8 --- /dev/null +++ b/beta/php8.4/fpm/wp-config-docker.php @@ -0,0 +1,139 @@ +&1 1>&2 2>&3)"; \ + [ -z "$err" ]; \ + \ + extDir="$(php -r 'echo ini_get("extension_dir");')"; \ + [ -d "$extDir" ]; \ + runDeps="$( \ + scanelf --needed --nobanner --format '%n#p' --recursive "$extDir" \ + | tr ',' '\n' \ + | sort -u \ + | awk 'system("[ -e /usr/local/lib/" $1 " ]") == 0 { next } { print "so:" $1 }' \ + )"; \ + apk add --no-network --virtual .wordpress-phpexts-rundeps $runDeps; \ + apk del --no-network .build-deps; \ + \ + ! { ldd "$extDir"/*.so | grep 'not found'; }; \ +# check for output like "PHP Warning: PHP Startup: Unable to load dynamic library 'foo' (tried: ...) + err="$(php --version 3>&1 1>&2 2>&3)"; \ + [ -z "$err" ] + +# set recommended PHP.ini settings +# excluding opcache due https://github.com/docker-library/wordpress/issues/407 +# https://wordpress.org/support/article/editing-wp-config-php/#configure-error-logging +RUN { \ +# https://www.php.net/manual/en/errorfunc.constants.php +# https://github.com/docker-library/wordpress/issues/420#issuecomment-517839670 + echo 'error_reporting = E_ERROR | E_WARNING | E_PARSE | E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_COMPILE_WARNING | E_RECOVERABLE_ERROR'; \ + echo 'display_errors = Off'; \ + echo 'display_startup_errors = Off'; \ + echo 'log_errors = On'; \ + echo 'error_log = /dev/stderr'; \ + echo 'log_errors_max_len = 1024'; \ + echo 'ignore_repeated_errors = On'; \ + echo 'ignore_repeated_source = Off'; \ + echo 'html_errors = Off'; \ + } > /usr/local/etc/php/conf.d/error-logging.ini + +# https://make.wordpress.org/cli/2018/05/31/gpg-signature-change/ +# pub rsa2048 2018-05-31 [SC] +# 63AF 7AA1 5067 C056 16FD DD88 A3A2 E8F2 26F0 BC06 +# uid [ unknown] WP-CLI Releases +# sub rsa2048 2018-05-31 [E] +ENV WORDPRESS_CLI_GPG_KEY 63AF7AA15067C05616FDDD88A3A2E8F226F0BC06 + +ENV WORDPRESS_CLI_VERSION 2.11.0 +ENV WORDPRESS_CLI_SHA512 adb12146bab8d829621efed41124dcd0012f9027f47e0228be7080296167566070e4a026a09c3989907840b21de94b7a35f3bfbd5f827c12f27c5803546d1bba + +RUN set -ex; \ + \ + apk add --no-cache --virtual .fetch-deps \ + gnupg \ + ; \ + \ + curl -o /usr/local/bin/wp.gpg -fL "https://github.com/wp-cli/wp-cli/releases/download/v${WORDPRESS_CLI_VERSION}/wp-cli-${WORDPRESS_CLI_VERSION}.phar.gpg"; \ + \ + GNUPGHOME="$(mktemp -d)"; export GNUPGHOME; \ + gpg --batch --keyserver keyserver.ubuntu.com --recv-keys "$WORDPRESS_CLI_GPG_KEY"; \ + gpg --batch --decrypt --output /usr/local/bin/wp /usr/local/bin/wp.gpg; \ + gpgconf --kill all; \ + rm -rf "$GNUPGHOME" /usr/local/bin/wp.gpg; unset GNUPGHOME; \ + \ + echo "$WORDPRESS_CLI_SHA512 */usr/local/bin/wp" | sha512sum -c -; \ + chmod +x /usr/local/bin/wp; \ + \ + apk del --no-network .fetch-deps; \ + \ + wp --allow-root --version + +VOLUME /var/www/html + +COPY docker-entrypoint.sh /usr/local/bin/ + +ENTRYPOINT ["docker-entrypoint.sh"] +USER www-data +CMD ["wp", "shell"] diff --git a/cli/php8.4/alpine/docker-entrypoint.sh b/cli/php8.4/alpine/docker-entrypoint.sh new file mode 100755 index 0000000000..b0f77dbb63 --- /dev/null +++ b/cli/php8.4/alpine/docker-entrypoint.sh @@ -0,0 +1,11 @@ +#!/bin/sh +set -euo pipefail + +# first arg is `-f` or `--some-option` +# or if our command is a valid wp-cli subcommand, let's invoke it through wp-cli instead +# (this allows for "docker run wordpress:cli help", etc) +if [ "${1#-}" != "$1" ] || wp help "$1" > /dev/null 2>&1; then + set -- wp "$@" +fi + +exec "$@" diff --git a/latest/php8.4/apache/Dockerfile b/latest/php8.4/apache/Dockerfile new file mode 100644 index 0000000000..308daa7d17 --- /dev/null +++ b/latest/php8.4/apache/Dockerfile @@ -0,0 +1,182 @@ +# +# NOTE: THIS DOCKERFILE IS GENERATED VIA "apply-templates.sh" +# +# PLEASE DO NOT EDIT IT DIRECTLY. +# + +FROM php:8.4-apache + +# persistent dependencies +RUN set -eux; \ + apt-get update; \ + apt-get install -y --no-install-recommends \ +# Ghostscript is required for rendering PDF previews + ghostscript \ + ; \ + rm -rf /var/lib/apt/lists/* + +# install the PHP extensions we need (https://make.wordpress.org/hosting/handbook/handbook/server-environment/#php-extensions) +RUN set -ex; \ + \ + savedAptMark="$(apt-mark showmanual)"; \ + \ + apt-get update; \ + apt-get install -y --no-install-recommends \ + libavif-dev \ + libfreetype6-dev \ + libicu-dev \ + libjpeg-dev \ + libmagickwand-dev \ + libpng-dev \ + libwebp-dev \ + libzip-dev \ + ; \ + \ + docker-php-ext-configure gd \ + --with-avif \ + --with-freetype \ + --with-jpeg \ + --with-webp \ + ; \ + docker-php-ext-install -j "$(nproc)" \ + bcmath \ + exif \ + gd \ + intl \ + mysqli \ + zip \ + ; \ +# https://pecl.php.net/package/imagick +# https://github.com/Imagick/imagick/commit/5ae2ecf20a1157073bad0170106ad0cf74e01cb6 (causes a lot of build failures, but strangely only intermittent ones 🤔) +# see also https://github.com/Imagick/imagick/pull/641 +# this is "pecl install imagick-3.7.0", but by hand so we can apply a small hack / part of the above commit + curl -fL -o imagick.tgz 'https://pecl.php.net/get/imagick-3.7.0.tgz'; \ + echo '5a364354109029d224bcbb2e82e15b248be9b641227f45e63425c06531792d3e *imagick.tgz' | sha256sum -c -; \ + tar --extract --directory /tmp --file imagick.tgz imagick-3.7.0; \ + # Apply patch from this PR https://github.com/Imagick/imagick/pull/690 + sed -i 's/php_strtolower/zend_str_tolower/g' /tmp/imagick-3.7.0/imagick.c; \ + # Fix error "Parameter format has null default, but is not nullable" + sed -i 's/string \$format = null$/string|null \$format = null/' /tmp/imagick-3.7.0/Imagick.stub.php; \ + grep '^//#endif$' /tmp/imagick-3.7.0/Imagick.stub.php; \ + test "$(grep -c '^//#endif$' /tmp/imagick-3.7.0/Imagick.stub.php)" = '1'; \ + sed -i -e 's!^//#endif$!#endif!' /tmp/imagick-3.7.0/Imagick.stub.php; \ + grep '^//#endif$' /tmp/imagick-3.7.0/Imagick.stub.php && exit 1 || :; \ + docker-php-ext-install /tmp/imagick-3.7.0; \ + rm -rf imagick.tgz /tmp/imagick-3.7.0; \ + \ +# some misbehaving extensions end up outputting to stdout 🙈 (https://github.com/docker-library/wordpress/issues/669#issuecomment-993945967) + out="$(php -r 'exit(0);')"; \ + [ -z "$out" ]; \ + err="$(php -r 'exit(0);' 3>&1 1>&2 2>&3)"; \ + [ -z "$err" ]; \ + \ + extDir="$(php -r 'echo ini_get("extension_dir");')"; \ + [ -d "$extDir" ]; \ +# reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies + apt-mark auto '.*' > /dev/null; \ + apt-mark manual $savedAptMark; \ + ldd "$extDir"/*.so \ + | awk '/=>/ { so = $(NF-1); if (index(so, "/usr/local/") == 1) { next }; gsub("^/(usr/)?", "", so); printf "*%s\n", so }' \ + | sort -u \ + | xargs -r dpkg-query --search \ + | cut -d: -f1 \ + | sort -u \ + | xargs -rt apt-mark manual; \ + \ + apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \ + rm -rf /var/lib/apt/lists/*; \ + \ + ! { ldd "$extDir"/*.so | grep 'not found'; }; \ +# check for output like "PHP Warning: PHP Startup: Unable to load dynamic library 'foo' (tried: ...) + err="$(php --version 3>&1 1>&2 2>&3)"; \ + [ -z "$err" ] + +# set recommended PHP.ini settings +# see https://secure.php.net/manual/en/opcache.installation.php +RUN set -eux; \ + docker-php-ext-enable opcache; \ + { \ + echo 'opcache.memory_consumption=128'; \ + echo 'opcache.interned_strings_buffer=8'; \ + echo 'opcache.max_accelerated_files=4000'; \ + echo 'opcache.revalidate_freq=2'; \ + } > /usr/local/etc/php/conf.d/opcache-recommended.ini +# https://wordpress.org/support/article/editing-wp-config-php/#configure-error-logging +RUN { \ +# https://www.php.net/manual/en/errorfunc.constants.php +# https://github.com/docker-library/wordpress/issues/420#issuecomment-517839670 + echo 'error_reporting = E_ERROR | E_WARNING | E_PARSE | E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_COMPILE_WARNING | E_RECOVERABLE_ERROR'; \ + echo 'display_errors = Off'; \ + echo 'display_startup_errors = Off'; \ + echo 'log_errors = On'; \ + echo 'error_log = /dev/stderr'; \ + echo 'log_errors_max_len = 1024'; \ + echo 'ignore_repeated_errors = On'; \ + echo 'ignore_repeated_source = Off'; \ + echo 'html_errors = Off'; \ + } > /usr/local/etc/php/conf.d/error-logging.ini + +RUN set -eux; \ + a2enmod rewrite expires; \ + \ +# https://httpd.apache.org/docs/2.4/mod/mod_remoteip.html + a2enmod remoteip; \ + { \ + echo 'RemoteIPHeader X-Forwarded-For'; \ +# these IP ranges are reserved for "private" use and should thus *usually* be safe inside Docker + echo 'RemoteIPInternalProxy 10.0.0.0/8'; \ + echo 'RemoteIPInternalProxy 172.16.0.0/12'; \ + echo 'RemoteIPInternalProxy 192.168.0.0/16'; \ + echo 'RemoteIPInternalProxy 169.254.0.0/16'; \ + echo 'RemoteIPInternalProxy 127.0.0.0/8'; \ + } > /etc/apache2/conf-available/remoteip.conf; \ + a2enconf remoteip; \ +# https://github.com/docker-library/wordpress/issues/383#issuecomment-507886512 +# (replace all instances of "%h" with "%a" in LogFormat) + find /etc/apache2 -type f -name '*.conf' -exec sed -ri 's/([[:space:]]*LogFormat[[:space:]]+"[^"]*)%h([^"]*")/\1%a\2/g' '{}' + + +RUN set -eux; \ + version='6.7.1'; \ + sha1='dfb745d4067368bb9a9491f2b6f7e8d52d740fd1'; \ + \ + curl -o wordpress.tar.gz -fL "https://wordpress.org/wordpress-$version.tar.gz"; \ + echo "$sha1 *wordpress.tar.gz" | sha1sum -c -; \ + \ +# upstream tarballs include ./wordpress/ so this gives us /usr/src/wordpress + tar -xzf wordpress.tar.gz -C /usr/src/; \ + rm wordpress.tar.gz; \ + \ +# https://wordpress.org/support/article/htaccess/ + [ ! -e /usr/src/wordpress/.htaccess ]; \ + { \ + echo '# BEGIN WordPress'; \ + echo ''; \ + echo 'RewriteEngine On'; \ + echo 'RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]'; \ + echo 'RewriteBase /'; \ + echo 'RewriteRule ^index\.php$ - [L]'; \ + echo 'RewriteCond %{REQUEST_FILENAME} !-f'; \ + echo 'RewriteCond %{REQUEST_FILENAME} !-d'; \ + echo 'RewriteRule . /index.php [L]'; \ + echo ''; \ + echo '# END WordPress'; \ + } > /usr/src/wordpress/.htaccess; \ + \ + chown -R www-data:www-data /usr/src/wordpress; \ +# pre-create wp-content (and single-level children) for folks who want to bind-mount themes, etc so permissions are pre-created properly instead of root:root +# wp-content/cache: https://github.com/docker-library/wordpress/issues/534#issuecomment-705733507 + mkdir wp-content; \ + for dir in /usr/src/wordpress/wp-content/*/ cache; do \ + dir="$(basename "${dir%/}")"; \ + mkdir "wp-content/$dir"; \ + done; \ + chown -R www-data:www-data wp-content; \ + chmod -R 1777 wp-content + +VOLUME /var/www/html + +COPY --chown=www-data:www-data wp-config-docker.php /usr/src/wordpress/ +COPY docker-entrypoint.sh /usr/local/bin/ + +ENTRYPOINT ["docker-entrypoint.sh"] +CMD ["apache2-foreground"] diff --git a/latest/php8.4/apache/docker-entrypoint.sh b/latest/php8.4/apache/docker-entrypoint.sh new file mode 100755 index 0000000000..1034f1dec1 --- /dev/null +++ b/latest/php8.4/apache/docker-entrypoint.sh @@ -0,0 +1,99 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +if [[ "$1" == apache2* ]] || [ "$1" = 'php-fpm' ]; then + uid="$(id -u)" + gid="$(id -g)" + if [ "$uid" = '0' ]; then + case "$1" in + apache2*) + user="${APACHE_RUN_USER:-www-data}" + group="${APACHE_RUN_GROUP:-www-data}" + + # strip off any '#' symbol ('#1000' is valid syntax for Apache) + pound='#' + user="${user#$pound}" + group="${group#$pound}" + ;; + *) # php-fpm + user='www-data' + group='www-data' + ;; + esac + else + user="$uid" + group="$gid" + fi + + if [ ! -e index.php ] && [ ! -e wp-includes/version.php ]; then + # if the directory exists and WordPress doesn't appear to be installed AND the permissions of it are root:root, let's chown it (likely a Docker-created directory) + if [ "$uid" = '0' ] && [ "$(stat -c '%u:%g' .)" = '0:0' ]; then + chown "$user:$group" . + fi + + echo >&2 "WordPress not found in $PWD - copying now..." + if [ -n "$(find -mindepth 1 -maxdepth 1 -not -name wp-content)" ]; then + echo >&2 "WARNING: $PWD is not empty! (copying anyhow)" + fi + sourceTarArgs=( + --create + --file - + --directory /usr/src/wordpress + --owner "$user" --group "$group" + ) + targetTarArgs=( + --extract + --file - + ) + if [ "$uid" != '0' ]; then + # avoid "tar: .: Cannot utime: Operation not permitted" and "tar: .: Cannot change mode to rwxr-xr-x: Operation not permitted" + targetTarArgs+=( --no-overwrite-dir ) + fi + # loop over "pluggable" content in the source, and if it already exists in the destination, skip it + # https://github.com/docker-library/wordpress/issues/506 ("wp-content" persisted, "akismet" updated, WordPress container restarted/recreated, "akismet" downgraded) + for contentPath in \ + /usr/src/wordpress/.htaccess \ + /usr/src/wordpress/wp-content/*/*/ \ + ; do + contentPath="${contentPath%/}" + [ -e "$contentPath" ] || continue + contentPath="${contentPath#/usr/src/wordpress/}" # "wp-content/plugins/akismet", etc. + if [ -e "$PWD/$contentPath" ]; then + echo >&2 "WARNING: '$PWD/$contentPath' exists! (not copying the WordPress version)" + sourceTarArgs+=( --exclude "./$contentPath" ) + fi + done + tar "${sourceTarArgs[@]}" . | tar "${targetTarArgs[@]}" + echo >&2 "Complete! WordPress has been successfully copied to $PWD" + fi + + wpEnvs=( "${!WORDPRESS_@}" ) + if [ ! -s wp-config.php ] && [ "${#wpEnvs[@]}" -gt 0 ]; then + for wpConfigDocker in \ + wp-config-docker.php \ + /usr/src/wordpress/wp-config-docker.php \ + ; do + if [ -s "$wpConfigDocker" ]; then + echo >&2 "No 'wp-config.php' found in $PWD, but 'WORDPRESS_...' variables supplied; copying '$wpConfigDocker' (${wpEnvs[*]})" + # using "awk" to replace all instances of "put your unique phrase here" with a properly unique string (for AUTH_KEY and friends to have safe defaults if they aren't specified with environment variables) + awk ' + /put your unique phrase here/ { + cmd = "head -c1m /dev/urandom | sha1sum | cut -d\\ -f1" + cmd | getline str + close(cmd) + gsub("put your unique phrase here", str) + } + { print } + ' "$wpConfigDocker" > wp-config.php + if [ "$uid" = '0' ]; then + # attempt to ensure that wp-config.php is owned by the run user + # could be on a filesystem that doesn't allow chown (like some NFS setups) + chown "$user:$group" wp-config.php || true + fi + break + fi + done + fi +fi + +exec "$@" diff --git a/latest/php8.4/apache/wp-config-docker.php b/latest/php8.4/apache/wp-config-docker.php new file mode 100644 index 0000000000..6df9809db8 --- /dev/null +++ b/latest/php8.4/apache/wp-config-docker.php @@ -0,0 +1,139 @@ +&1 1>&2 2>&3)"; \ + [ -z "$err" ]; \ + \ + extDir="$(php -r 'echo ini_get("extension_dir");')"; \ + [ -d "$extDir" ]; \ + runDeps="$( \ + scanelf --needed --nobanner --format '%n#p' --recursive "$extDir" \ + | tr ',' '\n' \ + | sort -u \ + | awk 'system("[ -e /usr/local/lib/" $1 " ]") == 0 { next } { print "so:" $1 }' \ + )"; \ + apk add --no-network --virtual .wordpress-phpexts-rundeps $runDeps; \ + apk del --no-network .build-deps; \ + \ + ! { ldd "$extDir"/*.so | grep 'not found'; }; \ +# check for output like "PHP Warning: PHP Startup: Unable to load dynamic library 'foo' (tried: ...) + err="$(php --version 3>&1 1>&2 2>&3)"; \ + [ -z "$err" ] + +# set recommended PHP.ini settings +# see https://secure.php.net/manual/en/opcache.installation.php +RUN set -eux; \ + docker-php-ext-enable opcache; \ + { \ + echo 'opcache.memory_consumption=128'; \ + echo 'opcache.interned_strings_buffer=8'; \ + echo 'opcache.max_accelerated_files=4000'; \ + echo 'opcache.revalidate_freq=2'; \ + } > /usr/local/etc/php/conf.d/opcache-recommended.ini +# https://wordpress.org/support/article/editing-wp-config-php/#configure-error-logging +RUN { \ +# https://www.php.net/manual/en/errorfunc.constants.php +# https://github.com/docker-library/wordpress/issues/420#issuecomment-517839670 + echo 'error_reporting = E_ERROR | E_WARNING | E_PARSE | E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_COMPILE_WARNING | E_RECOVERABLE_ERROR'; \ + echo 'display_errors = Off'; \ + echo 'display_startup_errors = Off'; \ + echo 'log_errors = On'; \ + echo 'error_log = /dev/stderr'; \ + echo 'log_errors_max_len = 1024'; \ + echo 'ignore_repeated_errors = On'; \ + echo 'ignore_repeated_source = Off'; \ + echo 'html_errors = Off'; \ + } > /usr/local/etc/php/conf.d/error-logging.ini + +RUN set -eux; \ + version='6.7.1'; \ + sha1='dfb745d4067368bb9a9491f2b6f7e8d52d740fd1'; \ + \ + curl -o wordpress.tar.gz -fL "https://wordpress.org/wordpress-$version.tar.gz"; \ + echo "$sha1 *wordpress.tar.gz" | sha1sum -c -; \ + \ +# upstream tarballs include ./wordpress/ so this gives us /usr/src/wordpress + tar -xzf wordpress.tar.gz -C /usr/src/; \ + rm wordpress.tar.gz; \ + \ +# https://wordpress.org/support/article/htaccess/ + [ ! -e /usr/src/wordpress/.htaccess ]; \ + { \ + echo '# BEGIN WordPress'; \ + echo ''; \ + echo 'RewriteEngine On'; \ + echo 'RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]'; \ + echo 'RewriteBase /'; \ + echo 'RewriteRule ^index\.php$ - [L]'; \ + echo 'RewriteCond %{REQUEST_FILENAME} !-f'; \ + echo 'RewriteCond %{REQUEST_FILENAME} !-d'; \ + echo 'RewriteRule . /index.php [L]'; \ + echo ''; \ + echo '# END WordPress'; \ + } > /usr/src/wordpress/.htaccess; \ + \ + chown -R www-data:www-data /usr/src/wordpress; \ +# pre-create wp-content (and single-level children) for folks who want to bind-mount themes, etc so permissions are pre-created properly instead of root:root +# wp-content/cache: https://github.com/docker-library/wordpress/issues/534#issuecomment-705733507 + mkdir wp-content; \ + for dir in /usr/src/wordpress/wp-content/*/ cache; do \ + dir="$(basename "${dir%/}")"; \ + mkdir "wp-content/$dir"; \ + done; \ + chown -R www-data:www-data wp-content; \ + chmod -R 1777 wp-content + +VOLUME /var/www/html + +COPY --chown=www-data:www-data wp-config-docker.php /usr/src/wordpress/ +COPY docker-entrypoint.sh /usr/local/bin/ + +ENTRYPOINT ["docker-entrypoint.sh"] +CMD ["php-fpm"] diff --git a/latest/php8.4/fpm-alpine/docker-entrypoint.sh b/latest/php8.4/fpm-alpine/docker-entrypoint.sh new file mode 100755 index 0000000000..1034f1dec1 --- /dev/null +++ b/latest/php8.4/fpm-alpine/docker-entrypoint.sh @@ -0,0 +1,99 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +if [[ "$1" == apache2* ]] || [ "$1" = 'php-fpm' ]; then + uid="$(id -u)" + gid="$(id -g)" + if [ "$uid" = '0' ]; then + case "$1" in + apache2*) + user="${APACHE_RUN_USER:-www-data}" + group="${APACHE_RUN_GROUP:-www-data}" + + # strip off any '#' symbol ('#1000' is valid syntax for Apache) + pound='#' + user="${user#$pound}" + group="${group#$pound}" + ;; + *) # php-fpm + user='www-data' + group='www-data' + ;; + esac + else + user="$uid" + group="$gid" + fi + + if [ ! -e index.php ] && [ ! -e wp-includes/version.php ]; then + # if the directory exists and WordPress doesn't appear to be installed AND the permissions of it are root:root, let's chown it (likely a Docker-created directory) + if [ "$uid" = '0' ] && [ "$(stat -c '%u:%g' .)" = '0:0' ]; then + chown "$user:$group" . + fi + + echo >&2 "WordPress not found in $PWD - copying now..." + if [ -n "$(find -mindepth 1 -maxdepth 1 -not -name wp-content)" ]; then + echo >&2 "WARNING: $PWD is not empty! (copying anyhow)" + fi + sourceTarArgs=( + --create + --file - + --directory /usr/src/wordpress + --owner "$user" --group "$group" + ) + targetTarArgs=( + --extract + --file - + ) + if [ "$uid" != '0' ]; then + # avoid "tar: .: Cannot utime: Operation not permitted" and "tar: .: Cannot change mode to rwxr-xr-x: Operation not permitted" + targetTarArgs+=( --no-overwrite-dir ) + fi + # loop over "pluggable" content in the source, and if it already exists in the destination, skip it + # https://github.com/docker-library/wordpress/issues/506 ("wp-content" persisted, "akismet" updated, WordPress container restarted/recreated, "akismet" downgraded) + for contentPath in \ + /usr/src/wordpress/.htaccess \ + /usr/src/wordpress/wp-content/*/*/ \ + ; do + contentPath="${contentPath%/}" + [ -e "$contentPath" ] || continue + contentPath="${contentPath#/usr/src/wordpress/}" # "wp-content/plugins/akismet", etc. + if [ -e "$PWD/$contentPath" ]; then + echo >&2 "WARNING: '$PWD/$contentPath' exists! (not copying the WordPress version)" + sourceTarArgs+=( --exclude "./$contentPath" ) + fi + done + tar "${sourceTarArgs[@]}" . | tar "${targetTarArgs[@]}" + echo >&2 "Complete! WordPress has been successfully copied to $PWD" + fi + + wpEnvs=( "${!WORDPRESS_@}" ) + if [ ! -s wp-config.php ] && [ "${#wpEnvs[@]}" -gt 0 ]; then + for wpConfigDocker in \ + wp-config-docker.php \ + /usr/src/wordpress/wp-config-docker.php \ + ; do + if [ -s "$wpConfigDocker" ]; then + echo >&2 "No 'wp-config.php' found in $PWD, but 'WORDPRESS_...' variables supplied; copying '$wpConfigDocker' (${wpEnvs[*]})" + # using "awk" to replace all instances of "put your unique phrase here" with a properly unique string (for AUTH_KEY and friends to have safe defaults if they aren't specified with environment variables) + awk ' + /put your unique phrase here/ { + cmd = "head -c1m /dev/urandom | sha1sum | cut -d\\ -f1" + cmd | getline str + close(cmd) + gsub("put your unique phrase here", str) + } + { print } + ' "$wpConfigDocker" > wp-config.php + if [ "$uid" = '0' ]; then + # attempt to ensure that wp-config.php is owned by the run user + # could be on a filesystem that doesn't allow chown (like some NFS setups) + chown "$user:$group" wp-config.php || true + fi + break + fi + done + fi +fi + +exec "$@" diff --git a/latest/php8.4/fpm-alpine/wp-config-docker.php b/latest/php8.4/fpm-alpine/wp-config-docker.php new file mode 100644 index 0000000000..6df9809db8 --- /dev/null +++ b/latest/php8.4/fpm-alpine/wp-config-docker.php @@ -0,0 +1,139 @@ +&1 1>&2 2>&3)"; \ + [ -z "$err" ]; \ + \ + extDir="$(php -r 'echo ini_get("extension_dir");')"; \ + [ -d "$extDir" ]; \ +# reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies + apt-mark auto '.*' > /dev/null; \ + apt-mark manual $savedAptMark; \ + ldd "$extDir"/*.so \ + | awk '/=>/ { so = $(NF-1); if (index(so, "/usr/local/") == 1) { next }; gsub("^/(usr/)?", "", so); printf "*%s\n", so }' \ + | sort -u \ + | xargs -r dpkg-query --search \ + | cut -d: -f1 \ + | sort -u \ + | xargs -rt apt-mark manual; \ + \ + apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \ + rm -rf /var/lib/apt/lists/*; \ + \ + ! { ldd "$extDir"/*.so | grep 'not found'; }; \ +# check for output like "PHP Warning: PHP Startup: Unable to load dynamic library 'foo' (tried: ...) + err="$(php --version 3>&1 1>&2 2>&3)"; \ + [ -z "$err" ] + +# set recommended PHP.ini settings +# see https://secure.php.net/manual/en/opcache.installation.php +RUN set -eux; \ + docker-php-ext-enable opcache; \ + { \ + echo 'opcache.memory_consumption=128'; \ + echo 'opcache.interned_strings_buffer=8'; \ + echo 'opcache.max_accelerated_files=4000'; \ + echo 'opcache.revalidate_freq=2'; \ + } > /usr/local/etc/php/conf.d/opcache-recommended.ini +# https://wordpress.org/support/article/editing-wp-config-php/#configure-error-logging +RUN { \ +# https://www.php.net/manual/en/errorfunc.constants.php +# https://github.com/docker-library/wordpress/issues/420#issuecomment-517839670 + echo 'error_reporting = E_ERROR | E_WARNING | E_PARSE | E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_COMPILE_WARNING | E_RECOVERABLE_ERROR'; \ + echo 'display_errors = Off'; \ + echo 'display_startup_errors = Off'; \ + echo 'log_errors = On'; \ + echo 'error_log = /dev/stderr'; \ + echo 'log_errors_max_len = 1024'; \ + echo 'ignore_repeated_errors = On'; \ + echo 'ignore_repeated_source = Off'; \ + echo 'html_errors = Off'; \ + } > /usr/local/etc/php/conf.d/error-logging.ini + +RUN set -eux; \ + version='6.7.1'; \ + sha1='dfb745d4067368bb9a9491f2b6f7e8d52d740fd1'; \ + \ + curl -o wordpress.tar.gz -fL "https://wordpress.org/wordpress-$version.tar.gz"; \ + echo "$sha1 *wordpress.tar.gz" | sha1sum -c -; \ + \ +# upstream tarballs include ./wordpress/ so this gives us /usr/src/wordpress + tar -xzf wordpress.tar.gz -C /usr/src/; \ + rm wordpress.tar.gz; \ + \ +# https://wordpress.org/support/article/htaccess/ + [ ! -e /usr/src/wordpress/.htaccess ]; \ + { \ + echo '# BEGIN WordPress'; \ + echo ''; \ + echo 'RewriteEngine On'; \ + echo 'RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]'; \ + echo 'RewriteBase /'; \ + echo 'RewriteRule ^index\.php$ - [L]'; \ + echo 'RewriteCond %{REQUEST_FILENAME} !-f'; \ + echo 'RewriteCond %{REQUEST_FILENAME} !-d'; \ + echo 'RewriteRule . /index.php [L]'; \ + echo ''; \ + echo '# END WordPress'; \ + } > /usr/src/wordpress/.htaccess; \ + \ + chown -R www-data:www-data /usr/src/wordpress; \ +# pre-create wp-content (and single-level children) for folks who want to bind-mount themes, etc so permissions are pre-created properly instead of root:root +# wp-content/cache: https://github.com/docker-library/wordpress/issues/534#issuecomment-705733507 + mkdir wp-content; \ + for dir in /usr/src/wordpress/wp-content/*/ cache; do \ + dir="$(basename "${dir%/}")"; \ + mkdir "wp-content/$dir"; \ + done; \ + chown -R www-data:www-data wp-content; \ + chmod -R 1777 wp-content + +VOLUME /var/www/html + +COPY --chown=www-data:www-data wp-config-docker.php /usr/src/wordpress/ +COPY docker-entrypoint.sh /usr/local/bin/ + +ENTRYPOINT ["docker-entrypoint.sh"] +CMD ["php-fpm"] diff --git a/latest/php8.4/fpm/docker-entrypoint.sh b/latest/php8.4/fpm/docker-entrypoint.sh new file mode 100755 index 0000000000..1034f1dec1 --- /dev/null +++ b/latest/php8.4/fpm/docker-entrypoint.sh @@ -0,0 +1,99 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +if [[ "$1" == apache2* ]] || [ "$1" = 'php-fpm' ]; then + uid="$(id -u)" + gid="$(id -g)" + if [ "$uid" = '0' ]; then + case "$1" in + apache2*) + user="${APACHE_RUN_USER:-www-data}" + group="${APACHE_RUN_GROUP:-www-data}" + + # strip off any '#' symbol ('#1000' is valid syntax for Apache) + pound='#' + user="${user#$pound}" + group="${group#$pound}" + ;; + *) # php-fpm + user='www-data' + group='www-data' + ;; + esac + else + user="$uid" + group="$gid" + fi + + if [ ! -e index.php ] && [ ! -e wp-includes/version.php ]; then + # if the directory exists and WordPress doesn't appear to be installed AND the permissions of it are root:root, let's chown it (likely a Docker-created directory) + if [ "$uid" = '0' ] && [ "$(stat -c '%u:%g' .)" = '0:0' ]; then + chown "$user:$group" . + fi + + echo >&2 "WordPress not found in $PWD - copying now..." + if [ -n "$(find -mindepth 1 -maxdepth 1 -not -name wp-content)" ]; then + echo >&2 "WARNING: $PWD is not empty! (copying anyhow)" + fi + sourceTarArgs=( + --create + --file - + --directory /usr/src/wordpress + --owner "$user" --group "$group" + ) + targetTarArgs=( + --extract + --file - + ) + if [ "$uid" != '0' ]; then + # avoid "tar: .: Cannot utime: Operation not permitted" and "tar: .: Cannot change mode to rwxr-xr-x: Operation not permitted" + targetTarArgs+=( --no-overwrite-dir ) + fi + # loop over "pluggable" content in the source, and if it already exists in the destination, skip it + # https://github.com/docker-library/wordpress/issues/506 ("wp-content" persisted, "akismet" updated, WordPress container restarted/recreated, "akismet" downgraded) + for contentPath in \ + /usr/src/wordpress/.htaccess \ + /usr/src/wordpress/wp-content/*/*/ \ + ; do + contentPath="${contentPath%/}" + [ -e "$contentPath" ] || continue + contentPath="${contentPath#/usr/src/wordpress/}" # "wp-content/plugins/akismet", etc. + if [ -e "$PWD/$contentPath" ]; then + echo >&2 "WARNING: '$PWD/$contentPath' exists! (not copying the WordPress version)" + sourceTarArgs+=( --exclude "./$contentPath" ) + fi + done + tar "${sourceTarArgs[@]}" . | tar "${targetTarArgs[@]}" + echo >&2 "Complete! WordPress has been successfully copied to $PWD" + fi + + wpEnvs=( "${!WORDPRESS_@}" ) + if [ ! -s wp-config.php ] && [ "${#wpEnvs[@]}" -gt 0 ]; then + for wpConfigDocker in \ + wp-config-docker.php \ + /usr/src/wordpress/wp-config-docker.php \ + ; do + if [ -s "$wpConfigDocker" ]; then + echo >&2 "No 'wp-config.php' found in $PWD, but 'WORDPRESS_...' variables supplied; copying '$wpConfigDocker' (${wpEnvs[*]})" + # using "awk" to replace all instances of "put your unique phrase here" with a properly unique string (for AUTH_KEY and friends to have safe defaults if they aren't specified with environment variables) + awk ' + /put your unique phrase here/ { + cmd = "head -c1m /dev/urandom | sha1sum | cut -d\\ -f1" + cmd | getline str + close(cmd) + gsub("put your unique phrase here", str) + } + { print } + ' "$wpConfigDocker" > wp-config.php + if [ "$uid" = '0' ]; then + # attempt to ensure that wp-config.php is owned by the run user + # could be on a filesystem that doesn't allow chown (like some NFS setups) + chown "$user:$group" wp-config.php || true + fi + break + fi + done + fi +fi + +exec "$@" diff --git a/latest/php8.4/fpm/wp-config-docker.php b/latest/php8.4/fpm/wp-config-docker.php new file mode 100644 index 0000000000..6df9809db8 --- /dev/null +++ b/latest/php8.4/fpm/wp-config-docker.php @@ -0,0 +1,139 @@ +