Per-project PHP version switching on Windows — automatic, instant, zero-config.
Drop a .phpversion file in your project, and php / composer use the right version. No manual switching, no batch scripts, no broken PATH.
# Inside project A
~/projects/api $ php -v
PHP 8.3.12 (cli) ...
# Just change directory — Shepherd handles the rest automatically
~/projects/api $ cd ../frontend
~/projects/frontend $ php -v
PHP 8.5.7 (cli) ...Laravel Herd installs multiple PHP versions side by side, but doesn't let you pin a version per project from the CLI. The usual workaround is batch scripts wrapping php.exe — fragile, slow, and broken across terminals.
Shepherd replaces that with a single compiled binary (~2 MB) that acts as a transparent shim for php and composer. It reads .phpversion, resolves the right php.exe from Herd's installations, syncs nginx, and gets out of the way.
Shepherd does not alter your Herd installation or replace its binaries. It sits in your user profile PATH, layers on top of Herd, and falls back to Herd's default behavior when no project configuration is found.
No subshells. No recursion. No race conditions.
- Laravel Herd for Windows
- Windows 10/11 (amd64 or arm64)
- Download the latest release from the releases page
- Run
shp.exe— it detects it's not installed and offers to set everything up:
Shepherd is not installed yet. Install now? [Y/n]
- Restart your terminal, then:
shp use 8.4 # writes .phpversion
php -v # → PHP 8.4.x
composer install # → uses PHP 8.4That's it. The installer places shims (php.exe, composer.exe, shp.exe) in %USERPROFILE%\.config\shepherd\bin, prepends it to your PATH, and broadcasts the change.
CI / non-interactive: Use
shp installin scripts. Interactive prompts are auto-skipped when stdin is not a terminal.
- Reads
.phpversionfrom the current directory, walking up the tree (like.nvmrc) - Resolves the matching
php.exefrom Herd's installs (~/.config/herd/bin/phpXX/) - Falls back to
herd.phar which-phpwhen no dotfile is found - Syncs Herd's nginx config so your
.testdomain matches the CLI version - Execs the real
php.exe— transparent, no wrapper overhead
| Command | Description |
|---|---|
shp use [version] |
Set the PHP version for the current project (latest, auto) |
shp run <ver> -- … |
Run a command with a specific PHP version |
shp which |
Show resolved PHP path and source |
shp current |
Print the resolved PHP version |
shp list |
List available PHP versions |
shp status |
Show configuration overview |
shp xdebug <cmd> |
Toggle/configure xdebug (on, off, debug, coverage, toggle) |
shp ext add <name> |
Install a PHP extension (DLL + deps + ini) |
shp ext list |
List installed extensions |
shp ext remove <n> |
Remove an installed extension |
shp reverb |
Show Laravel Reverb status and .env config |
shp doctor |
Diagnose common setup issues |
shp self-update |
Update to the latest release (SHA256-verified) |
shp install |
Install shims and configure PATH |
shp uninstall |
Remove shims and restore PATH |
shp version |
Show current version |
| Flag | Description |
|---|---|
--verbose |
Extra diagnostic output |
--quiet |
Suppress non-essential output |
--json |
Machine-readable JSON output |
--no-interactive |
Skip prompts (auto-detected in CI) |
Toggle xdebug without editing php.ini manually:
shp xdebug on # enable (mode=debug)
shp xdebug coverage # switch to coverage mode
shp xdebug off # disable
shp xdebug toggle # quick on/offWorks on the PHP version resolved for the current project.
Test compatibility with another PHP version without modifying .phpversion:
shp run 8.3 -- php artisan test
shp run 8.3 -- composer install
shp run latest -- php -vThe resolved version applies only to that command. Your .phpversion stays untouched.
Don't know which version to pin? Let Shepherd figure it out:
shp use autoReads require.php from composer.json, picks the highest installed version that satisfies the constraint, and writes .phpversion. Useful for onboarding onto an existing project.
Install extensions not bundled with Herd — no manual DLL download:
shp ext add redis
shp ext add imagick
shp ext add sqlsrv --php=all # all installed versionsSupported: igbinary, imagick, memcached, pdo_sqlsrv, redis, sqlsrv.
Handles PECL lookup, DLL download, system deps (winget), ini registration, and verification.
List what's installed and remove what you don't need:
shp ext list # show extensions for the current PHP version
shp ext remove redis # remove DLL and php.ini directive
shp ext remove redis --php=allshp self-updateDownloads the latest release, verifies SHA256, and replaces all shims. Releases are signed with cosign — see SECURITY.md.
The binary detects how it was invoked:
| Invoked as | Behavior |
|---|---|
php / php.exe |
Transparent PHP shim |
composer / composer.exe |
Runs composer.phar via resolved PHP |
shp.exe |
Management commands |
One binary, three names — shp install sets them all up.
Point your IDE's PHP interpreter to the Shepherd shim so static analysis matches your terminal:
PhpStorm:
Settings → PHP → CLI Interpreter → ... → Add Local → path:
%USERPROFILE%\.config\shepherd\bin\php.exe
VS Code (Intelephense / PHP Intelephense):
In .vscode/settings.json:
{
"php.validate.executablePath": "${env:USERPROFILE}/.config/shepherd/bin/php.exe"
}The shim resolves the correct PHP version per project, so the IDE always uses the same binary as your terminal.
shp doctorChecks: Herd presence, .phpversion validity, shim installation, PATH order, shell aliases, Developer Mode, CA certificate, nginx config, PHP-CGI ports.
Common fixes:
- Wrong PHP version →
shp statusto check PATH order, thenshp install+ restart terminal - Version not found → install it from the Herd UI
- nginx errors →
shp doctorwill report the file and line
go build -ldflags="-s -w -X main.version=dev" -o shp.exe .
.\shp.exe install