Install and switch PHP versions on Windows (CMD + PowerShell) and Linux (bash/zsh), without admin rights.
# Download both files (phpvm.ps1 + install.ps1) to the same folder, then:
.\install.ps1Restart your terminal. No admin required.
curl -fsSL https://raw.githubusercontent.com/devhardiyanto/phpvm/main/linux/install.sh | bash
source ~/.bashrcOr manually:
git clone https://github.com/devhardiyanto/phpvm.git ~/.phpvm-src
source ~/.phpvm-src/linux/phpvm.shRemoves ~/.phpvm and the phpvm entry from your shell config (PATH on Windows,
the # phpvm source block on Linux). It does not touch anything else.
.\uninstall.ps1 # removes everything (asks to confirm)
.\uninstall.ps1 -KeepVersions # keep built PHP versions
.\uninstall.ps1 -Yes # no promptcurl -fsSL https://raw.githubusercontent.com/devhardiyanto/phpvm/main/linux/uninstall.sh | bash -s -- --yes
# or, from a clone:
bash linux/uninstall.sh # removes everything (asks to confirm)
bash linux/uninstall.sh --keep-versions # keep built PHP versionsBy default the uninstaller removes all built PHP versions too; pass
--keep-versions / -KeepVersions to retain them. The phpvm function stays
loaded in the current shell until you restart it.
phpvm install 8.3.0 # install a specific PHP version
phpvm install 8.3 # install latest 8.3.x patch (auto-resolves)
phpvm install 8 # install latest 8.x patch (e.g. 8.5.x)
phpvm install 7.4 # works for older lines (7.x, 5.x)
phpvm use 8.3.0 # switch active version
phpvm list # list installed versions
phpvm current # show active version
phpvm uninstall 8.1.29 # remove a version
phpvm which # path to active php binary
phpvm ini # open php.ini in editorA successful phpvm install automatically activates the freshly installed
version, so you can skip a separate phpvm use for the common case.
Drop a .phpvmrc file in your project root containing the PHP version you want:
echo "8.3" > .phpvmrcThen run phpvm auto from anywhere in the project — phpvm walks up to find the nearest .phpvmrc and prepends that version to your shell PATH (session only, your global phpvm use is untouched).
For hands-off switching, install the PowerShell prompt hook:
phpvm hook install # adds a snippet to $PROFILE
# restart PowerShell, then `cd` between projects - phpvm auto-switches per directory
phpvm hook status # check whether the hook is installed
phpvm hook uninstall # remove the hook.phpvmrc accepts a full semver (8.3.0), a major.minor (8.3 — picks the highest installed patch), or a leading v (v8.3.0). Lines starting with # are comments. If the version is not installed locally, phpvm warns but never auto-installs.
Same .phpvmrc file works on Linux — the format is identical.
echo "8.3" > .phpvmrc
phpvm auto # one-shot switch from the current directoryFor automatic switching on every cd, enable the shell hook:
phpvm hook enable # writes $PHPVM_DIR/.auto-hook flag
exec $SHELL # or restart your terminalphpvm registers the hook in the shell it detects:
| Shell | Mechanism |
|---|---|
| zsh | add-zsh-hook chpwd _phpvm_auto — runs on every directory change |
| bash | PROMPT_COMMAND="_phpvm_auto -s; ..." — runs before every prompt |
Manage with phpvm hook status / phpvm hook disable. Because phpvm.sh is already sourced into your shell rc, there is no separate file edit step — the hook activates the next time the shell loads.
# List / inspect
phpvm ext list # all bundled extensions (ON/OFF)
phpvm ext loaded # currently loaded (php -m)
phpvm ext info redis # details about an extension
# Enable/disable bundled extensions (edit php.ini)
phpvm ext enable mbstring
phpvm ext enable pdo_mysql
phpvm ext enable curl
phpvm ext enable zip
phpvm ext disable pdo_sqlite
# Install PECL extensions
phpvm ext install redis
phpvm ext install imagick
phpvm ext install mongodb
phpvm ext install mongodb 1.17.0 # specific version
# XDebug (Windows: from xdebug.org | Linux: via PECL)
phpvm ext install xdebugOne command enables the extensions a typical Laravel app needs:
phpvm ext laravel # full preset: minimal + intl, gd, opcache, pdo_pgsql, Redis (PECL)
phpvm ext laravel minimal # required only: openssl, pdo_mysql, mbstring, tokenizer, xml, ctype, fileinfo, bcmath, curl, zip, sodium
phpvm ext laravel full # explicit full (same as bare `phpvm ext laravel`)Already-loaded extensions are reported as already ON and skipped. On Linux, extensions that aren't built into the active PHP are skipped with a not built into this PHP note rather than failed.
phpvm composer # installs a single global composer that follows the active PHP versionThe installer signature is verified against composer.github.io/installer.sig (SHA-384) before execution. Composer is installed once — composer.phar in ~/.phpvm/ and a shim in ~/.phpvm/bin/ (on PATH) that runs whatever PHP is active. Switch versions with phpvm use <other> and the same composer keeps working; no need to re-run phpvm composer. (Composer 2.x requires PHP ≥ 7.2.5, so an extremely old active version won't run the latest composer.)
phpvm fix-ini # rewrites extension_dir to match PHP's compiled-in pathUseful when extension_dir was set by a previous install or copied from another machine. On Linux, the value comes from PHP_EXTENSION_DIR; on Windows, from $VERSIONS_DIR\<ver>\ext.
- Downloads PHP binaries from windows.php.net
- Stores versions in
%USERPROFILE%\.phpvm\versions\<version>\ - Switches via a directory junction (
mklink /J) — no admin needed - Extensions installed from windows.php.net PECL
- XDebug fetched directly from xdebug.org
- Builds PHP from source (php.net)
- Stores versions in
~/.phpvm/versions/<version>/ - Switches via symlink (
~/.phpvm/current) prepended to$PATH - Extensions installed via
pecl, enabled via per-versionconf.d/drop-ins phpvm composer/phpvm fix-ini/phpvm ext laravelwork the same as Windows since 1.7.0
Run phpvm deps to print the install command for your distro.
Ubuntu / Debian:
sudo apt-get install -y \
build-essential autoconf bison re2c pkg-config \
libxml2-dev libsqlite3-dev libssl-dev libcurl4-openssl-dev \
libonig-dev libzip-dev zlib1g-dev libreadline-dev \
libpng-dev libjpeg-dev libwebp-dev libfreetype6-dev \
libgmp-dev libmysqlclient-dev libpq-devphpvm/
├── windows/
│ ├── phpvm.ps1 # main script
│ ├── install.ps1 # installer
│ └── uninstall.ps1 # uninstaller
├── linux/
│ ├── phpvm.sh # main script (sourced in .bashrc)
│ ├── install.sh # curl installer
│ └── uninstall.sh # curl uninstaller
└── README.md
~/.phpvm/
├── versions/
│ ├── 8.3.0/ # Windows: php.exe lives here
│ │ # Linux: bin/php lives here
│ └── 8.1.29/
├── current -> versions/8.3.0 (junction on Windows, symlink on Linux)
├── cache/ # Linux: cached source tarballs
├── composer.phar # global Composer (follows the active version)
├── bin/ # on PATH: composer shim (+ phpvm.cmd / phpvm.ps1 on Windows)
├── phpvm.ps1 # Windows: main script
└── phpvm.sh # Linux: main script (sourced in .bashrc)
| Variable | Default | Description |
|---|---|---|
PHPVM_DIR |
~/.phpvm |
phpvm home directory |
EDITOR |
nano |
Editor used by phpvm ini (Linux) |
PHPVM_SKIP_HASH |
unset | When set to 1, skip SHA-256 verification on Windows installs (use for content-rewriting corporate proxies) |
PHPVM_NO_UPDATE_CHECK |
unset | When set, skip the daily phpvm update check |
PHPVM_AUTO_ACTIVE |
unset | Internal: tracks the version currently pinned by phpvm auto in the current shell |
| Platform | Shell | Status |
|---|---|---|
| Windows 10/11 | CMD | ✅ |
| Windows 10/11 | PowerShell 5+ | ✅ |
| Ubuntu 20.04+ | bash / zsh | ✅ |
| Debian 11+ | bash / zsh | ✅ |
| Fedora / RHEL | bash / zsh | ✅ |
| Arch Linux | bash / zsh | ✅ |
MIT