psy/psysh
PsySH is a runtime developer console, interactive debugger, and REPL for PHP. Inspect variables, evaluate expressions, and debug live code from your terminal, with configurable themes, history, and integrations like Laravel Tinker.
What this package actually is
PsySH is not a Laravel component; it's a standalone, framework-agnostic PHP REPL/debugger that exposes an embeddable library surface (Psy\Shell, Psy\Configuration, Psy\ExecutionLoopClosure, plus a pluggable command and matcher system). Laravel relevance comes almost entirely from the fact that laravel/tinker is a thin wrapper around PsySH — this is the engine, not the Laravel adapter.
Architecture fit
Configuration, inject a bound scope (setBoundObject, setData), customize the startup banner, history path, and add your own Command subclasses. This is exactly the shape needed for a project-specific artisan console.Integration feasibility
High. The path is well-trodden: Tinker proves the embedding model works inside Laravel's container/bootstrap. A custom Artisan command that boots Psy\Shell with a preconfigured Configuration is a few dozen lines. Custom commands and completions are supported first-class (CommandArgumentCompletionAware as of 0.12.22). No private API use is required for the standard embedding.
Technical risk
trustProject mitigates rather than removes it.config/copy, deprecated multiline string format, ShellOutput decoupling). Treat every minor as potentially breaking.opportunity: 54.44 score suggests limited differentiation upside — the package is table stakes you inherit, not a lever you invest in.Key questions for engineering
laravel/tinker, and is it ≥ 0.12.19 / 0.11.23?artisan tinker-equivalent to more than a named admin group?Stack fit
laravel/tinker as a require-dev dependency and inherit PsySH transitively. Adding psy/psysh directly as a top-level requirement is only justified if you are embedding the Shell yourself.php artisan app:console), construct Psy\Configuration in its handle(), point setConfigDir() at storage_path() (never the project root — this is the CVE's blast radius), and set setHistoryFile() to a per-developer path.Migration path
composer show psy/psysh across all applications and confirm every resolved version is at or above the patched releases. This is a one-line fix and the highest-value action in the whole assessment.App::environment('local') plus an explicit opt-in flag) so the console command is not registered in deployed environments. Make this a code-level control, not a documentation convention.$app/tenant context, a handful of read-only helper commands.useExperimentalReadline off until it is the shipped default.Compatibility
execute() calls, Composer proxy scripts) are a known edge-case area; expect to test --no-interaction explicitly.Sequencing
Patch/pin (day 1) → environment gate (week 1) → optional custom console (only if a named stakeholder wants it) → revisit readline after it becomes default. Everything after step 2 is discretionary.
Maintenance
Command subclasses are your maintenance liability; keep them few, read-only, and documented.Support
help, doc, and ls cover most questions. Expect recurring tickets about history not persisting, tab completion misbehaving, and rendering artifacts in unusual terminals — not about application behavior.Scaling
trusted_projects.json. On shared hosts, dev containers with mounted home directories, or CI runners, these can collide or leak prior commands. Scope them to ephemeral per-run paths in CI.Failure modes
| Failure | Impact | Mitigation |
|---|---|---|
| Unpatched version + untrusted CWD (CVE-2026-25129 class) | Arbitrary code execution | Pin ≥ 0.12.19 / 0.11.23; set config dir to storage_path() |
| Command exposed in production | Full DB/app access via eval | Environment gate in code; no public route to the console |
| No TTY (CI, Docker, cron) | Hangs or degrades confusingly | Test --no-interaction; ensure commands are only reachable interactively |
| History file writable/shared | Secret and query leakage | Per-developer path; exclude from backups and image layers |
| Minor version behavior change | Custom commands break silently | Caret pin + changelog review on upgrade |
| Experimental readline enabled | Rendering/input bugs across terminals | Leave disabled until default |
Ramp-up
php artisan tinker and existing knowledge transfer.Configuration, Shell, and custom commands; longer to be confident with the readline/renderer internals, which are mid-refactor.How can I help you explore Laravel packages today?