Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

## [Unreleased]

### Added

- **`latest-remote` keyword:** Install the newest available remote PHP version directly from package manager repos with `phpvm install latest-remote`.
- **`latest-available` alias:** Alias for `latest-remote`.

## [v1.11.0](https://github.com/Thavarshan/phpvm/compare/v1.10.0...v1.11.0) - 2026-04-13

### Added
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ PHP 8.1.13
- Seamlessly switch between installed PHP versions.
- **Run commands with a specific PHP version** without globally switching (`phpvm exec`, `phpvm run`).
- **List available remote PHP versions** from your system package manager (`phpvm ls-remote`).
- **Install the latest available remote PHP version** with `phpvm install latest-remote`.
- **Resolve version descriptors and aliases** to installed version numbers (`phpvm resolve`).
- Auto-switch PHP versions based on project `.phpvmrc` (configurable depth via `PHPVM_PHPVMRC_MAX_DEPTH`).
- Automatic directory-based switching via built-in cd hook (`PROMPT_COMMAND` for bash, `chpwd` for zsh).
Expand Down Expand Up @@ -120,6 +121,7 @@ If the installation was successful, it should output the path to `phpvm`.
| `phpvm auto` | Auto-switch based on `.phpvmrc` file |
| `phpvm list` or `phpvm ls` | List all installed PHP versions |
| `phpvm ls-remote [pattern]` | List available remote PHP versions |
| `phpvm install latest-remote` | Install the latest remote PHP version available |
| `phpvm resolve <version\|alias>` | Resolve a version descriptor to an installed version |
| `phpvm alias [name] [ver]` | Create, update, or list version aliases |
| `phpvm unalias <name>` | Remove version alias |
Expand Down
187 changes: 105 additions & 82 deletions phpvm.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

# shellcheck disable=SC2155 # Allow declare and assign on same line for better readability

PHPVM_VERSION="1.11.0"
PHPVM_VERSION="1.12.0"

# Test mode flag
PHPVM_TEST_MODE="${PHPVM_TEST_MODE:-false}"
Expand Down Expand Up @@ -1016,6 +1016,17 @@ phpvm_resolve_version() {
fi
fi

if [ "$input" = "latest-remote" ] || [ "$input" = "latest-available" ]; then
if resolved=$(phpvm_get_latest_remote_version); then
printf '%s\n' "$resolved"
return 0
else
phpvm_err "No remote PHP versions found."
phpvm_warn "List available versions with: phpvm ls-remote"
return "$PHPVM_EXIT_NOT_FOUND"
fi
fi

# SECURITY: Only check alias file if input passes alias name validation
# This prevents path traversal attacks like "../../../etc/passwd"
if phpvm_validate_alias_name "$input" && [ -f "$PHPVM_DIR/alias/$input" ]; then
Expand Down Expand Up @@ -1092,6 +1103,95 @@ phpvm_get_latest_installed_version() {
return 1
}

# Get available remote PHP versions from package manager repositories
phpvm_get_remote_versions() {
local pattern="$1"
local remote_versions=""

if phpvm_is_test_mode; then
remote_versions="7.4
8.0
8.1
8.2
8.3
8.4"
else
case "$PKG_MANAGER" in
brew)
remote_versions=$(
brew formulae 2> /dev/null | command grep -E '^php(@[0-9]+\.[0-9]+)?$' | while IFS= read -r formula; do
if [ "$formula" = "php" ]; then
printf '%s\n' "$(brew_php_major_minor 2> /dev/null || printf '%s' 'latest')"
else
printf '%s\n' "${formula#php@}"
fi
done | command sort -t. -k1,1n -k2,2n
)
;;
apt)
remote_versions=$(
apt-cache search '^php[0-9]' 2> /dev/null |
command grep -oE 'php[0-9]+\.[0-9]+-cli' |
command sed 's/php//;s/-cli//' |
command sort -t. -k1,1n -k2,2n -u
)
;;
dnf)
remote_versions=$(
dnf module list php 2> /dev/null |
command grep -oE '[0-9]+\.[0-9]+' |
command sort -t. -k1,1n -k2,2n -u
)
;;
yum)
remote_versions=$(
yum list available 'php*-cli' 2> /dev/null |
command grep -oE 'php[0-9]+-php-cli' |
command sed 's/php//;s/-php-cli//' |
command sed 's/\([0-9]\)\([0-9]\)/\1.\2/' |
command sort -t. -k1,1n -k2,2n -u
)
;;
pacman)
remote_versions=$(
pacman -Ss '^php' 2> /dev/null |
command grep -oE 'php[0-9]*\s+[0-9]+\.[0-9]+' |
command grep -oE '[0-9]+\.[0-9]+' |
command sort -t. -k1,1n -k2,2n -u
)
;;
*)
return 1
;;
esac
fi

if [ -n "$pattern" ] && [ -n "$remote_versions" ]; then
remote_versions=$(printf '%s\n' "$remote_versions" | command grep -F "$pattern" || true)
fi

printf '%s\n' "$remote_versions"
}

# Get latest available remote PHP version
phpvm_get_latest_remote_version() {
local remote_versions
local latest

remote_versions=$(phpvm_get_remote_versions) || return 1
if [ -z "$remote_versions" ]; then
return 1
fi

latest=$(printf '%s\n' "$remote_versions" | phpvm_latest_from_list)
if [ -n "$latest" ]; then
printf '%s\n' "$latest"
return 0
fi

return 1
}

