From b93fbe2974d6ed848756fdf34247b7a8cf706f8f Mon Sep 17 00:00:00 2001 From: Riddhesh Sanghvi Date: Thu, 29 Oct 2026 06:15:33 +0530 Subject: [PATCH 1/4] fix(php): stop the install when the PHP CLI disables functions ee needs HestiaCP and similar panels put pcntl_signal, pcntl_signal_dispatch and other functions into disable_functions of the CLI php.ini. PHP 8 removes disabled functions, so the install succeeded and the first `ee site` command died with "Call to undefined function EE\Site\Type\pcntl_signal()". check_php_functions asks the host PHP CLI whether the functions ee calls are available and stops before Docker and the phar are installed, naming the functions, the ini files that disable them and a conf.d override that survives panel upgrades. On PHP 7, where calling a disabled function only warns, disabled pcntl_* functions only get a warning. Refs EasyEngine/easyengine#1956 --- functions | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/functions b/functions index c20697e..4b81b55 100644 --- a/functions +++ b/functions @@ -411,6 +411,67 @@ function setup_php_extensions() { fi } +# Functions ee calls unguarded that hardened php.ini files (e.g. HestiaCP's CLI one) often list in disable_functions. +EE_PHP_REQUIRED_FUNCTIONS="curl_exec disk_free_space escapeshellarg exec getmypid ini_set pcntl_signal pcntl_signal_dispatch php_uname posix_getegid posix_geteuid posix_getpwuid proc_close proc_open" + +function check_php_functions() { + ee_log_info1 "Checking required PHP functions" + if ! command -v php >/dev/null 2>&1; then + return 0 + fi + + local report kind name disabled="" missing="" degraded="" ini_files="" scan_dir="" + # PHP 7 only warns when a disabled function is called, so disabled pcntl_* just loses rollback on signals there. + # shellcheck disable=SC2016,SC2086 # PHP code, not shell; one argument per function + if ! report="$(php -r ' + $off = function_exists("ini_get") ? preg_split("/[\s,]+/", (string) ini_get("disable_functions")) : array(); + foreach (array_slice($argv, 1) as $f) { + if (function_exists($f)) { continue; } + if (!in_array($f, $off, true)) { echo "missing ", $f, "\n"; } + elseif (PHP_MAJOR_VERSION < 8 && strpos($f, "pcntl_") === 0) { echo "degraded ", $f, "\n"; } + else { echo "disabled ", $f, "\n"; } + } + if (function_exists("php_ini_loaded_file") && function_exists("php_ini_scanned_files") && function_exists("file_get_contents")) { + $files = array_merge(array((string) php_ini_loaded_file()), explode(",", (string) php_ini_scanned_files())); + foreach (array_filter(array_map("trim", $files)) as $file) { + if (preg_match("/^[ \t]*disable_functions[ \t]*=[ \t]*[\"\x27]?[ \t]*[A-Za-z_]/m", (string) @file_get_contents($file))) { echo "ini ", $file, "\n"; } + } + } + echo "scandir ", PHP_CONFIG_FILE_SCAN_DIR, "\n";' -- $EE_PHP_REQUIRED_FUNCTIONS)"; then + ee_log_fail "ERROR: Could not run the PHP CLI ($(command -v php)) to check the functions EasyEngine needs." + fi + + while read -r kind name; do + case "$kind" in + disabled) disabled+=" $name" ;; + missing) missing+=" $name" ;; + degraded) degraded+=" $name" ;; + ini) ini_files+=" $name" ;; + scandir) scan_dir="$name" ;; + esac + done <<< "$report" + + if [ -n "$degraded" ]; then + ee_log_warn "The PHP CLI disables${degraded}; EasyEngine can't roll back a site operation interrupted by a signal (on PHP 8 this is a fatal error)." + fi + if [ -z "$disabled" ] && [ -z "$missing" ]; then + return 0 + fi + + if [ -n "$disabled" ]; then + echo "ERROR: The PHP CLI ($(command -v php)) disables functions EasyEngine needs:${disabled}." >&2 + echo " They are listed in disable_functions in:${ini_files:- (see php --ini)}" >&2 + echo " Hosting panels such as HestiaCP set this. Remove them from disable_functions in the PHP CLI configuration (the PHP-FPM one can stay as it is) and re-run." >&2 + if [ -n "$scan_dir" ]; then + echo " Panel upgrades may rewrite php.ini; a file such as ${scan_dir}/99-easyengine.ini that sets disable_functions without them keeps the change." >&2 + fi + fi + if [ -n "$missing" ]; then + echo "ERROR: The PHP CLI ($(command -v php)) lacks functions EasyEngine needs:${missing}. Enable the PHP extensions that provide them (e.g. pcntl, posix, curl) and re-run." >&2 + fi + exit 1 +} + function create_swap() { ee_log_info2 "Enabling 1GiB swap" EE_SWAP_FILE="/ee-swapfile" @@ -467,9 +528,11 @@ function check_dependencies() { fi setup_host_dependencies - setup_docker setup_php setup_php_extensions + # Before Docker, so a refused host is left without it. + check_php_functions + setup_docker check_swap } From 8cfec0f27844bf937aa7807caf81abaa5d20a59b Mon Sep 17 00:00:00 2001 From: Riddhesh Sanghvi Date: Thu, 29 Oct 2026 06:18:25 +0530 Subject: [PATCH 2/4] test(php): cover the PHP function check Bats cases for the report parsing, the messages, PHP 7's warn-only pcntl case and the check's place in check_dependencies, plus real-PHP cases when php is installed. Host prep now asserts on every matrix distro that the stock PHP passes and that a HestiaCP-style disable_functions file in the CLI scan dir is refused with the functions and the file named. --- tests/README.md | 6 +- tests/integration/host-prep.sh | 19 ++++++ tests/unit/php_functions.bats | 120 +++++++++++++++++++++++++++++++++ 3 files changed, 142 insertions(+), 3 deletions(-) create mode 100644 tests/unit/php_functions.bats diff --git a/tests/README.md b/tests/README.md index 6b0d752..29766d3 100644 --- a/tests/README.md +++ b/tests/README.md @@ -19,7 +19,7 @@ bounded **before** the phar download/pull and stubs `docker`, exercising only th OS-version–dependent surface that has actually regressed in the past: ``` -check_dependencies → setup_host_dependencies → setup_php → setup_php_extensions +check_dependencies → setup_host_dependencies → setup_php → setup_php_extensions → check_php_functions ``` PHP selection in particular can only be validated against real distro repos + @@ -74,8 +74,8 @@ functions to drive the algorithm through every branch deterministically. - `ee_select_and_install_php` — native vs PPA, version-first ordering, no-candidate. - `download_and_install_easyengine` — checksum match / mismatch / empty phar / empty checksum / download failure. -- Host-prep integration — php + the three required extensions install across - Ubuntu 22.04/24.04/26.04/rolling/devel and Debian 12/13. +- `check_php_functions` — functions disabled via `disable_functions` (comma- or space-separated) or missing from the build, PHP 7's warn-only pcntl case, the messages, and its place in `check_dependencies` (after PHP setup, before Docker). +- Host-prep integration — php + the three required extensions install across Ubuntu 22.04/24.04/26.04/rolling/devel and Debian 12/13, the functions ee calls are usable, and `check_php_functions` refuses a HestiaCP-style CLI `disable_functions`. ## Not yet covered (candidate follow-ups) diff --git a/tests/integration/host-prep.sh b/tests/integration/host-prep.sh index 90cae88..922b689 100755 --- a/tests/integration/host-prep.sh +++ b/tests/integration/host-prep.sh @@ -74,6 +74,25 @@ for ext in curl sqlite3 zip; do check "php extension loaded: $ext" bash -c "php -m | grep -qiE '^${ext}\$'" done +# The functions ee calls are usable on a stock distro PHP; check_php_functions exits on failure, hence the subshell. +in_subshell() { ( "$@" ); } +check "required PHP functions are available" in_subshell check_php_functions + +# A HestiaCP-style CLI disable_functions must be refused with the file named; a scan-dir file overrides php.ini. +scan_dir="$(php -r 'echo PHP_CONFIG_FILE_SCAN_DIR;')" +if [ -n "$scan_dir" ] && [ -d "$scan_dir" ]; then + panel_ini="$scan_dir/99-ee-test-panel.ini" + echo "disable_functions = pcntl_alarm,pcntl_fork,pcntl_signal,pcntl_signal_dispatch" > "$panel_ini" + panel_out="$( (check_php_functions) 2>&1 )" + panel_rc=$? + rm -f "$panel_ini" + check "disabled PHP functions are refused" test "$panel_rc" -ne 0 + check "the error names the disabled functions" grep -qF "disables functions EasyEngine needs: pcntl_signal pcntl_signal_dispatch." <<< "$panel_out" + check "the error names the ini file" grep -qF "$panel_ini" <<< "$panel_out" +else + fail "PHP CLI has no config scan dir to test disable_functions with" +fi + # Base host dependency installed earlier in check_dependencies(). if ! command -v sqlite3 >/dev/null 2>&1; then apt-get install -y sqlite3 >/dev/null 2>&1 || true diff --git a/tests/unit/php_functions.bats b/tests/unit/php_functions.bats new file mode 100644 index 0000000..6e30854 --- /dev/null +++ b/tests/unit/php_functions.bats @@ -0,0 +1,120 @@ +#!/usr/bin/env bats +# +# check_php_functions() — refuses a PHP CLI that disables or lacks functions ee calls (e.g. HestiaCP's pcntl_* list), which otherwise only fails on the first `ee site` command. + +setup() { + source "${BATS_TEST_DIRNAME}/../helpers/common.bash" + # Resolve a real php before the stub dir shadows it (empty when none). + REAL_PHP="$(command -v php || true)" + export REAL_PHP + stub_init + export EE_QUIET_OUTPUT="" + load_functions +} + +teardown() { + stub_cleanup +} + +# php stub that prints the report lines from $PHP_REPORT and exits $PHP_EXIT. +stub_php_report() { + make_stub php <<'EOF' +#!/usr/bin/env bash +printf '%b' "${PHP_REPORT:-}" +exit "${PHP_EXIT:-0}" +EOF +} + +@test "passes when every required function is available" { + stub_php_report + PHP_REPORT="" run check_php_functions + [ "$status" -eq 0 ] + [[ "$output" != *ERROR* ]] +} + +@test "passes when only an unrelated ini line is reported" { + stub_php_report + PHP_REPORT='ini /etc/php/8.3/cli/php.ini\n' run check_php_functions + [ "$status" -eq 0 ] +} + +@test "fails and names disabled functions, the ini file and a durable override" { + stub_php_report + PHP_REPORT='disabled pcntl_signal\ndisabled pcntl_signal_dispatch\nini /etc/php/8.3/cli/php.ini\nscandir /etc/php/8.3/cli/conf.d\n' run check_php_functions + [ "$status" -eq 1 ] + [[ "$output" == *"disables functions EasyEngine needs: pcntl_signal pcntl_signal_dispatch."* ]] + [[ "$output" == *"disable_functions in: /etc/php/8.3/cli/php.ini"* ]] + [[ "$output" == *"/etc/php/8.3/cli/conf.d/99-easyengine.ini"* ]] +} + +@test "only warns about pcntl functions PHP 7 disables" { + stub_php_report + PHP_REPORT='degraded pcntl_signal\n' run check_php_functions + [ "$status" -eq 0 ] + [[ "$output" == *"disables pcntl_signal; EasyEngine can't roll back"* ]] + [[ "$output" != *ERROR* ]] +} + +@test "points at php --ini when no file could be identified" { + stub_php_report + PHP_REPORT='disabled exec\n' run check_php_functions + [ "$status" -eq 1 ] + [[ "$output" == *"(see php --ini)"* ]] +} + +@test "fails and names functions missing from the PHP build" { + stub_php_report + PHP_REPORT='missing pcntl_signal\n' run check_php_functions + [ "$status" -eq 1 ] + [[ "$output" == *"lacks functions EasyEngine needs: pcntl_signal."* ]] + [[ "$output" != *"disables functions"* ]] +} + +@test "fails when the PHP CLI cannot run" { + stub_php_report + PHP_EXIT=255 run check_php_functions + [ "$status" -eq 1 ] + [[ "$output" == *"Could not run the PHP CLI"* ]] +} + +@test "skips the check when no php is installed" { + PATH="$STUB_DIR:/usr/bin/missing" run check_php_functions + [ "$status" -eq 0 ] +} + +# The rest run the real PHP code against a real php, when one is available. +stub_real_php() { # stub_real_php + if [ -z "$REAL_PHP" ]; then + skip "no php on this host" + fi + printf '#!/bin/sh\nexec %s -d "disable_functions=%s" "$@"\n' "$REAL_PHP" "$1" | make_stub php +} + +@test "real php: detects functions disabled via disable_functions" { + # PHP splits the list on commas and whitespace. + stub_real_php "pcntl_alarm, curl_exec exec" + run check_php_functions + [ "$status" -eq 1 ] + [[ "$output" == *"disables functions EasyEngine needs: curl_exec exec."* ]] +} + +@test "real php: passes when nothing required is disabled" { + stub_real_php "" + # shellcheck disable=SC2016,SC2086 + if ! php -r 'foreach (array_slice($argv, 1) as $f) { if (!function_exists($f)) { exit(1); } }' -- $EE_PHP_REQUIRED_FUNCTIONS; then + skip "this php lacks an extension ee needs (e.g. curl, pcntl, posix)" + fi + run check_php_functions + [ "$status" -eq 0 ] +} + +@test "check_dependencies checks PHP functions after PHP setup and before Docker" { + CALLS="" + for fn in ee_apt_update setup_host_dependencies setup_php setup_php_extensions check_php_functions setup_docker check_swap; do + eval "$fn() { CALLS+=\" $fn\"; }" + done + export EE_LINUX_DISTRO=Debian + printf '#!/bin/sh\nexit 0\n' | make_stub sqlite3 + check_dependencies >/dev/null + [[ "$CALLS" == *"setup_php_extensions check_php_functions setup_docker"* ]] +} From 9de36d6fc38b43757fe3e15b735aeb87342c0407 Mon Sep 17 00:00:00 2001 From: Riddhesh Sanghvi Date: Thu, 29 Oct 2026 06:20:44 +0530 Subject: [PATCH 3/4] fix(php): word the disabled-functions error neutrally The error no longer assumes a hosting panel set disable_functions. It names the functions and ini files, says why EasyEngine needs them, and suggests a conf.d override that a tool rewriting php.ini on upgrade leaves alone, or editing php.ini directly. When a conf.d file itself disables them, the override would not win, so it asks to edit the listed files instead. Continuation lines use ee_log_verbose instead of hand-typed indentation. --- functions | 39 ++++++++++++++++++++++------------ tests/integration/host-prep.sh | 17 ++++++++------- tests/unit/php_functions.bats | 25 ++++++++++++++++------ 3 files changed, 54 insertions(+), 27 deletions(-) diff --git a/functions b/functions index 4b81b55..db80e62 100644 --- a/functions +++ b/functions @@ -420,7 +420,7 @@ function check_php_functions() { return 0 fi - local report kind name disabled="" missing="" degraded="" ini_files="" scan_dir="" + local report kind name disabled="" missing="" degraded="" ini_files="" scan_dir="" where # PHP 7 only warns when a disabled function is called, so disabled pcntl_* just loses rollback on signals there. # shellcheck disable=SC2016,SC2086 # PHP code, not shell; one argument per function if ! report="$(php -r ' @@ -443,31 +443,44 @@ function check_php_functions() { while read -r kind name; do case "$kind" in - disabled) disabled+=" $name" ;; - missing) missing+=" $name" ;; - degraded) degraded+=" $name" ;; - ini) ini_files+=" $name" ;; + disabled) disabled+="${disabled:+, }$name" ;; + missing) missing+="${missing:+, }$name" ;; + degraded) degraded+="${degraded:+, }$name" ;; + ini) ini_files+="${ini_files:+, }$name" ;; scandir) scan_dir="$name" ;; esac done <<< "$report" if [ -n "$degraded" ]; then - ee_log_warn "The PHP CLI disables${degraded}; EasyEngine can't roll back a site operation interrupted by a signal (on PHP 8 this is a fatal error)." + ee_log_warn "The PHP CLI disables ${degraded}; EasyEngine can't roll back a site operation interrupted by a signal (on PHP 8 this is a fatal error)." fi if [ -z "$disabled" ] && [ -z "$missing" ]; then return 0 fi if [ -n "$disabled" ]; then - echo "ERROR: The PHP CLI ($(command -v php)) disables functions EasyEngine needs:${disabled}." >&2 - echo " They are listed in disable_functions in:${ini_files:- (see php --ini)}" >&2 - echo " Hosting panels such as HestiaCP set this. Remove them from disable_functions in the PHP CLI configuration (the PHP-FPM one can stay as it is) and re-run." >&2 - if [ -n "$scan_dir" ]; then - echo " Panel upgrades may rewrite php.ini; a file such as ${scan_dir}/99-easyengine.ini that sets disable_functions without them keeps the change." >&2 - fi + echo "ERROR: The PHP CLI ($(command -v php)) disables functions EasyEngine needs: ${disabled}." >&2 + { + if [ -n "$ini_files" ]; then + ee_log_verbose "disable_functions is set in: ${ini_files}" + else + ee_log_verbose "disable_functions is set in one of the files that php --ini lists." + fi + ee_log_verbose "EasyEngine calls these functions directly and can't run without them." + # A conf.d override only wins over files that load before it, so not when conf.d itself disables them. + if [ -n "$scan_dir" ] && [[ ", $ini_files" != *", $scan_dir/"* ]]; then + ee_log_verbose "To fix it, create ${scan_dir}/99-easyengine.ini that sets disable_functions to the same list without these functions." + ee_log_verbose "It overrides php.ini and is kept when a tool rewrites php.ini on upgrade. Editing php.ini directly also works." + else + if [ -n "$ini_files" ]; then where="the file(s) above"; else where="that file"; fi + ee_log_verbose "To fix it, remove them from disable_functions in ${where}." + fi + ee_log_verbose "Only the PHP CLI configuration needs the change; PHP-FPM can stay as it is. Then re-run the installer." + } >&2 fi if [ -n "$missing" ]; then - echo "ERROR: The PHP CLI ($(command -v php)) lacks functions EasyEngine needs:${missing}. Enable the PHP extensions that provide them (e.g. pcntl, posix, curl) and re-run." >&2 + echo "ERROR: The PHP CLI ($(command -v php)) lacks functions EasyEngine needs: ${missing}." >&2 + ee_log_verbose "Install or enable the PHP extensions that provide them (e.g. pcntl, posix, curl), then re-run the installer." >&2 fi exit 1 } diff --git a/tests/integration/host-prep.sh b/tests/integration/host-prep.sh index 922b689..7d294f2 100755 --- a/tests/integration/host-prep.sh +++ b/tests/integration/host-prep.sh @@ -81,14 +81,15 @@ check "required PHP functions are available" in_subshell check_php_functions # A HestiaCP-style CLI disable_functions must be refused with the file named; a scan-dir file overrides php.ini. scan_dir="$(php -r 'echo PHP_CONFIG_FILE_SCAN_DIR;')" if [ -n "$scan_dir" ] && [ -d "$scan_dir" ]; then - panel_ini="$scan_dir/99-ee-test-panel.ini" - echo "disable_functions = pcntl_alarm,pcntl_fork,pcntl_signal,pcntl_signal_dispatch" > "$panel_ini" - panel_out="$( (check_php_functions) 2>&1 )" - panel_rc=$? - rm -f "$panel_ini" - check "disabled PHP functions are refused" test "$panel_rc" -ne 0 - check "the error names the disabled functions" grep -qF "disables functions EasyEngine needs: pcntl_signal pcntl_signal_dispatch." <<< "$panel_out" - check "the error names the ini file" grep -qF "$panel_ini" <<< "$panel_out" + hardened_ini="$scan_dir/99-ee-test-hardening.ini" + echo "disable_functions = pcntl_alarm,pcntl_fork,pcntl_signal,pcntl_signal_dispatch" > "$hardened_ini" + hardened_out="$( (check_php_functions) 2>&1 )" + hardened_rc=$? + rm -f "$hardened_ini" + check "disabled PHP functions are refused" test "$hardened_rc" -ne 0 + check "the error names the disabled functions" grep -qF "disables functions EasyEngine needs: pcntl_signal, pcntl_signal_dispatch." <<< "$hardened_out" + check "the error names the ini file" grep -qF "$hardened_ini" <<< "$hardened_out" + check "the error says to edit that file" grep -qF "remove them from disable_functions in the file(s) above." <<< "$hardened_out" else fail "PHP CLI has no config scan dir to test disable_functions with" fi diff --git a/tests/unit/php_functions.bats b/tests/unit/php_functions.bats index 6e30854..6761818 100644 --- a/tests/unit/php_functions.bats +++ b/tests/unit/php_functions.bats @@ -38,13 +38,24 @@ EOF [ "$status" -eq 0 ] } -@test "fails and names disabled functions, the ini file and a durable override" { +@test "fails and names disabled functions, the ini file and a conf.d override" { stub_php_report PHP_REPORT='disabled pcntl_signal\ndisabled pcntl_signal_dispatch\nini /etc/php/8.3/cli/php.ini\nscandir /etc/php/8.3/cli/conf.d\n' run check_php_functions [ "$status" -eq 1 ] - [[ "$output" == *"disables functions EasyEngine needs: pcntl_signal pcntl_signal_dispatch."* ]] - [[ "$output" == *"disable_functions in: /etc/php/8.3/cli/php.ini"* ]] - [[ "$output" == *"/etc/php/8.3/cli/conf.d/99-easyengine.ini"* ]] + [[ "$output" == *"ERROR: The PHP CLI ("*") disables functions EasyEngine needs: pcntl_signal, pcntl_signal_dispatch."* ]] + [[ "$output" == *" disable_functions is set in: /etc/php/8.3/cli/php.ini"* ]] + [[ "$output" == *"create /etc/php/8.3/cli/conf.d/99-easyengine.ini that sets disable_functions to the same list without these functions."* ]] + [[ "$output" == *"Editing php.ini directly also works."* ]] + [[ "$output" == *"Then re-run the installer."* ]] +} + +@test "suggests editing the files when a conf.d file disables the functions" { + stub_php_report + PHP_REPORT='disabled exec\nini /etc/php/8.3/cli/php.ini\nini /etc/php/8.3/cli/conf.d/99-zz-hardening.ini\nscandir /etc/php/8.3/cli/conf.d\n' run check_php_functions + [ "$status" -eq 1 ] + [[ "$output" == *"disable_functions is set in: /etc/php/8.3/cli/php.ini, /etc/php/8.3/cli/conf.d/99-zz-hardening.ini"* ]] + [[ "$output" == *"remove them from disable_functions in the file(s) above."* ]] + [[ "$output" != *99-easyengine.ini* ]] } @test "only warns about pcntl functions PHP 7 disables" { @@ -59,7 +70,8 @@ EOF stub_php_report PHP_REPORT='disabled exec\n' run check_php_functions [ "$status" -eq 1 ] - [[ "$output" == *"(see php --ini)"* ]] + [[ "$output" == *"disable_functions is set in one of the files that php --ini lists."* ]] + [[ "$output" == *"remove them from disable_functions in that file."* ]] } @test "fails and names functions missing from the PHP build" { @@ -67,6 +79,7 @@ EOF PHP_REPORT='missing pcntl_signal\n' run check_php_functions [ "$status" -eq 1 ] [[ "$output" == *"lacks functions EasyEngine needs: pcntl_signal."* ]] + [[ "$output" == *"Install or enable the PHP extensions that provide them"* ]] [[ "$output" != *"disables functions"* ]] } @@ -95,7 +108,7 @@ stub_real_php() { # stub_real_php stub_real_php "pcntl_alarm, curl_exec exec" run check_php_functions [ "$status" -eq 1 ] - [[ "$output" == *"disables functions EasyEngine needs: curl_exec exec."* ]] + [[ "$output" == *"disables functions EasyEngine needs: curl_exec, exec."* ]] } @test "real php: passes when nothing required is disabled" { From de5cfd9600e0c1ba3918ed58f4eba0322f72823b Mon Sep 17 00:00:00 2001 From: Riddhesh Sanghvi Date: Thu, 29 Oct 2026 06:55:52 +0530 Subject: [PATCH 4/4] fix(php): shorten the PHP function check errors to four lines Drop the line repeating that EasyEngine needs the functions and fold the conf.d override and the php.ini alternative into one line, so every variant of the disabled-functions error is at most 4 lines. The missing-functions error uses the same "To fix it" wording. Tests assert the line counts. --- functions | 8 +++----- tests/integration/host-prep.sh | 1 + tests/unit/php_functions.bats | 19 +++++++++++++++---- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/functions b/functions index db80e62..2298efd 100644 --- a/functions +++ b/functions @@ -466,21 +466,19 @@ function check_php_functions() { else ee_log_verbose "disable_functions is set in one of the files that php --ini lists." fi - ee_log_verbose "EasyEngine calls these functions directly and can't run without them." # A conf.d override only wins over files that load before it, so not when conf.d itself disables them. if [ -n "$scan_dir" ] && [[ ", $ini_files" != *", $scan_dir/"* ]]; then - ee_log_verbose "To fix it, create ${scan_dir}/99-easyengine.ini that sets disable_functions to the same list without these functions." - ee_log_verbose "It overrides php.ini and is kept when a tool rewrites php.ini on upgrade. Editing php.ini directly also works." + ee_log_verbose "To fix it, create ${scan_dir}/99-easyengine.ini that sets disable_functions to the same list without these functions, or edit php.ini directly." else if [ -n "$ini_files" ]; then where="the file(s) above"; else where="that file"; fi ee_log_verbose "To fix it, remove them from disable_functions in ${where}." fi - ee_log_verbose "Only the PHP CLI configuration needs the change; PHP-FPM can stay as it is. Then re-run the installer." + ee_log_verbose "Only the PHP CLI configuration needs the change, not PHP-FPM. Then re-run the installer." } >&2 fi if [ -n "$missing" ]; then echo "ERROR: The PHP CLI ($(command -v php)) lacks functions EasyEngine needs: ${missing}." >&2 - ee_log_verbose "Install or enable the PHP extensions that provide them (e.g. pcntl, posix, curl), then re-run the installer." >&2 + ee_log_verbose "To fix it, install or enable the PHP extensions that provide them (e.g. pcntl, posix, curl) for the PHP CLI. Then re-run the installer." >&2 fi exit 1 } diff --git a/tests/integration/host-prep.sh b/tests/integration/host-prep.sh index 7d294f2..7b4aeb9 100755 --- a/tests/integration/host-prep.sh +++ b/tests/integration/host-prep.sh @@ -90,6 +90,7 @@ if [ -n "$scan_dir" ] && [ -d "$scan_dir" ]; then check "the error names the disabled functions" grep -qF "disables functions EasyEngine needs: pcntl_signal, pcntl_signal_dispatch." <<< "$hardened_out" check "the error names the ini file" grep -qF "$hardened_ini" <<< "$hardened_out" check "the error says to edit that file" grep -qF "remove them from disable_functions in the file(s) above." <<< "$hardened_out" + check "the error is at most 4 lines" test "$(sed -n '/^ERROR: /,$p' <<< "$hardened_out" | wc -l)" -le 4 else fail "PHP CLI has no config scan dir to test disable_functions with" fi diff --git a/tests/unit/php_functions.bats b/tests/unit/php_functions.bats index 6761818..a203e3d 100644 --- a/tests/unit/php_functions.bats +++ b/tests/unit/php_functions.bats @@ -25,6 +25,13 @@ exit "${PHP_EXIT:-0}" EOF } +# The error, from its ERROR: line on, spans exactly $1 lines. +assert_error_lines() { + local n + n="$(sed -n '/^ERROR: /,$p' <<< "$output" | wc -l)" + [ "$n" -eq "$1" ] +} + @test "passes when every required function is available" { stub_php_report PHP_REPORT="" run check_php_functions @@ -44,9 +51,9 @@ EOF [ "$status" -eq 1 ] [[ "$output" == *"ERROR: The PHP CLI ("*") disables functions EasyEngine needs: pcntl_signal, pcntl_signal_dispatch."* ]] [[ "$output" == *" disable_functions is set in: /etc/php/8.3/cli/php.ini"* ]] - [[ "$output" == *"create /etc/php/8.3/cli/conf.d/99-easyengine.ini that sets disable_functions to the same list without these functions."* ]] - [[ "$output" == *"Editing php.ini directly also works."* ]] - [[ "$output" == *"Then re-run the installer."* ]] + [[ "$output" == *"create /etc/php/8.3/cli/conf.d/99-easyengine.ini that sets disable_functions to the same list without these functions, or edit php.ini directly."* ]] + [[ "$output" == *"Only the PHP CLI configuration needs the change, not PHP-FPM. Then re-run the installer."* ]] + assert_error_lines 4 } @test "suggests editing the files when a conf.d file disables the functions" { @@ -56,6 +63,7 @@ EOF [[ "$output" == *"disable_functions is set in: /etc/php/8.3/cli/php.ini, /etc/php/8.3/cli/conf.d/99-zz-hardening.ini"* ]] [[ "$output" == *"remove them from disable_functions in the file(s) above."* ]] [[ "$output" != *99-easyengine.ini* ]] + assert_error_lines 4 } @test "only warns about pcntl functions PHP 7 disables" { @@ -72,6 +80,7 @@ EOF [ "$status" -eq 1 ] [[ "$output" == *"disable_functions is set in one of the files that php --ini lists."* ]] [[ "$output" == *"remove them from disable_functions in that file."* ]] + assert_error_lines 4 } @test "fails and names functions missing from the PHP build" { @@ -79,8 +88,10 @@ EOF PHP_REPORT='missing pcntl_signal\n' run check_php_functions [ "$status" -eq 1 ] [[ "$output" == *"lacks functions EasyEngine needs: pcntl_signal."* ]] - [[ "$output" == *"Install or enable the PHP extensions that provide them"* ]] + [[ "$output" == *"To fix it, install or enable the PHP extensions that provide them"* ]] + [[ "$output" == *"Then re-run the installer."* ]] [[ "$output" != *"disables functions"* ]] + assert_error_lines 2 } @test "fails when the PHP CLI cannot run" {