From bf1c5611d7d1b04ceb65352559b5b380cc242b0e Mon Sep 17 00:00:00 2001 From: Volodymyr Kolesnykov Date: Thu, 30 May 2024 08:06:55 +0300 Subject: [PATCH] refactor(php): support Ubuntu and Debian * add support for Debian and Ubuntu (noble is not supported at the moment; see https://github.com/oerdnj/deb.sury.org/issues/2127) * remove `installsAfter`; * install the required packages automatically; * update PHP configuration files --- features/src/php/devcontainer-feature.json | 10 +- features/src/php/install.sh | 332 +++++++++++++++++---- features/src/php/php.ini | 21 +- features/src/php/www.conf.tpl | 4 +- 4 files changed, 280 insertions(+), 87 deletions(-) diff --git a/features/src/php/devcontainer-feature.json b/features/src/php/devcontainer-feature.json index 0514a3c2..ca854ecf 100644 --- a/features/src/php/devcontainer-feature.json +++ b/features/src/php/devcontainer-feature.json @@ -2,10 +2,7 @@ "id": "php", "name": "PHP", "description": "Installs PHP into the Dev Environment", - "version": "2.3.1", - "containerEnv": { - "PHP_INI_DIR": "/etc/php" - }, + "version": "2.4.0", "options": { "version": { "type": "string", @@ -44,8 +41,5 @@ "phpSniffer.autoDetect": true } } - }, - "installsAfter": [ - "ghcr.io/automattic/vip-codespaces/base" - ] + } } diff --git a/features/src/php/install.sh b/features/src/php/install.sh index 889373f9..41796bcc 100755 --- a/features/src/php/install.sh +++ b/features/src/php/install.sh @@ -4,7 +4,15 @@ set -e PATH=/usr/local/bin:/usr/local/sbin:/bin:/sbin:/usr/bin:/usr/sbin -setup_php81() { +setup_php81_alpine() { + alpine_version="$(cat /etc/alpine-release)" + if [ "$(printf '%s\n' "3.20" "${alpine_version}" | sort -V | head -n1 || true)" = "3.20" ]; then + REPOS="-X https://dl-cdn.alpinelinux.org/alpine/v3.19/main -X https://dl-cdn.alpinelinux.org/alpine/v3.19/community" + else + REPOS="" + fi + + # shellcheck disable=SC2086 # We need to expand $REPOS apk add --no-cache \ icu-data-full ghostscript \ php81 php81-fpm php81-pear \ @@ -33,6 +41,7 @@ setup_php81() { php81-openssl \ php81-pcntl \ php81-pdo \ + php81-pdo_mysql \ php81-pdo_sqlite \ php81-phar \ php81-posix \ @@ -51,26 +60,27 @@ setup_php81() { php81-xml \ php81-xmlreader \ php81-xmlwriter \ - php81-zip + php81-zip ${REPOS} - apk add --no-cache php81-dev gcc make libc-dev graphicsmagick-dev libtool graphicsmagick libgomp + # shellcheck disable=SC2086 # We need to expand $REPOS + apk add --no-cache php81-dev gcc make libc-dev graphicsmagick-dev libtool graphicsmagick libgomp ${REPOS} pecl81 channel-update pecl.php.net pecl81 install channel://pecl.php.net/gmagick-2.0.6RC1 < /dev/null || true apk del --no-cache php81-dev gcc make libc-dev graphicsmagick-dev libtool echo "extension=gmagick.so" > /etc/php81/conf.d/40_gmagick.ini - [ ! -f /usr/sbin/php-fpm ] && ln -s /usr/sbin/php-fpm81 /usr/sbin/php-fpm - [ ! -f /usr/bin/php ] && ln -s /usr/bin/php81 /usr/bin/php - [ ! -f /usr/bin/pecl ] && ln -s /usr/bin/pecl81 /usr/bin/pecl [ ! -f /usr/bin/pear ] && ln -s /usr/bin/pear81 /usr/bin/pear [ ! -f /usr/bin/peardev ] && ln -s /usr/bin/peardev81 /usr/bin/peardev + [ ! -f /usr/bin/pecl ] && ln -s /usr/bin/pecl81 /usr/bin/pecl + [ ! -f /usr/bin/phar.phar ] && ln -s /usr/bin/phar.phar81 /usr/bin/phar.phar [ ! -f /usr/bin/phar ] && ln -s /usr/bin/phar81 /usr/bin/phar - [ ! -f /usr/bin/phar.phar ] && ln -s /usr/bin/phar81 /usr/bin/phar.phar + [ ! -f /usr/bin/php ] && ln -s /usr/bin/php81 /usr/bin/php + [ ! -f /usr/sbin/php-fpm ] && ln -s /usr/sbin/php-fpm81 /usr/sbin/php-fpm true } -setup_php82() { +setup_php82_alpine() { apk add --no-cache \ icu-data-full ghostscript \ php82 php82-fpm php82-pear \ @@ -99,6 +109,7 @@ setup_php82() { php82-openssl \ php82-pcntl \ php82-pdo \ + php82-pdo_mysql \ php82-pdo_sqlite \ php82-phar \ php82-posix \ @@ -125,17 +136,17 @@ setup_php82() { echo "extension=gmagick.so" > /etc/php82/conf.d/40_gmagick.ini - [ ! -f /usr/sbin/php-fpm ] && ln -s /usr/sbin/php-fpm82 /usr/sbin/php-fpm - [ ! -f /usr/bin/php ] && ln -s /usr/bin/php82 /usr/bin/php - [ ! -f /usr/bin/pecl ] && ln -s /usr/bin/pecl82 /usr/bin/pecl [ ! -f /usr/bin/pear ] && ln -s /usr/bin/pear82 /usr/bin/pear [ ! -f /usr/bin/peardev ] && ln -s /usr/bin/peardev82 /usr/bin/peardev + [ ! -f /usr/bin/pecl ] && ln -s /usr/bin/pecl82 /usr/bin/pecl + [ ! -f /usr/bin/phar.phar ] && ln -s /usr/bin/phar.phar82 /usr/bin/phar.phar [ ! -f /usr/bin/phar ] && ln -s /usr/bin/phar82 /usr/bin/phar - [ ! -f /usr/bin/phar.phar ] && ln -s /usr/bin/phar82 /usr/bin/phar.phar + [ ! -f /usr/bin/php ] && ln -s /usr/bin/php82 /usr/bin/php + [ ! -f /usr/sbin/php-fpm ] && ln -s /usr/sbin/php-fpm82 /usr/sbin/php-fpm true } -setup_php83() { +setup_php83_alpine() { apk add --no-cache \ icu-data-full ghostscript \ php83 php83-fpm php83-pear \ @@ -163,6 +174,7 @@ setup_php83() { php83-openssl \ php83-pcntl \ php83-pdo \ + php83-pdo_mysql \ php83-pdo_sqlite \ php83-phar \ php83-posix \ @@ -181,91 +193,289 @@ setup_php83() { php83-xmlwriter \ php83-zip - # Missing: php83-pecl-mcrypt - apk add --no-cache php83-dev gcc make libc-dev graphicsmagick-dev libtool graphicsmagick libgomp pecl83 channel-update pecl.php.net pecl83 install channel://pecl.php.net/gmagick-2.0.6RC1 < /dev/null || true - pecl83 install timezonedb < /dev/null || true + echo "extension=gmagick.so" > /etc/php83/conf.d/40_gmagick.ini - apk del --no-cache php83-dev gcc make libc-dev graphicsmagick-dev libtool + alpine_version="$(cat /etc/alpine-release)" + if [ "$(printf '%s\n' "3.20" "${alpine_version}" | sort -V | head -n1 || true)" = "3.20" ]; then + apk add --no-cache php83-pecl-mcrypt php83-pecl-timezonedb + # Alpine 3.20.0: these symlinks are broken + rm -f /usr/bin/phar /usr/bin/phar.phar + else + pecl83 install timezonedb < /dev/null || true + echo "extension=timezonedb.so" > /etc/php83/conf.d/40_timezonedb.ini + fi - echo "extension=gmagick.so" > /etc/php83/conf.d/40_gmagick.ini - echo "extension=timezonedb.so" > /etc/php83/conf.d/40_timezonedb.ini + apk del --no-cache php83-dev gcc make libc-dev graphicsmagick-dev libtool - [ ! -f /usr/sbin/php-fpm ] && ln -s /usr/sbin/php-fpm83 /usr/sbin/php-fpm - [ ! -f /usr/bin/php ] && ln -s /usr/bin/php83 /usr/bin/php - [ ! -f /usr/bin/pecl ] && ln -s /usr/bin/pecl83 /usr/bin/pecl [ ! -f /usr/bin/pear ] && ln -s /usr/bin/pear83 /usr/bin/pear [ ! -f /usr/bin/peardev ] && ln -s /usr/bin/peardev83 /usr/bin/peardev + [ ! -f /usr/bin/pecl ] && ln -s /usr/bin/pecl83 /usr/bin/pecl + [ ! -f /usr/bin/phar.phar ] && ln -s /usr/bin/phar.phar83 /usr/bin/phar.phar [ ! -f /usr/bin/phar ] && ln -s /usr/bin/phar83 /usr/bin/phar - [ ! -f /usr/bin/phar.phar ] && ln -s /usr/bin/phar83 /usr/bin/phar.phar + [ ! -f /usr/bin/php ] && ln -s /usr/bin/php83 /usr/bin/php + [ ! -f /usr/sbin/php-fpm ] && ln -s /usr/sbin/php-fpm83 /usr/sbin/php-fpm true } +setup_php81_deb() { + eatmydata apt-get install -y --no-install-recommends ghostscript + eatmydata apt-get install -y \ + php8.1-cli php8.1-fpm \ + php8.1-apcu php8.1-bcmath php8.1-curl php8.1-gd php8.1-gmagick php8.1-gmp php8.1-gnupg php8.1-intl php8.1-igbinary php8.1-mbstring php8.1-mcrypt \ + php8.1-memcache php8.1-memcached php8.1-mysql php8.1-soap php8.1-sqlite3 php8.1-ssh2 php8.1-xml php8.1-zip + eatmydata apt-get install -y --no-install-recommends php-pear + phpdismod ffi gettext readline sysvmsg xsl + + ln -s /usr/sbin/php-fpm8.1 /usr/sbin/php-fpm + + PACKAGES="php8.1-dev" + if ! hash make >/dev/null 2>&1; then + PACKAGES="${PACKAGES} make" + fi + + # shellcheck disable=SC2086 + eatmydata apt-get install -y --no-install-recommends ${PACKAGES} + pecl channel-update pecl.php.net + pecl install timezonedb < /dev/null + echo "extension=timezonedb.so" > /etc/php/8.1/mods-available/timezonedb.ini + phpenmod timezonedb + + # shellcheck disable=SC2086 + eatmydata apt-get remove --purge -y ${PACKAGES} +} + +setup_php82_deb() { + eatmydata apt-get install -y --no-install-recommends ghostscript + eatmydata apt-get install -y \ + php8.2-cli php8.2-fpm \ + php8.2-apcu php8.2-bcmath php8.2-curl php8.2-gd php8.2-gmagick php8.2-gmp php8.2-gnupg php8.2-intl php8.2-igbinary php8.2-mbstring php8.2-mcrypt \ + php8.2-memcache php8.2-memcached php8.2-mysql php8.2-soap php8.2-sqlite3 php8.2-ssh2 php8.2-xml php8.2-zip + eatmydata apt-get install -y --no-install-recommends php-pear + phpdismod ffi gettext readline sysvmsg xsl + + ln -s /usr/sbin/php-fpm8.2 /usr/sbin/php-fpm + + PACKAGES="php8.2-dev" + if ! hash make >/dev/null 2>&1; then + PACKAGES="${PACKAGES} make" + fi + + # shellcheck disable=SC2086 + eatmydata apt-get install -y --no-install-recommends ${PACKAGES} + pecl channel-update pecl.php.net + pecl install timezonedb < /dev/null + echo "extension=timezonedb.so" > /etc/php/8.2/mods-available/timezonedb.ini + phpenmod timezonedb + + # shellcheck disable=SC2086 + eatmydata apt-get remove --purge -y ${PACKAGES} +} + +setup_php83_deb() { + eatmydata apt-get install -y --no-install-recommends ghostscript + eatmydata apt-get install -y \ + php8.3-cli php8.3-fpm \ + php8.3-apcu php8.3-bcmath php8.3-curl php8.3-gd php8.3-gmagick php8.3-gmp php8.3-gnupg php8.3-igbinary php8.3-intl php8.3-mbstring php8.3-mcrypt \ + php8.3-memcache php8.3-memcached php8.3-mysql php8.3-soap php8.3-sqlite3 php8.3-ssh2 php8.3-xml php8.3-zip + eatmydata apt-get install -y --no-install-recommends php-pear + phpdismod ffi gettext readline sysvmsg xsl + + ln -s /usr/sbin/php-fpm8.3 /usr/sbin/php-fpm + + PACKAGES="php8.3-dev" + if ! hash make >/dev/null 2>&1; then + PACKAGES="${PACKAGES} make" + fi + + # shellcheck disable=SC2086 + eatmydata apt-get install -y --no-install-recommends ${PACKAGES} + pecl channel-update pecl.php.net + pecl install timezonedb < /dev/null + echo "extension=timezonedb.so" > /etc/php/8.3/mods-available/timezonedb.ini + phpenmod timezonedb + + # shellcheck disable=SC2086 + eatmydata apt-get remove --purge -y ${PACKAGES} +} + if [ "$(id -u || true)" -ne 0 ]; then echo 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.' exit 1 fi +: "${_REMOTE_USER:?"_REMOTE_USER is required"}" : "${VERSION:?}" : "${COMPOSER:=}" +PHP_VERSION="${VERSION}" -echo "(*) Installing PHP ${VERSION}..." -case "${VERSION}" in - "8.0"|"8.1") - VERSION="8.1" - setup_php81 - echo "export PHP_INI_DIR=/etc/php81" > /etc/profile.d/php_ini_dir.sh - PHP_INI_DIR=/etc/php81 - ln -sf /etc/php81 /etc/php - ;; +echo "(*) Installing PHP ${PHP_VERSION}..." + +# shellcheck source=/dev/null +. /etc/os-release + +: "${ID:=}" +: "${ID_LIKE:=${ID}}" + +case "${ID_LIKE}" in + "debian") + export DEBIAN_FRONTEND=noninteractive + PACKAGES="" + if ! hash eatmydata >/dev/null 2>&1; then + PACKAGES="${PACKAGES} eatmydata" + fi + + if ! hash curl >/dev/null 2>&1; then + PACKAGES="${PACKAGES} curl" + fi + + if ! hash update-ca-certificates >/dev/null 2>&1; then + PACKAGES="${PACKAGES} ca-certificates" + fi + + if ! hash gpg >/dev/null 2>&1; then + PACKAGES="${PACKAGES} gnupg2" + fi + + if ! hash lsb_release >/dev/null 2>&1; then + PACKAGES="${PACKAGES} lsb-release" + fi + + if ! hash envsubst >/dev/null 2>&1; then + PACKAGES="${PACKAGES} gettext" + fi + + if [ -n "${PACKAGES}" ]; then + apt-get update + # shellcheck disable=SC2086 + apt-get install -y --no-install-recommends ${PACKAGES} + fi + + CODENAME="$(lsb_release -sc)" + + case "${ID}" in + "debian") + curl -SLo /tmp/debsuryorg-archive-keyring.deb https://packages.sury.org/debsuryorg-archive-keyring.deb + dpkg -i /tmp/debsuryorg-archive-keyring.deb + rm -f /tmp/debsuryorg-archive-keyring.deb + echo "deb https://packages.sury.org/php/ ${CODENAME} main" > /etc/apt/sources.list.d/php.list + ;; + + "ubuntu") + echo "deb http://ppa.launchpad.net/ondrej/php/ubuntu ${CODENAME} main" > /etc/apt/sources.list.d/php.list + curl -sSL "https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x71DAEAAB4AD4CAB6" | gpg --dearmor > /etc/apt/trusted.gpg.d/ppa-ondrej-php.gpg + ;; + + *) + echo "(!) Unsupported distribution: ${ID}" + exit 1 + esac + + apt-get update - "8.2") - setup_php82 - echo "export PHP_INI_DIR=/etc/php82" > /etc/profile.d/php_ini_dir.sh - PHP_INI_DIR=/etc/php82 - ln -sf /etc/php82 /etc/php + case "${PHP_VERSION}" in + "8.0"|"8.1") + PHP_VERSION="8.1" + PHP_INI_DIR=/etc/php/8.1 + setup_php81_deb + ;; + + "8.2") + PHP_INI_DIR=/etc/php/8.2 + setup_php82_deb + ;; + + "8.3") + PHP_INI_DIR=/etc/php/8.3 + setup_php83_deb + ;; + + *) + echo "(!) PHP version ${PHP_VERSION} is not supported." + exit 1 + ;; + esac + + echo "export PHP_INI_DIR=${PHP_INI_DIR}" > /etc/profile.d/php_ini_dir.sh + + install -m 0644 php.ini "${PHP_INI_DIR}/cli/php.ini" + install -m 0644 php.ini "${PHP_INI_DIR}/fpm/php.ini" + # shellcheck disable=SC2016 + envsubst '$_REMOTE_USER' < www.conf.tpl > "${PHP_INI_DIR}/fpm/pool.d/www.conf" + install -m 0644 -o root -g root docker.conf zz-docker.conf "${PHP_INI_DIR}/fpm/pool.d/" + + apt-get autoremove --purge -y + apt-get clean + rm -rf /var/lib/apt/lists/* ;; - "8.3") - setup_php83 - echo "export PHP_INI_DIR=/etc/php83" > /etc/profile.d/php_ini_dir.sh - PHP_INI_DIR=/etc/php83 - ln -sf /etc/php83 /etc/php + "alpine") + PACKAGES="" + if ! hash curl >/dev/null 2>&1; then + PACKAGES="${PACKAGES} curl" + fi + + if ! hash envsubst >/dev/null 2>&1; then + PACKAGES="${PACKAGES} gettext" + fi + + if [ -n "${PACKAGES}" ]; then + # shellcheck disable=SC2086 + apk add --no-cache ${PACKAGES} + fi + + case "${PHP_VERSION}" in + "8.0"|"8.1") + PHP_VERSION="8.1" + PHP_INI_DIR=/etc/php81 + setup_php81_alpine + ;; + + "8.2") + PHP_INI_DIR=/etc/php82 + setup_php82_alpine + ;; + + "8.3") + PHP_INI_DIR=/etc/php83 + setup_php83_alpine + ;; + + *) + echo "(!) PHP version ${PHP_VERSION} is not supported." + exit 1 + ;; + esac + + echo "export PHP_INI_DIR=${PHP_INI_DIR}" > /etc/profile.d/php_ini_dir.sh + + getent group www-data > /dev/null || addgroup -g 82 -S www-data + getent passwd www-data > /dev/null || adduser -u 82 -D -S -G www-data -H www-data + + install -m 0644 php.ini "${PHP_INI_DIR}/php.ini" + # shellcheck disable=SC2016 + envsubst '$_REMOTE_USER' < www.conf.tpl > "${PHP_INI_DIR}/php-fpm.d/www.conf" + install -m 0644 -o root -g root docker.conf zz-docker.conf "${PHP_INI_DIR}/php-fpm.d/" ;; *) - echo "(!) PHP version ${VERSION} is not supported." + echo "(!) Unsupported distribution: ${ID}" exit 1 - ;; esac -getent group www-data > /dev/null || addgroup -g 82 -S www-data -getent passwd www-data > /dev/null || adduser -u 82 -D -S -G www-data -H www-data - pecl update-channels rm -rf /tmp/pear ~/.pearrc -install -m 0644 php.ini "${PHP_INI_DIR}/php.ini" -if [ -z "${_REMOTE_USER}" ] || [ "${_REMOTE_USER}" = "root" ]; then - PHP_USER="www-data" -else - PHP_USER="${_REMOTE_USER}" -fi - -export PHP_USER -# shellcheck disable=SC2016 -envsubst '$PHP_USER' < www.conf.tpl > "${PHP_INI_DIR}/php-fpm.d/www.conf" -install -d -m 0750 -o "${PHP_USER}" -g adm /var/log/php-fpm -install -m 0644 -o root -g root docker.conf zz-docker.conf "${PHP_INI_DIR}/php-fpm.d/" +install -d -m 0750 -o "${_REMOTE_USER}" -g adm /var/log/php-fpm install -D -m 0755 -o root -g root service-run /etc/sv/php-fpm/run install -d -m 0755 -o root -g root /etc/service ln -sf /etc/sv/php-fpm /etc/service/php-fpm if [ "${COMPOSER}" = "true" ]; then - wget -q https://getcomposer.org/installer -O composer-setup.php - HASH="$(wget -q -O - https://composer.github.io/installer.sig)" + curl -SLo composer-setup.php https://getcomposer.org/installer + HASH="$(curl -SL https://composer.github.io/installer.sig)" php -r "if (hash_file('sha384', 'composer-setup.php') === '${HASH}') { echo 'Installer verified', PHP_EOL; } else { echo 'Installer corrupt', PHP_EOL; unlink('composer-setup.php'); exit(1); }" php composer-setup.php --install-dir="/usr/local/bin" --filename=composer rm -f composer-setup.php diff --git a/features/src/php/php.ini b/features/src/php/php.ini index aafd85f0..eb2c1038 100644 --- a/features/src/php/php.ini +++ b/features/src/php/php.ini @@ -25,25 +25,14 @@ date.timezone = "UTC" ;; PACKAGE SETTINGS ;; ;;;;;;;;;;;;;;;;;;;;;; -; Xdebug -xdebug.max_nesting_level = 512 -xdebug.remote_autostart = 1 -xdebug.show_exception_trace = 0 -xdebug.collect_params = 0 - -; Extra config to allow enabling profiler -xdebug.profiler_output_dir = /wp -xdebug.profiler_output_name = xdebug.%t.out -xdebug.profiler_enable_trigger = 1 - ; Globals expose_php = on -max_execution_time = 90 -max_input_time = 900 +max_execution_time = 1200 +max_input_time = 1200 max_input_vars = 10000 -memory_limit = 512M -upload_max_filesize = 100M -post_max_size = 100M +memory_limit = 768M +upload_max_filesize = 5120M +post_max_size = 5248M error_reporting = E_ALL & ~E_DEPRECATED ignore_repeated_errors = on html_errors = off diff --git a/features/src/php/www.conf.tpl b/features/src/php/www.conf.tpl index 47596204..00afb5fc 100644 --- a/features/src/php/www.conf.tpl +++ b/features/src/php/www.conf.tpl @@ -1,6 +1,6 @@ [www] -user = ${PHP_USER} -group = ${PHP_USER} +user = ${_REMOTE_USER} +group = ${_REMOTE_USER} listen = 127.0.0.1:9000 pm = dynamic pm.max_children = 5