From 9e28e4994d1b979ba59d18706c6910219b0f2d7c Mon Sep 17 00:00:00 2001 From: Irfan Hardiyanto <52022757+devhardiyanto@users.noreply.github.com> Date: Sun, 26 Jul 2026 10:47:44 +0700 Subject: [PATCH 1/5] fix(linux): join older-patch versions with ", " not a cycling delimiter paste -d takes a *list* of delimiters and cycles through it, so `paste -sd ', '` alternated comma and space: three stale patches printed as "8.3.1,8.3.2 8.3.5". Join on a comma and space it out afterwards. Windows was already correct - Show-OlderPatchHint uses -join ', '. devhardiyanto --- linux/phpvm.sh | 4 +++- tests/linux/commands.bats | 9 +++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/linux/phpvm.sh b/linux/phpvm.sh index fc6b0b5..4ed3c83 100644 --- a/linux/phpvm.sh +++ b/linux/phpvm.sh @@ -625,7 +625,9 @@ _phpvm_older_patch_hint() { [[ -z "$older" ]] && return 0 newest=$(echo "$older" | tail -1) - _dim "Older patch of ${ver%.*} still installed: $(echo "$older" | paste -sd ', ' -)" + # paste -d takes a *list* of delimiters and cycles through it, so ', ' would + # join as "a,b c". Join on a comma, then space it out. + _dim "Older patch of ${ver%.*} still installed: $(echo "$older" | paste -sd, - | sed 's/,/, /g')" _dim "Remove it with: phpvm uninstall $newest" } diff --git a/tests/linux/commands.bats b/tests/linux/commands.bats index 48980ef..1f43f70 100644 --- a/tests/linux/commands.bats +++ b/tests/linux/commands.bats @@ -428,6 +428,15 @@ EOF [[ "$output" == *"phpvm uninstall 8.5.6"* ]] } +@test "older_patch_hint: separates several patches with a comma and a space" { + # paste -d cycles its delimiter list, so ', ' used to join as "a,b c". + mkdir -p "$PHPVM_VERSIONS"/{8.5.1,8.5.2,8.5.6,8.5.8} + run _phpvm_older_patch_hint 8.5.8 + [ "$status" -eq 0 ] + [[ "$output" == *"8.5.1, 8.5.2, 8.5.6"* ]] + [[ "$output" != *"8.5.2 8.5.6"* ]] +} + # ---------- phpvm_doctor ---------- @test "doctor: warns when no active version" { From a2b13f055d8edbe8f09a3a41256205d0130f3b62 Mon Sep 17 00:00:00 2001 From: Irfan Hardiyanto <52022757+devhardiyanto@users.noreply.github.com> Date: Sun, 26 Jul 2026 11:04:10 +0700 Subject: [PATCH 2/5] feat(linux): refuse PHP <8.1 on OpenSSL 3 before building Building PHP 7.3 on a modern distro fails ~10 minutes in with a wall of C errors, the useful one buried mid-log: RSA_SSLV23_PADDING undeclared. OpenSSL removed that constant in 3.0 and php-src only stopped using it in 8.1, so the outcome is knowable before a single file is compiled. Add _phpvm_check_openssl_compat, called right after the dependency check once the version is fully resolved. It reads the OpenSSL version from pkg-config (what ./configure consults) and falls back to the openssl CLI. It fails open: if neither probe answers the build still runs, and PHPVM_SKIP_OPENSSL_CHECK=1 overrides it outright - a guard that cannot be bypassed would strand anyone whose pkg-config resolves an OpenSSL 1.1 this probe can't see. Also surface the first hard error from the build log on any failure, preferring a configure error over later compiler noise. "See log" alone left the user paging through thousands of lines. Ref: https://github.com/php/php-src/issues/9503 devhardiyanto --- linux/phpvm.sh | 63 ++++++++++++++++ tests/linux/build_preflight.bats | 125 +++++++++++++++++++++++++++++++ 2 files changed, 188 insertions(+) create mode 100644 tests/linux/build_preflight.bats diff --git a/linux/phpvm.sh b/linux/phpvm.sh index 4ed3c83..c5d4cff 100644 --- a/linux/phpvm.sh +++ b/linux/phpvm.sh @@ -234,6 +234,51 @@ _phpvm_detect_os() { fi } +# Version of the OpenSSL that ./configure will actually resolve. pkg-config is +# what configure consults, so ask it first; the `openssl` CLI is a fallback and +# can disagree with the installed headers. Echoes "3.0.2"; non-zero if unknown. +_phpvm_openssl_version() { + local v="" + command -v pkg-config &>/dev/null && v=$(pkg-config --modversion openssl 2>/dev/null) + [[ -z "$v" ]] && command -v openssl &>/dev/null && v=$(openssl version 2>/dev/null | awk '{print $2}') + # Strip a letter suffix: OpenSSL 1.1.1w -> 1.1.1 + v="${v%%[a-zA-Z]*}" + [[ -n "$v" ]] || return 1 + echo "$v" +} + +# PHP's ext/openssl references RSA_SSLV23_PADDING, which OpenSSL removed in +# 3.0. php-src only stopped using it in 8.1, so anything older dies partway +# through `make` with a wall of C errors. That is knowable up front, so refuse +# before spending ten minutes compiling. +# https://github.com/php/php-src/issues/9503 +# Escape hatch: PHPVM_SKIP_OPENSSL_CHECK=1 for setups where pkg-config resolves +# an OpenSSL 1.1 that this probe can't see. +_phpvm_check_openssl_compat() { + local ver="$1" + [[ -n "${PHPVM_SKIP_OPENSSL_CHECK:-}" ]] && return 0 + + local major="${ver%%.*}" minor + minor=$(echo "$ver" | cut -d. -f2) + # PHP 8.1+ builds fine against OpenSSL 3. + (( major > 8 )) && return 0 + (( major == 8 && minor >= 1 )) && return 0 + + local ssl + ssl=$(_phpvm_openssl_version) || return 0 # can't tell -> don't block + [[ "${ssl%%.*}" -lt 3 ]] && return 0 + + _err "PHP $ver cannot be built against OpenSSL $ssl." + _dim "OpenSSL 3.0 removed RSA_SSLV23_PADDING; PHP's openssl extension only" + _dim "stopped using it in 8.1, so the build would fail in ext/openssl." + echo "" + _dim "Options:" + _dim " - Install a supported line: phpvm install 8.1 (or newer)" + _dim " - Keep OpenSSL 1.1 as what pkg-config resolves, then re-run with:" + _dim " PHPVM_SKIP_OPENSSL_CHECK=1 phpvm install $ver" + return 1 +} + # ── Check build dependencies ────────────────────────────────────────────────── _phpvm_check_deps() { local missing=() @@ -358,6 +403,22 @@ _phpvm_run_logged() { return $rc } +# A failed build leaves thousands of lines of compiler noise in $PHPVM_LOG, and +# the line that explains it is buried near the middle — "See log" alone makes +# the user page through it. Pull out the first hard error instead. +_phpvm_show_build_error() { + [[ -f "$PHPVM_LOG" ]] || return 0 + + local first + # configure's own failure is the more useful line when both are present. + first=$(grep -m1 -E '^configure: error:' "$PHPVM_LOG" 2>/dev/null) + [[ -z "$first" ]] && first=$(grep -m1 -E '(^|[[:space:]])(fatal )?error:' "$PHPVM_LOG" 2>/dev/null) + [[ -z "$first" ]] && return 0 + + _err "First error in the log:" + _dim " ${first}" +} + # Resolve a partial version to the highest published patch on php.net. # "8" -> latest 8.x (e.g. 8.5.7) # "8.3" -> latest 8.3.x (e.g. 8.3.31) @@ -435,6 +496,7 @@ phpvm_install() { # Check deps before attempting download _phpvm_check_deps || return 1 + _phpvm_check_openssl_compat "$ver" || return 1 # Download source local tarball="php-$ver.tar.gz" @@ -572,6 +634,7 @@ phpvm_install() { _phpvm_run_logged "Installing" make install \ || { _err "Install failed. See log: $PHPVM_LOG"; exit 1; } ) || { + _phpvm_show_build_error rm -rf "$target" return 1 } diff --git a/tests/linux/build_preflight.bats b/tests/linux/build_preflight.bats new file mode 100644 index 0000000..1be9df8 --- /dev/null +++ b/tests/linux/build_preflight.bats @@ -0,0 +1,125 @@ +#!/usr/bin/env bats +# Build preflight: the OpenSSL 3 vs PHP < 8.1 guard and the build-log error +# surfacing. Older-patch hint formatting lives in commands.bats. +# Run from repo root: bats tests/linux/ + +setup() { + export PHPVM_DIR="$BATS_TEST_TMPDIR/phpvm" + export PHPVM_VERSIONS="$PHPVM_DIR/versions" + export PHPVM_CURRENT="$PHPVM_DIR/current" + export PHPVM_LOG="$BATS_TEST_TMPDIR/build.log" + export PHPVM_NO_INIT=1 + export PHPVM_NO_UPDATE_CHECK=1 + unset PHPVM_SKIP_OPENSSL_CHECK + mkdir -p "$PHPVM_VERSIONS" + + # shellcheck disable=SC1091 + . "$BATS_TEST_DIRNAME/../../linux/phpvm.sh" +} + +# Pin the detected OpenSSL version so the result doesn't depend on the host. +_stub_openssl() { + eval "_phpvm_openssl_version() { echo '$1'; }" +} + +# ── OpenSSL compatibility guard ─────────────────────────────────────────────── + +@test "openssl 3 blocks PHP 7.3" { + _stub_openssl 3.0.2 + run _phpvm_check_openssl_compat 7.3.33 + [ "$status" -eq 1 ] + [[ "$output" == *"cannot be built against OpenSSL 3.0.2"* ]] +} + +@test "openssl 3 blocks PHP 7.4 and 8.0" { + _stub_openssl 3.2.1 + run _phpvm_check_openssl_compat 7.4.33 + [ "$status" -eq 1 ] + run _phpvm_check_openssl_compat 8.0.30 + [ "$status" -eq 1 ] +} + +@test "openssl 3 allows PHP 8.1 and newer" { + _stub_openssl 3.0.2 + run _phpvm_check_openssl_compat 8.1.0 + [ "$status" -eq 0 ] + run _phpvm_check_openssl_compat 8.3.10 + [ "$status" -eq 0 ] + run _phpvm_check_openssl_compat 9.0.0 + [ "$status" -eq 0 ] +} + +@test "openssl 1.1 allows old PHP" { + _stub_openssl 1.1.1 + run _phpvm_check_openssl_compat 7.3.33 + [ "$status" -eq 0 ] +} + +@test "undetectable openssl does not block the build" { + eval "_phpvm_openssl_version() { return 1; }" + run _phpvm_check_openssl_compat 7.3.33 + [ "$status" -eq 0 ] +} + +@test "PHPVM_SKIP_OPENSSL_CHECK overrides the guard" { + _stub_openssl 3.0.2 + PHPVM_SKIP_OPENSSL_CHECK=1 run _phpvm_check_openssl_compat 7.3.33 + [ "$status" -eq 0 ] +} + +@test "the block message names the remedy" { + _stub_openssl 3.0.2 + run _phpvm_check_openssl_compat 7.3.33 + [[ "$output" == *"phpvm install 8.1"* ]] + [[ "$output" == *"PHPVM_SKIP_OPENSSL_CHECK=1"* ]] +} + +@test "openssl version probe strips a letter suffix" { + # Only meaningful when the host actually has one of the probes. + if ! command -v pkg-config &>/dev/null && ! command -v openssl &>/dev/null; then + skip "no pkg-config or openssl on this host" + fi + run _phpvm_openssl_version + if [ "$status" -eq 0 ]; then + [[ "$output" =~ ^[0-9]+(\.[0-9]+)*$ ]] + fi +} + +# ── Build log error surfacing ──────────────────────────────────────────────── + +@test "surfaces the first compiler error from the log" { + cat > "$PHPVM_LOG" <<'LOG' +checking for gcc... yes +gcc -c foo.c -o foo.o +ext/openssl/openssl.c:1491:58: error: 'RSA_SSLV23_PADDING' undeclared +make: *** [Makefile:736: ext/openssl/openssl.lo] Error 1 +LOG + run _phpvm_show_build_error + [[ "$output" == *"First error in the log"* ]] + [[ "$output" == *"RSA_SSLV23_PADDING"* ]] +} + +@test "prefers a configure error over later compiler noise" { + cat > "$PHPVM_LOG" <<'LOG' +checking for libxml... no +configure: error: Package requirements (libxml-2.0) were not met +something.c:1:1: error: later noise +LOG + run _phpvm_show_build_error + [[ "$output" == *"libxml-2.0"* ]] + [[ "$output" != *"later noise"* ]] +} + +@test "stays quiet when the log holds no error" { + printf 'all good\nbuild complete\n' > "$PHPVM_LOG" + run _phpvm_show_build_error + [ "$status" -eq 0 ] + [ -z "$output" ] +} + +@test "stays quiet when there is no log at all" { + rm -f "$PHPVM_LOG" + run _phpvm_show_build_error + [ "$status" -eq 0 ] + [ -z "$output" ] +} From ef2a447195ea7f7c7648aa9e7a4639843cf1105f Mon Sep 17 00:00:00 2001 From: Irfan Hardiyanto <52022757+devhardiyanto@users.noreply.github.com> Date: Sun, 26 Jul 2026 11:06:44 +0700 Subject: [PATCH 3/5] test(ci): add a zsh compatibility job phpvm.sh is documented as bash/zsh and registers a chpwd hook for zsh users, but the bats suite runs under bash - so zsh has had zero coverage. Add a smoke script exercising the paths most likely to depend on bash semantics (array indexing in _phpvm_levenshtein, the unquoted $_PHPVM_COMMANDS split in _phpvm_unknown) plus the read-only commands, and wire it into CI. Expected to fail on this commit: proving the gap is the point. The fix follows. devhardiyanto --- .github/workflows/ci.yml | 16 +++++++ tests/linux/zsh-smoke.zsh | 90 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 tests/linux/zsh-smoke.zsh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7d238bd..a951931 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -69,6 +69,22 @@ jobs: - name: Bats (offline) run: bats tests/linux/ + # bats runs under bash, so nothing else covers zsh - even though phpvm.sh is + # documented as bash/zsh and installs a chpwd hook for zsh users. + zsh: + name: zsh compatibility + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + + - name: Install zsh + run: | + sudo apt-get update + sudo apt-get install -y zsh + + - name: zsh smoke + run: zsh tests/linux/zsh-smoke.zsh + # Experimental (B12 spike): does phpvm.sh behave on macOS/BSD userland? # Not a merge gate yet - failures inform the macOS backlog item. macos: diff --git a/tests/linux/zsh-smoke.zsh b/tests/linux/zsh-smoke.zsh new file mode 100644 index 0000000..c38b1df --- /dev/null +++ b/tests/linux/zsh-smoke.zsh @@ -0,0 +1,90 @@ +#!/usr/bin/env zsh +# zsh compatibility smoke for linux/phpvm.sh. +# +# The bats suite runs under bash, so nothing else in this repo exercises zsh - +# yet phpvm.sh is documented as bash/zsh and registers a chpwd hook for zsh +# users. Anything that quietly depends on bash semantics (0-indexed arrays, +# word-splitting on unquoted parameters) only shows up here. +# +# Run from repo root: zsh tests/linux/zsh-smoke.zsh + +emulate -R zsh # no bash/ksh compatibility crutches - test zsh as users get it + +typeset -i fail=0 + +check() { # check + if [[ "$3" == *"$2"* ]]; then + print "ok - $1" + else + print "FAIL - $1" + print " expected to contain: $2" + print " actual: $3" + fail=1 + fi +} + +check_status() { # check_status + if [[ "$3" == "$2" ]]; then + print "ok - $1" + else + print "FAIL - $1 (expected status $2, got $3)" + fail=1 + fi +} + +tmp=$(mktemp -d) +export PHPVM_DIR="$tmp/phpvm" +export PHPVM_NO_UPDATE_CHECK=1 +export PHPVM_NO_INIT=1 +source ./linux/phpvm.sh +unset PHPVM_NO_INIT + +print "-- sourcing --" +check_status "phpvm.sh sources cleanly under zsh" 0 $? + +print "-- levenshtein (array indexing) --" +out=$(_phpvm_levenshtein kitten sitting 2>&1) +check "levenshtein kitten/sitting = 3" "3" "$out" +out=$(_phpvm_levenshtein '' abc 2>&1) +check "levenshtein ''/abc = 3" "3" "$out" +out=$(_phpvm_levenshtein same same 2>&1) +check "levenshtein same/same = 0" "0" "$out" + +print "-- did-you-mean (command-list splitting) --" +out=$(phpvm instal 2>&1) +check "suggests install for 'instal'" "Did you mean 'install'" "$out" +out=$(phpvm doctro 2>&1) +check "suggests doctor for 'doctro'" "Did you mean 'doctor'" "$out" +out=$(phpvm zzzzzzzzzz 2>&1) +check "no suggestion for nonsense" "is not a phpvm command" "$out" + +print "-- basic read-only commands --" +out=$(phpvm version 2>&1) +check "version prints the version" "phpvm" "$out" +out=$(phpvm list 2>&1) +check "list runs without error" "No PHP versions installed" "$out" +out=$(phpvm help 2>&1) +check "help runs" "PHP Version Manager" "$out" +out=$(phpvm current 2>&1) +check "current runs" "No PHP version" "$out" + +print "-- .phpvmrc parsing --" +mkdir -p "$tmp/proj" +print "8.3" > "$tmp/proj/.phpvmrc" +out=$(cd "$tmp/proj" && _phpvm_read_rc "$tmp/proj/.phpvmrc" 2>&1) +check "reads a .phpvmrc version" "8.3" "$out" + +print "-- older-patch hint --" +mkdir -p "$PHPVM_DIR/versions/8.5.1" "$PHPVM_DIR/versions/8.5.2" "$PHPVM_DIR/versions/8.5.8" +out=$(_phpvm_older_patch_hint 8.5.8 2>&1) +check "joins older patches with ', '" "8.5.1, 8.5.2" "$out" + +rm -rf "$tmp" + +print "" +if (( fail )); then + print "zsh smoke: FAILED" + exit 1 +fi +print "zsh smoke: all checks passed" +exit 0 From 9148dd8b674dae784d4ba6ca9bdd810efed80a3e Mon Sep 17 00:00:00 2001 From: Irfan Hardiyanto <52022757+devhardiyanto@users.noreply.github.com> Date: Sun, 26 Jul 2026 11:33:33 +0700 Subject: [PATCH 4/5] fix(linux): make did-you-mean work under zsh The zsh job added in the previous commit went red, as intended. It found three separate bash assumptions, each hidden behind the one before it, all on the `phpvm ` path: 1. _phpvm_levenshtein assigned row[0]. zsh arrays are 1-based and reject that outright - "assignment to invalid subscript range" - so the function printed an error instead of a distance. Shift the indices by one; bash just leaves index 0 unused. 2. `for c in $_PHPVM_COMMANDS` relied on word-splitting an unquoted parameter, which zsh does not do. Every typo was answered with "Did you mean 'install use list ls current ...'" - the entire command list as a single suggestion. Make it a real array. 3. ${a:i-1:1} makes zsh parse ":i" as a history modifier. Spell the offset out as $((i-1)). phpvm.sh:335 already documents this exact trap for the spinner; the levenshtein loop had missed it. Net effect: under zsh, every mistyped command printed an internal error and a nonsense suggestion. bash behaviour is unchanged - 77/77 bats still green. Also fixes two bugs in the smoke script itself: `print "-- x --"` ate the leading dashes as options, and a status check was reading $? from the preceding print rather than from the source. devhardiyanto --- linux/phpvm.sh | 30 ++++++++++++++++++++---------- tests/linux/zsh-smoke.zsh | 27 +++++++++------------------ 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/linux/phpvm.sh b/linux/phpvm.sh index c5d4cff..2270e0d 100644 --- a/linux/phpvm.sh +++ b/linux/phpvm.sh @@ -1589,33 +1589,43 @@ _phpvm_levenshtein() { (( la == 0 )) && { echo "$lb"; return; } (( lb == 0 )) && { echo "$la"; return; } local i j cost prev cur del ins sub min + # Indices are shifted by one so the array starts at 1: zsh arrays are + # 1-based and reject row[0] outright ("assignment to invalid subscript + # range"), while bash simply leaves index 0 unused. local -a row - for (( j = 0; j <= lb; j++ )); do row[j]=$j; done + for (( j = 0; j <= lb; j++ )); do row[j+1]=$j; done for (( i = 1; i <= la; i++ )); do - prev=${row[0]} - row[0]=$i + prev=${row[1]} + row[1]=$i for (( j = 1; j <= lb; j++ )); do - cur=${row[j]} - if [[ "${a:i-1:1}" == "${b:j-1:1}" ]]; then cost=0; else cost=1; fi - del=$(( row[j] + 1 )); ins=$(( row[j-1] + 1 )); sub=$(( prev + cost )) + cur=${row[j+1]} + # ${a:i-1:1} makes zsh read ":i" as a history modifier + # ("unrecognized modifier"). Spell the offset arithmetic out. + if [[ "${a:$((i-1)):1}" == "${b:$((j-1)):1}" ]]; then cost=0; else cost=1; fi + del=$(( row[j+1] + 1 )); ins=$(( row[j] + 1 )); sub=$(( prev + cost )) min=$del (( ins < min )) && min=$ins (( sub < min )) && min=$sub - row[j]=$min + row[j+1]=$min prev=$cur done done - echo "${row[lb]}" + echo "${row[lb+1]}" } # Canonical command list (includes aliases) for suggestions. -_PHPVM_COMMANDS="install use list ls current uninstall remove which doctor ini deps ext composer wp-cli fix-ini auto hook upgrade update version help" +# An array, not a space-separated string: zsh does not word-split unquoted +# parameters, so `for c in $_PHPVM_COMMANDS` handed the whole list over as a +# single candidate and every typo was answered with the entire list. +_PHPVM_COMMANDS=(install use list ls current uninstall remove which doctor ini + deps ext composer wp-cli fix-ini auto hook upgrade update + version help) # Unknown command: suggest the nearest match instead of dumping the full help. _phpvm_unknown() { local cmd="$1" local best="" bestd=99 c d - for c in $_PHPVM_COMMANDS; do + for c in "${_PHPVM_COMMANDS[@]}"; do d=$(_phpvm_levenshtein "$cmd" "$c") (( d < bestd )) && { bestd=$d; best=$c; } done diff --git a/tests/linux/zsh-smoke.zsh b/tests/linux/zsh-smoke.zsh index c38b1df..ec50031 100644 --- a/tests/linux/zsh-smoke.zsh +++ b/tests/linux/zsh-smoke.zsh @@ -23,26 +23,17 @@ check() { # check fi } -check_status() { # check_status - if [[ "$3" == "$2" ]]; then - print "ok - $1" - else - print "FAIL - $1 (expected status $2, got $3)" - fail=1 - fi -} - tmp=$(mktemp -d) export PHPVM_DIR="$tmp/phpvm" export PHPVM_NO_UPDATE_CHECK=1 export PHPVM_NO_INIT=1 -source ./linux/phpvm.sh +if ! source ./linux/phpvm.sh; then + print "FAIL - phpvm.sh could not be sourced under zsh" + exit 1 +fi unset PHPVM_NO_INIT -print "-- sourcing --" -check_status "phpvm.sh sources cleanly under zsh" 0 $? - -print "-- levenshtein (array indexing) --" +print -r -- "-- levenshtein (array indexing) --" out=$(_phpvm_levenshtein kitten sitting 2>&1) check "levenshtein kitten/sitting = 3" "3" "$out" out=$(_phpvm_levenshtein '' abc 2>&1) @@ -50,7 +41,7 @@ check "levenshtein ''/abc = 3" "3" "$out" out=$(_phpvm_levenshtein same same 2>&1) check "levenshtein same/same = 0" "0" "$out" -print "-- did-you-mean (command-list splitting) --" +print -r -- "-- did-you-mean (command-list splitting) --" out=$(phpvm instal 2>&1) check "suggests install for 'instal'" "Did you mean 'install'" "$out" out=$(phpvm doctro 2>&1) @@ -58,7 +49,7 @@ check "suggests doctor for 'doctro'" "Did you mean 'doctor'" "$out" out=$(phpvm zzzzzzzzzz 2>&1) check "no suggestion for nonsense" "is not a phpvm command" "$out" -print "-- basic read-only commands --" +print -r -- "-- basic read-only commands --" out=$(phpvm version 2>&1) check "version prints the version" "phpvm" "$out" out=$(phpvm list 2>&1) @@ -68,13 +59,13 @@ check "help runs" "PHP Version Manager" "$out" out=$(phpvm current 2>&1) check "current runs" "No PHP version" "$out" -print "-- .phpvmrc parsing --" +print -r -- "-- .phpvmrc parsing --" mkdir -p "$tmp/proj" print "8.3" > "$tmp/proj/.phpvmrc" out=$(cd "$tmp/proj" && _phpvm_read_rc "$tmp/proj/.phpvmrc" 2>&1) check "reads a .phpvmrc version" "8.3" "$out" -print "-- older-patch hint --" +print -r -- "-- older-patch hint --" mkdir -p "$PHPVM_DIR/versions/8.5.1" "$PHPVM_DIR/versions/8.5.2" "$PHPVM_DIR/versions/8.5.8" out=$(_phpvm_older_patch_hint 8.5.8 2>&1) check "joins older patches with ', '" "8.5.1, 8.5.2" "$out" From fac52c58f508b1e519c855a5be4116eb7dc983d2 Mon Sep 17 00:00:00 2001 From: Irfan Hardiyanto <52022757+devhardiyanto@users.noreply.github.com> Date: Sun, 26 Jul 2026 11:41:55 +0700 Subject: [PATCH 5/5] chore: bump version to 1.13.1 devhardiyanto --- README.md | 6 ++++++ linux/install.sh | 2 +- linux/phpvm.sh | 2 +- version.txt | 2 +- windows/install.ps1 | 2 +- windows/phpvm.ps1 | 2 +- windows/src/00-header.ps1 | 2 +- 7 files changed, 12 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index b84e477..63d9c1b 100644 --- a/README.md +++ b/README.md @@ -402,8 +402,14 @@ Invoke-Pester -Configuration (New-PesterConfiguration -Hashtable (Import-PowerSh ```bash bats tests/linux/ +zsh tests/linux/zsh-smoke.zsh # bats runs under bash; this covers zsh ``` +`phpvm.sh` targets both bash and zsh, and the two differ in ways that stay +invisible until a zsh user hits them — zsh arrays are 1-based, zsh does not +word-split unquoted parameters, and `${var:i}` is parsed as a history modifier +rather than a substring offset. Run the zsh smoke before touching `phpvm.sh`. + --- ## License diff --git a/linux/install.sh b/linux/install.sh index c4c20ae..23fc745 100644 --- a/linux/install.sh +++ b/linux/install.sh @@ -6,7 +6,7 @@ set -e -PHPVM_VERSION="1.13.0" +PHPVM_VERSION="1.13.1" PHPVM_DIR="${PHPVM_DIR:-$HOME/.phpvm}" PHPVM_REPO="https://raw.githubusercontent.com/devhardiyanto/phpvm/main" diff --git a/linux/phpvm.sh b/linux/phpvm.sh index 2270e0d..b1c3631 100644 --- a/linux/phpvm.sh +++ b/linux/phpvm.sh @@ -10,7 +10,7 @@ # phpvm use 8.3.0 # ============================================================================== -PHPVM_VERSION="1.13.0" +PHPVM_VERSION="1.13.1" PHPVM_DIR="${PHPVM_DIR:-$HOME/.phpvm}" PHPVM_VERSIONS="$PHPVM_DIR/versions" PHPVM_CURRENT="$PHPVM_DIR/current" diff --git a/version.txt b/version.txt index f88cf52..da38e07 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.13.0 \ No newline at end of file +1.13.1 \ No newline at end of file diff --git a/windows/install.ps1 b/windows/install.ps1 index ea7166e..53c843f 100644 --- a/windows/install.ps1 +++ b/windows/install.ps1 @@ -8,7 +8,7 @@ Set-StrictMode -Version Latest $ErrorActionPreference = "Stop" -$PHPVM_VERSION = "1.13.0" +$PHPVM_VERSION = "1.13.1" $PHPVM_DIR = if ($env:PHPVM_DIR) { $env:PHPVM_DIR } else { "$env:USERPROFILE\.phpvm" } $PHPVM_BIN = "$PHPVM_DIR\bin" diff --git a/windows/phpvm.ps1 b/windows/phpvm.ps1 index c389391..8d2c79b 100644 --- a/windows/phpvm.ps1 +++ b/windows/phpvm.ps1 @@ -23,7 +23,7 @@ Set-StrictMode -Version Latest $ErrorActionPreference = "Stop" # -- Constants ----------------------------------------------------------------- -$PHPVM_VERSION = "1.13.0" +$PHPVM_VERSION = "1.13.1" $PHPVM_DIR = if ($env:PHPVM_DIR) { $env:PHPVM_DIR } else { "$env:USERPROFILE\.phpvm" } $VERSIONS_DIR = "$PHPVM_DIR\versions" $CURRENT_LINK = "$PHPVM_DIR\current" diff --git a/windows/src/00-header.ps1 b/windows/src/00-header.ps1 index 52f0bfd..f288432 100644 --- a/windows/src/00-header.ps1 +++ b/windows/src/00-header.ps1 @@ -15,7 +15,7 @@ Set-StrictMode -Version Latest $ErrorActionPreference = "Stop" # -- Constants ----------------------------------------------------------------- -$PHPVM_VERSION = "1.13.0" +$PHPVM_VERSION = "1.13.1" $PHPVM_DIR = if ($env:PHPVM_DIR) { $env:PHPVM_DIR } else { "$env:USERPROFILE\.phpvm" } $VERSIONS_DIR = "$PHPVM_DIR\versions" $CURRENT_LINK = "$PHPVM_DIR\current"