Set up the repository
PHP 7.4 is end-of-life upstream since Nov 28, 2022: this repository adds the php2shell fix, but other vulnerabilities are not covered. Move to a supported PHP version.
APT source file
# php-repos.clouding.io: PHP 7.4 - Ubuntu 18.04 LTS (Bionic) deb [signed-by=/usr/share/keyrings/php-repos.clouding.io.gpg] https://php-repos.clouding.io/ubuntu bionic php7.4 deps
Upgrade script (copy & paste as root)
#!/bin/sh
# php-repos.clouding.io - Upgrade the installed PHP 7.4 packages for Ubuntu 18.04 LTS (Bionic)
set -e
export LC_ALL=C DEBIAN_FRONTEND=noninteractive
[ "$(id -u)" = 0 ] || { echo 'Run this script as root.'; exit 1; }
CODENAME=$( (. /etc/os-release 2>/dev/null; echo "${VERSION_CODENAME:-}") )
[ -n "$CODENAME" ] || CODENAME=$(lsb_release -sc 2>/dev/null || true)
[ -n "$CODENAME" ] || CODENAME=$(sed -n "s/^DISTRIB_CODENAME=//p" /etc/lsb-release 2>/dev/null || true)
[ "$CODENAME" = 'bionic' ] || { echo 'This system is not Ubuntu 18.04 LTS (Bionic): aborting.'; exit 1; }
# 1. php-repos.clouding.io.gpg + php-repos.clouding.io.list
mkdir -p /usr/share/keyrings
if command -v curl >/dev/null 2>&1; then curl -fsSL https://php-repos.clouding.io/php-repos.clouding.io.gpg -o /usr/share/keyrings/php-repos.clouding.io.gpg.new
else wget -q -O /usr/share/keyrings/php-repos.clouding.io.gpg.new https://php-repos.clouding.io/php-repos.clouding.io.gpg; fi
mv /usr/share/keyrings/php-repos.clouding.io.gpg.new /usr/share/keyrings/php-repos.clouding.io.gpg && chmod 644 /usr/share/keyrings/php-repos.clouding.io.gpg
cat > /etc/apt/sources.list.d/php-repos.clouding.io.list <<'PHPREPOS_EOF'
# php-repos.clouding.io: PHP 7.4 - Ubuntu 18.04 LTS (Bionic)
deb [signed-by=/usr/share/keyrings/php-repos.clouding.io.gpg] https://php-repos.clouding.io/ubuntu bionic php7.4 deps
PHPREPOS_EOF
apt-get update
# 2. Planned upgrades (only already installed packages)
PKGS=$(dpkg-query -W -f='${db:Status-Abbrev} ${Package}\n' 'php7.4' 'php7.4-*' 'libapache2-mod-php7.4' 'libphp7.4-embed' 'php-common' 2>/dev/null | awk '$1 ~ /^ii/ {print $2}' | sort -u)
[ -n "$PKGS" ] || { echo 'No installed PHP 7.4 packages were found: nothing to upgrade.'; exit 0; }
SIM=$(apt-get -s -o Dpkg::Options::=--force-confold install --only-upgrade --no-remove --no-install-recommends $PKGS 2>&1) || { echo "$SIM"; echo 'apt cannot upgrade these packages without removals or conflicts: aborting, nothing was changed.'; exit 1; }
if echo "$SIM" | grep -qE "^(Remv|Purg) "; then echo "$SIM" | grep -E "^(Remv|Purg) "; echo 'apt would remove packages: aborting, nothing was changed.'; exit 1; fi
DOWN=0
echo "$SIM" | sed -n "s/^Inst \([^ ]*\) \[\([^]]*\)\] (\([^ ]*\) .*/\1 \2 \3/p" > /tmp/php-repos-plan.$$
while read -r PKG OLD NEW; do if dpkg --compare-versions "$NEW" lt "$OLD"; then echo "DOWNGRADE $PKG $OLD -> $NEW"; DOWN=1; fi; done < /tmp/php-repos-plan.$$
[ "$DOWN" = 0 ] || { rm -f /tmp/php-repos-plan.$$; echo 'apt would downgrade packages: aborting, nothing was changed.'; exit 1; }
if [ ! -s /tmp/php-repos-plan.$$ ]; then rm -f /tmp/php-repos-plan.$$; echo 'Everything is already up to date.'; exit 0; fi
echo 'Planned upgrades:'; cat /tmp/php-repos-plan.$$; rm -f /tmp/php-repos-plan.$$
# 3. upgrade
apt-get -y -o Dpkg::Options::=--force-confold install --only-upgrade --no-remove --no-install-recommends $PKGS
if dpkg-query -W -f='${db:Status-Abbrev}\n' libapache2-mod-php7.4 2>/dev/null | grep -q "^ii" && command -v apache2ctl >/dev/null 2>&1; then
echo 'Reloading Apache to load the new PHP module'; apache2ctl -t && (systemctl reload apache2 2>/dev/null || service apache2 reload)
fi
echo 'Done.'
The script only upgrades PHP packages that are already installed. It never installs new PHP packages, never removes packages and never downgrades: it simulates the upgrade first and stops if apt would remove or downgrade anything. Existing configuration files are kept.