# Check if Remi repository is available/enabled for RHEL/Fedora systems
check_remi_repository() {
# Check if Remi repository is installed
Expand Down Expand Up @@ -2530,6 +2630,8 @@ Usage:
Version Arguments:
X.Y or X.Y.Z Specific PHP version (e.g., 8.1 or 8.1.15)
latest Latest installed version (not latest available)
latest-remote Latest remote version available via package manager
latest-available Alias for 'latest-remote'
stable Alias for 'latest' (latest installed)
system System default PHP
<alias-name> Any user-defined alias (e.g., 'default', 'prod')
Expand Down Expand Up @@ -2879,89 +2981,10 @@ phpvm_ls_remote() {
local pattern="${1:-}"
local remote_versions=""

# In test mode, return a hardcoded list for testing
if phpvm_is_test_mode; then
remote_versions="7.4
8.0
8.1
8.2
8.3
8.4"
if [ -n "$pattern" ]; then
remote_versions=$(printf '%s\n' "$remote_versions" | command grep -F "$pattern")
fi
if [ -z "$remote_versions" ]; then
phpvm_warn "No PHP versions found matching '$pattern'."
return "$PHPVM_EXIT_NOT_FOUND"
fi
phpvm_echo "Available remote PHP versions:"
printf '%s\n' "$remote_versions" | while IFS= read -r v; do
printf ' %s\n' "$v"
done
return "$PHPVM_EXIT_SUCCESS"
fi

phpvm_echo "Querying available PHP versions from $PKG_MANAGER..."

case "$PKG_MANAGER" in
brew)
# List all php formulae from Homebrew
remote_versions=$(
brew formulae 2> /dev/null | command grep -E '^php(@[0-9]+\.[0-9]+)?$' | while IFS= read -r formula; do
if [ "$formula" = "php" ]; then
printf '%s\n' "$(brew_php_major_minor 2> /dev/null || printf '%s\n' 'latest')"
else
printf '%s\n' "${formula#php@}"
fi
done | command sort -t. -k1,1n -k2,2n
)
;;
apt)
# List PHP versions available from apt repositories
remote_versions=$(
apt-cache search '^php[0-9]' 2> /dev/null |
command grep -oE 'php[0-9]+\.[0-9]+-cli' |
command sed 's/php//;s/-cli//' |
command sort -t. -k1,1n -k2,2n -u
)
;;
dnf)
# List PHP module streams available via dnf
remote_versions=$(
dnf module list php 2> /dev/null |
command grep -oE '[0-9]+\.[0-9]+' |
command sort -t. -k1,1n -k2,2n -u
)
;;
yum)
# List Remi PHP packages available via yum
remote_versions=$(
yum list available 'php*-cli' 2> /dev/null |
command grep -oE 'php[0-9]+-php-cli' |
command sed 's/php//;s/-php-cli//' |
command sed 's/\([0-9]\)\([0-9]\)/\1.\2/' |
command sort -t. -k1,1n -k2,2n -u
)
;;
pacman)
# List PHP packages available via pacman
remote_versions=$(
pacman -Ss '^php' 2> /dev/null |
command grep -oE 'php[0-9]*\s+[0-9]+\.[0-9]+' |
command grep -oE '[0-9]+\.[0-9]+' |
command sort -t. -k1,1n -k2,2n -u
)
;;
*)
remote_versions=$(phpvm_get_remote_versions "$pattern") || {
phpvm_err "ls-remote is not supported for package manager: $PKG_MANAGER"
return "$PHPVM_EXIT_ERROR"
;;
esac

# Apply pattern filter if given
if [ -n "$pattern" ] && [ -n "$remote_versions" ]; then
remote_versions=$(printf '%s\n' "$remote_versions" | command grep -F "$pattern")
fi
}

if [ -z "$remote_versions" ]; then
if [ -n "$pattern" ]; then
Expand Down
17 changes: 17 additions & 0 deletions release-notes-1.12.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# v1.12.0 Release Notes

## Added

- Add `latest-remote` keyword for `phpvm install`, allowing users to install the newest available PHP version from remote package manager repositories.
- Add `latest-available` alias for `latest-remote`.

## Details

- `phpvm install latest-remote`
- `phpvm install latest-available`

## Notes

- This feature works with existing package manager support for Homebrew, apt, dnf, yum, and pacman.
- It complements `phpvm ls-remote`, which lists available remote versions.
- In test mode, the remote version list is simulated for coverage.
18 changes: 18 additions & 0 deletions tests/02_features.bats
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,24 @@ load test_helper
[[ ! "$output" =~ "7.4" ]]
}

@test "phpvm install latest-remote installs newest available PHP version" {
run install_php "latest-remote"
[ "$status" -eq 0 ]
[[ "$output" =~ "Installing PHP 8.4" ]]
[[ "$output" =~ "PHP 8.4 installed." ]]
phpvm_test_php_installed "8.4"
[ "$?" -eq 0 ]
}

@test "phpvm install latest-available also installs newest available remote PHP" {
run install_php "latest-available"
[ "$status" -eq 0 ]
[[ "$output" =~ "Installing PHP 8.4" ]]
[[ "$output" =~ "PHP 8.4 installed." ]]
phpvm_test_php_installed "8.4"
[ "$?" -eq 0 ]
}

# --- resolve command ---

@test "phpvm resolve returns installed version" {
Expand Down
Loading