php-version-manager 1.3.1

A blazing fast, zero-configuration PHP version manager
# PVM (PHP Version Manager)

[![Build Status](https://github.com/WebProject-xyz/php-version-manager/actions/workflows/release.yml/badge.svg)](https://github.com/WebProject-xyz/php-version-manager/actions/workflows/release.yml)
[![License: GPL v3](https://img.shields.io/badge/License-GPL_v3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
[![Crates.io Version](https://img.shields.io/crates/v/php-version-manager)](https://crates.io/crates/php-version-manager)

Native, blazing fast, zero-configuration PHP version manager for Arch Linux and other Linux/macOS environments, heavily inspired by [fnm](https://github.com/Schniz/fnm).

PVM uses pre-compiled static PHP CLI binaries from [Static PHP CLI (SPC)](https://dl.static-php.dev/) to completely bypass compilation times and library dependency hell on Linux.

## Features
- 🚀 **Blazing Fast**: Written in Rust natively. Execution means zero overhead compared to Docker wrappers.
-**Zero Configuration**: Auto-switches PHP versions based on `.php-version` files.
- 📦 **Static Binaries**: No compilation needed. The `pvm install` command instantly downloads self-contained executables with most common extensions pre-baked.
- 🧩 **Multi-Package Selection**: Pick which packages to install per version — `cli`, `fpm`, and/or `micro` (`micro.sfx`) — via an interactive MultiSelect prompt. `cli` is the default.
- 🐘 **Native Composer Support**: Works out of the box with your system's global Composer without any explicit proxy or configuration.
- 🖱️ **Interactive TUI Menus**: Run `pvm` without arguments to launch a master selection menu. Or run commands like `pvm use` / `pvm ls-remote` / `pvm uninstall` without parameters to select actions via a visual UI.
- 🏷️ **Smart Aliasing**: Install and use patches cleanly by saying `pvm install 8.4`. PVM dynamically figures out the highest patch (`8.4.18`) underneath the hood.
- 🔄 **Patch Update Check**: `pvm use 8.4` notices when a newer patch (e.g. `8.4.19`) is available and offers to install and switch in one step.
- 📝 **`.php-version` Bootstrap**: `pvm init` interactively picks a major.minor and writes `.php-version` for the current directory.
- 🗑️ **Clean Uninstall**: `pvm uninstall` (alias `rm` / `remove`) removes a version's binaries; warns when removing the active one.
- 🐚 **Multi-Shell**: Bash, Zsh, and Fish wrappers generated by `pvm env`, with concurrency-safe per-PID env files locked via `fs4`.
-**Cached Cloud Resolution**: Quickly check for new versions on `dl.static-php.dev` under lightning-fast 24-hour JSON caching.

## Installation

We provide an automatic install script that detects your platform exactly like `fnm` and downloads the pre-compiled native `pvm` binary directly from GitHub Releases into `~/.local/share/pvm/bin`, and then instructs you how to append the hook to your profile.

**Using a script (macOS/Linux)**

```bash
curl -fsSL https://raw.githubusercontent.com/WebProject-xyz/php-version-manager/main/install.sh | bash
```

**Building from Source**
If you prefer to compile the application from scratch using Rust:

```bash
git clone git@github.com:WebProject-xyz/php-version-manager.git
cd php-version-manager
chmod +x build.sh
./build.sh
```

## Usage

```bash
# Enter the master interactive TUI menu
pvm

# Install a specific PHP version (by minor alias or fully-qualified).
# Opens a MultiSelect to pick packages: cli (default), fpm, micro.
pvm install 8.4          # alias: pvm i 8.4

# Install the absolute latest version available
pvm install latest

# Use a version in the current shell.
# Auto-prompts to install + switch if a newer patch exists upstream.
pvm use 8.4

# List all local installed versions alongside their specific aliases
pvm ls                   # alias for: pvm list

# Interactively view and install available cloud versions
pvm ls-remote            # alias: pvm list-remote

# Print the currently active PHP version
pvm current

# Remove an installed version (interactive picker if no arg).
pvm uninstall 8.3        # aliases: pvm rm 8.3 / pvm remove 8.3

# Write a .php-version file for this directory (interactive picker)
pvm init

# Check for and apply updates to pvm itself
pvm self-update          # optional: pvm self-update --apply to apply automatically
```

### Auto-Switching
If you run `pvm init` or manually create a `.php-version` file in a project directory containing `8.3`, PVM will automatically switch to your best local `8.3.x` patch when you `cd` into that folder. The `cd` hook is installed via `pvm env` (Bash, Zsh, or Fish — auto-detected from `$SHELL`).

## Packages

Each PHP version can ship up to three binaries; you pick which during `pvm install` via the MultiSelect prompt. All land under `$PVM_DIR/versions/<full-semver>/bin/`. Upstream reference for all three SAPIs: [static-php.dev — SAPI Reference](https://static-php.dev/en/guide/sapi-reference.html).

| Package | Binary | What it is |
|---------|--------|------------|
| `cli` (default) | `php` | Standard command-line PHP — runs scripts, REPL via `php -a`, drives Composer. See [SAPI Reference: CLI]https://static-php.dev/en/guide/sapi-reference.html#cli. |
| `fpm` | `php-fpm` | FastCGI Process Manager for serving PHP behind nginx/Caddy/Apache. Setup details in the next section + [SAPI Reference: FPM]https://static-php.dev/en/guide/sapi-reference.html#fpm. |
| `micro` | `micro.sfx` | [phpmicro]https://github.com/easysoft/phpmicro self-contained executable stub — concat with a `.php` or `.phar` to ship a single-file PHP app. Combining requires the upstream [`spc`]https://static-php.dev/en/guide/getting-started.html toolchain (`spc micro:combine app.phar --output=app`); pvm only delivers the stub. See [SAPI Reference: Micro]https://static-php.dev/en/guide/sapi-reference.html#micro. |

After `pvm use <version>`, every selected binary is on `$PATH` (CLI as `php`, FPM as `php-fpm`); `micro.sfx` stays at its absolute path since it's a build artifact, not something you invoke directly.

## Running PHP-FPM

> Upstream reference: [static-php.dev — SAPI Reference: FPM]https://static-php.dev/en/guide/sapi-reference.html#fpm documents the binary's CLI flags (`-y`, `-c`, `-t`), a minimal `php-fpm.conf`, and an nginx FastCGI block. The guide below extends that with service wiring (systemd / launchd) and pvm-specific paths.

PVM downloads a static `php-fpm` binary alongside `php` when you tick the `fpm` package during `pvm install`. The static-php-cli tarball ships only the binary — no `php-fpm.conf`, no pool files, no init script — so you wire those up yourself. The binary lives next to the CLI at:

```text
$PVM_DIR/versions/<full-semver>/bin/php-fpm
```

`$PVM_DIR` defaults to `~/.local/share/pvm`. After running `pvm use 8.4` it is also on `$PATH` as plain `php-fpm`.

### 1. Install the fpm package

```bash
pvm install 8.4
# When the MultiSelect prompt appears, tick "fpm" (and "cli" if you want both).
pvm use 8.4
php-fpm -v       # confirm it resolves to the pvm-managed binary
which php-fpm    # → ~/.local/share/pvm/versions/8.4.x/bin/php-fpm
```

### 2. Create a minimal config

Put these under `~/.config/php-fpm/` (any path works — the binary takes `-y` and `-c`):

`~/.config/php-fpm/php-fpm.conf`:

```ini
[global]
pid = /tmp/php-fpm.pid
error_log = /tmp/php-fpm.log
daemonize = no

include = /home/YOU/.config/php-fpm/pool.d/*.conf
```

`~/.config/php-fpm/pool.d/www.conf`:

```ini
[www]
user = YOU
group = YOU
listen = 127.0.0.1:9000
; or a unix socket:
; listen = /tmp/php-fpm-www.sock
; listen.owner = YOU
; listen.group = YOU
; listen.mode = 0660

pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3

catch_workers_output = yes
clear_env = no
```

Replace `YOU` with your username (`whoami`).

### 3. Run it in the foreground

```bash
# Validate config first
php-fpm -y ~/.config/php-fpm/php-fpm.conf -t

# Foreground run, logs to stdout
php-fpm -y ~/.config/php-fpm/php-fpm.conf -F

# With a custom php.ini (the static binary has no compiled-in ini path)
php-fpm -c ~/.config/php-fpm/php.ini -y ~/.config/php-fpm/php-fpm.conf -F
```

Flag summary (matches upstream [SAPI Reference: FPM](https://static-php.dev/en/guide/sapi-reference.html#fpm)):

- `-y <file>``php-fpm.conf` path (required, no default for static builds)
- `-c <file>``php.ini` path (optional; without it, fpm runs with hard-coded defaults)
- `-t` — validate config and exit
- `-F` — stay in foreground (don't fork to daemon)
- `-v` — print version
- `-m` — list compiled-in extensions

### 4. Run it as a service

**systemd (Linux, user unit)** — `~/.config/systemd/user/php-fpm.service`:

```ini
[Unit]
Description=PHP-FPM (managed by pvm)
After=network.target

[Service]
Type=simple
ExecStart=%h/.local/share/pvm/versions/8.4.18/bin/php-fpm -y %h/.config/php-fpm/php-fpm.conf -F
Restart=on-failure

[Install]
WantedBy=default.target
```

```bash
systemctl --user daemon-reload
systemctl --user enable --now php-fpm
journalctl --user -u php-fpm -f
```

Pin the full semver in `ExecStart` (e.g. `8.4.18`) — symlinking to `versions/8.4` is not maintained by pvm, so a future `pvm install 8.4` that resolves to `8.4.19` will not move the service.

**launchd (macOS)** — `~/Library/LaunchAgents/dev.pvm.php-fpm.plist`:

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
  "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key><string>dev.pvm.php-fpm</string>
  <key>ProgramArguments</key>
  <array>
    <string>/Users/YOU/.local/share/pvm/versions/8.4.18/bin/php-fpm</string>
    <string>-y</string>
    <string>/Users/YOU/.config/php-fpm/php-fpm.conf</string>
    <string>-F</string>
  </array>
  <key>RunAtLoad</key><true/>
  <key>KeepAlive</key><true/>
  <key>StandardOutPath</key><string>/tmp/php-fpm.out.log</string>
  <key>StandardErrorPath</key><string>/tmp/php-fpm.err.log</string>
</dict>
</plist>
```

```bash
launchctl load ~/Library/LaunchAgents/dev.pvm.php-fpm.plist
```

### 5. Hook up nginx

```nginx
location ~ \.php$ {
    fastcgi_pass   127.0.0.1:9000;
    fastcgi_index  index.php;
    fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
    include        fastcgi_params;
}
```

### Notes

- The static binary is self-contained — no system libphp / no extension `.so` files. Run `php-fpm -m` to list the extensions baked into your build.
- Switching the active CLI via `pvm use 8.3` does **not** restart your fpm service; the service runs whichever absolute path you wired into the unit/plist. Bump the path and reload when you upgrade.
- For multiple parallel versions (e.g. 8.3 + 8.4), run two services on different ports/sockets — `pvm` does not multiplex fpm for you.