From 064d75261dd888a7e49e3ff5cb47ad0ca7c5f8c8 Mon Sep 17 00:00:00 2001 From: enoch85 Date: Tue, 7 Jul 2026 18:01:22 +0000 Subject: [PATCH] Fix PECL migration: verify that PHP extensions actually load, repair if not The PECL to OS package migration left systems broken when the OS package was already installed: pecl uninstall deleted the dpkg-owned .so, the mods-available .ini (a dpkg conffile) was removed and never restored, and phpdismod -v ALL left the modules disabled. Re-running the update never repaired this since the migration only triggered on PECL registry state. The block is now state-based instead of history-based: after cleaning up PECL leftovers, each extension is checked with php -m (not loaded, or loaded twice via stale enable symlinks = broken). If any of them is broken, all three packages are purged and reinstalled as one unit (they are interdependent), stale enable symlinks are removed, and the result is verified again. This auto-heals already broken installations on their next update run and is a no-op on healthy systems. Fixes #2820 --- nextcloud_update.sh | 56 ++++++++++++++++++++++++++++++++------------- 1 file changed, 40 insertions(+), 16 deletions(-) diff --git a/nextcloud_update.sh b/nextcloud_update.sh index d0938947d3..e2440c2bf5 100644 --- a/nextcloud_update.sh +++ b/nextcloud_update.sh @@ -450,31 +450,55 @@ print_text_in_color "$ICyan" "Checking PHP extensions (migrating from PECL to OS # Check current PHP version check_php -# Migrate each extension from PECL to OS package -# igbinary must come before redis since php-redis depends on php-igbinary +# Remove PECL leftovers (the PECL .so lives in the same directory as the OS package one, +# so uninstalling it may delete a file owned by an already installed OS package - repaired below) for ext in igbinary smbclient redis do if pecl list 2>/dev/null | grep -q "^$ext " then - print_text_in_color "$ICyan" "Migrating $ext from PECL to OS package..." - # Disable extension first - phpdismod -v ALL "$ext" 2>/dev/null || true - # Uninstall from PECL + print_text_in_color "$ICyan" "Removing PECL version of $ext..." yes | pecl uninstall "$ext" 2>/dev/null || true - # Remove manual .ini if it exists rm -f "$PHP_MODS_DIR/$ext.ini" - # Install OS package (use --allow-change-held-packages in case the package was previously held) - apt-get update -q4 & spinner_loading && RUNLEVEL=1 apt-get install -y --allow-change-held-packages php"$PHPVER"-"$ext" - phpenmod -v "$PHPVER" "$ext" - print_text_in_color "$IGreen" "Migrated $ext to OS package (php$PHPVER-$ext)" - elif ! is_this_installed php"$PHPVER"-"$ext" + fi +done + +# Install as OS packages, or repair systems broken by an earlier version of this migration +# https://github.com/nextcloud/vm/issues/2820 +# The check is based on what actually loads (php -m) rather than package state, since broken +# systems typically have the packages installed while the .so and/or .ini files are gone. +BROKEN_PHP_EXTENSIONS="" +for ext in igbinary smbclient redis +do + # Broken if the extension doesn't load, or is loaded twice (stale PECL-era enable symlinks) + if ! php -m 2>/dev/null | grep -qix "$ext" \ + || php -m 2>&1 | grep -qi "Module \"$ext\" is already loaded" then - # Not installed via PECL, but also not installed as OS package - install it - print_text_in_color "$ICyan" "Installing $ext as OS package..." - apt-get update -q4 & spinner_loading && RUNLEVEL=1 apt-get install -y --allow-change-held-packages php"$PHPVER"-"$ext" - phpenmod -v "$PHPVER" "$ext" + BROKEN_PHP_EXTENSIONS+=" $ext" fi done +if [ -n "$BROKEN_PHP_EXTENSIONS" ] +then + print_text_in_color "$ICyan" "Installing PHP extensions:$BROKEN_PHP_EXTENSIONS" + # Purge and reinstall all three as one unit (php-redis depends on php-igbinary, so they + # can't be repaired one at a time) to make dpkg lay down fresh .so and .ini files again + apt-get purge php"$PHPVER"-igbinary php"$PHPVER"-smbclient php"$PHPVER"-redis -y --allow-change-held-packages + # Remove stale enable symlinks (e.g. PECL-era priority 20) so modules can't get loaded twice + rm -f /etc/php/*/*/conf.d/*-igbinary.ini /etc/php/*/*/conf.d/*-smbclient.ini /etc/php/*/*/conf.d/*-redis.ini + apt-get update -q4 & spinner_loading + RUNLEVEL=1 apt-get install php"$PHPVER"-igbinary php"$PHPVER"-smbclient php"$PHPVER"-redis -y \ + --allow-change-held-packages \ + -o Dpkg::Options::="--force-confmiss" -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" + phpenmod -v ALL igbinary smbclient redis + for ext in igbinary smbclient redis + do + if php -m 2>/dev/null | grep -qix "$ext" + then + print_text_in_color "$IGreen" "$ext is now installed and enabled (php$PHPVER-$ext)" + else + msg_box "The PHP extension $ext couldn't be enabled. Please report this to $ISSUES" + fi + done +fi # Remove old extension references from php.ini if they exist for ext in redis igbinary smbclient