psy/psysh
PsySH is an interactive PHP REPL and runtime developer console for debugging and exploring code. Inspect variables, run snippets, and get contextual help in a powerful shell, with configuration, themes, and integrations available.
PsySH has a new config command for inspecting and updating runtime-configurable settings during the current session. You can tweak things like pager, theme, verbosity, useSuggestions, useSyntaxHighlighting, clipboardCommand, and semicolonsSuppressReturn without restarting the shell. Fixes #361
There’s also a new copy command for copying the last result ($_) or any expression to your clipboard. Works with system clipboard commands, or via OSC 52 for SSH and remote terminals.
Configure with clipboardCommand or useOsc52Clipboard in your config.
Optionally suppress return values by ending a statement with ;, similar to MATLAB/Octave behavior. Supports a 'double' mode requiring ;; for suppression (if requireSemicolons is also enabled, both true and 'double' require ;;).
'semicolonsSuppressReturn' => true,
'semicolonsSuppressReturn' => 'double', // Always require ;; to suppress
Strings are now valid PHP!
""". The old format is available via useDeprecatedMultilineStrings until the next major release.Providing an exceptionDetails callback via config renders additional context about exceptions (e.g. validation errors) alongside the error message. Fixes #648
A few other improvements:
help layout adapts to terminal width.Legacy readline now shares PsySH’s newer completion engine, which brings much better parity between ext-readline/libedit and experimental interactive readline. Command argument completion, better multiline buffering, and a handful of command-dispatch edge cases now work much more consistently outside experimental readline too.
Commands can now define their own argument completions via CommandArgumentCompletionAware.
New in the experimental interactive readline:
useSyntaxHighlighting if you don't like colors, I guess.psy\info() and --info also report more detail about readline and autocomplete state.
Run psysh with --experimental-readline and try it out. It's getting kind of awesome!
Bare config and copy at the prompt now resolve to PsySH commands before PHP function calls. Prefix ambiguous input with ; to force PHP execution.
use function and use const statements.throw new Exception in PHP 7.4--pager / --no-pager CLI optionspsy\info() / --info output with interactive completionedit command instead of waiting for the editor to closeAdded an experimental interactive readline: a from-scratch pure-PHP readline replacement built specifically for PsySH. Instead of delegating to ext-readline or ext-libedit, this gives PsySH full control over input, editing, completion, and rendering.
This is opt-in and experimental. Default behavior is completely unchanged. Enable it in your config or from the command line:
'useExperimentalReadline' => true,
psysh --experimental-readline
See the interactive readline wiki page for more!
The new completion engine is syntax-aware, type-aware, and runtime-value-aware. It parses your input, resolves types from live objects in scope, and completes based on what your code actually is, not just string matching on symbol names.
Type $user-> and see that object's actual methods and properties. Chain through $repo->find(1)-> and get completions for the return type. Fuzzy matching means asum finds array_sum and stl finds strtolower. Completions show in a navigable multi-column menu.
Press Enter on an incomplete statement and the input continues on the next line with proper indentation. Closing brackets auto-dedent. Shift+Enter always inserts a newline. No more fighting the shell to write a multi-line closure.
'useSuggestions' => true.ext-readline or ext-libedit required. Works with any terminal.This addresses a bunch of long-standing issues: #234, #254, #309, #346, #506, #561, #668, #732, #769, #869.
We'd love your feedback! Give it a try, and let us know what works and what doesn't. The goal is to make this the default. Help us get it there. 🧪
Fixed several edge cases with the Restricted Mode introduced in v0.12.19 where non-interactive contexts (piped input, execute() calls, Composer proxy scripts) could incorrectly trigger trust prompts or restrict trusted functionality.
Fixes #913
Decoupled commands from ShellOutput via a new ShellOutputAdapter, so commands degrade gracefully when used in non-interactive contexts rather than failing on missing shell features.
bin/ to scripts/Fixed a CWD configuration poisoning vulnerability (CVE-2026-25129) where a malicious .psysh.php file in an attacker-writable directory could execute arbitrary code when a victim runs PsySH from that directory. This affects all versions prior to v0.12.19 and v0.11.23, including downstream consumers like Laravel Tinker, when invoked from an attacker-writable CWD.
Fixed in v0.12.19 and v0.11.23. Upgrade ASAP.
PsySH now requires explicit trust before loading project-local config (.psysh.php), local PsySH binaries, or Composer autoloads from untrusted projects. Trust decisions are persisted per-project in trusted_projects.json.
Configure with trustProject:
'trustProject' => 'prompt', // default — ask interactively
'trustProject' => 'always', // trust all projects
'trustProject' => 'never', // always run restricted
Or use --trust-project / --no-trust-project CLI flags, or the PSYSH_TRUST_PROJECT env var.
Non-interactive sessions automatically skip untrusted features with a warning.
Tab completion, ls, doc, and show commands now recognize [@method](https://github.com/method) and [@property](https://github.com/property) docblock tags. Magic members display in magenta so you can tell them apart from real methods and properties.
Inheritance works as expected — magic members from parent classes, interfaces, and traits are included, with child declarations taking precedence.
Also fixes parsing of generic types (e.g., array<int, string>) in docblock tags, which previously broke on whitespace inside angle brackets.
See #905
--cwd to actually change the working directory. Previously it only affected discovery for autoload/config, so relative paths and other directory-dependent behavior didn’t work as expected inside the shell..psysh.php), local PsySH binaries, or Composer autoloads from untrusted projects. Configure with trustProject config option, --trust-project / --no-trust-project CLI flags, or PSYSH_TRUST_PROJECT env var.exit() not working when uopz extension is loadedCtrl-C might be incorrectly handled after exiting)Install the uopz extension (5.0+) and PsySH will automatically reload modified files during your session. Edit code, switch back to PsySH, and your changes are live—no restart needed!
PsySH skips "risky" reloads by default (conditional definitions, static variables). Use the new yolo command to bypass safety checks:
>>> my_helper()
Warning: Skipped conditional: if (...) { function my_helper() ... }
>>> yolo !!
=> "result"
See the documentation for more details.
Ctrl-C — plays nicer with Laravel + PHP 8.5A quick release adding support for Symfony Console v7.4+ and v8.x.
The abbreviated return value output introduced in v0.12.13 has been reverted. This feature attempted to show shorter output for statements that looked like they were trying to take an action (like assignments and method calls with side effects) while preserving full output for inspection-like statements. Unfortunately, this version of the feature just ... wasn't it.
Thanks to everyone who provided feedback. If you've got thoughts on approaches that could make this better, please share them in #512!
You can now update the PHP manual directly from inside the shell! Run doc --update-manual to fetch the latest manual version.
--info and \Psy\info()--update-manual--update-manual with an existing sqlite manualhistory command filtering and --head/--tail interaction to apply filters firstdoc foo when help foo doesn't match a known commandcurl_close() on PHP >= 8.0.0 (thanks @mpesari!)Log user input, command invocations, and executed code to a PSR-3 logger or callback.
// Simple callback
$config->setLogging(function ($kind, $data) {
file_put_contents('/tmp/psysh.log', "[$kind] $data\n", FILE_APPEND);
});
// PSR-3 logger with granular control
$config->setLogging([
'logger' => $psrLogger,
'level' => [
'input' => 'info',
'command' => false, // disable
'execute' => 'debug',
],
]);
This has been one of our longest-requested features (🙈) and it fixes #821, #651, and #565.
This release adds a new PHP-based manual data format (v3), replacing the previously used sqlite db. The new format:
The phar builds now bundle the PHP manual, so these changes work out of the box. There are also automatic update notifications, an an --update-manual command to keep docs current.
If you've already got a manual file installed, remove it then run psysh --update-manual (or psysh --update-manual=LANG) after upgrading!
Fixes #595 and #406.
--help output... pushing the limits of what we can plausibly put in a point release.
Added opt-in autoload warming to improve tab completion and command support. When enabled, PsySH pre-loads classes at startup, making them available to ls, doc, show, and tab completion.
// Enable with defaults (project classes only)
'warmAutoload' => true,
// Advanced configuration
'warmAutoload' => [
'includeTests' => true,
'excludeNamespaces' => ['App\Tests\'],
'includeVendorNamespaces' => ['Symfony\', 'Doctrine\'],
],
Custom warmers can be implemented via AutoloadWarmerInterface. Fixes #650
Auto-adds use statements when you reference a class by its short name (and there's exactly one match in configured namespaces).
'implicitUse' => [
'includeNamespaces' => ['App\'],
'excludeNamespaces' => ['App\Legacy\'],
],
Works great with autoload warming to make class references feel natural.
doc, show, and ls commands now resolve class names using the current namespace and use statements, just like code execution does.
Hitting ctrl-c during long-running code now interrupts execution and returns to the prompt instead of exiting the shell entirely. Works with or without process forking (requires pcntl and posix support). Fixes #154
PsySH now properly handles exit status codes! Use exit(42) for non-zero status codes or exit('message') to print before exiting. Also exits with non-zero status on unrecoverable errors.
Class names, functions, interfaces, constants, and more in ls, doc, and show commands are now clickable links to php.net (requires Symfony 4.3+, PsySH manual installed, and OSC 8 compatible terminal).
--info CLI option--warm-autoload CLI optionClassNamesMatcher tab completionFixing unintentional PHP support version regression due to editor autoformatting :(
Includes the following from v0.12.11:
-> (Thanks @igorsantos07!)Improve editor command when using default system runtime dir.
Resolve type error with the transient readline implementation (Thanks @Fludem!)
Tighten up a bunch of internal types and defaults.
This should have absolutely no impact on your day to day life, but it makes things nicer for me :)
New:
--help output for some command input exceptions (for example, if you're missing an argument, instead of just complaining, show the command usage)readline_info() to tab completion matchers' getMatches methods, allowing more complex matching logic (Thanks @tareqas!)Improved:
E_STRICT deprecation (Thanks @Ayesh!)less is not a symlink, and where PHP error suppression is ignored ... or over-eargly turned into exceptions (Thanks @piurafunk!)It's that time of year. Let's ship a major version of PsySH!
New:
And improved:
PsySH v0.12.0 drops support for PHP versions older than 7.4.
Upgrade already 🙂
Made a bunch of behind the scenes fixes, that you probably won't even notice. Specifically:
New:
strictTypes config option to enforce strict types by default.cli-server SAPI (Thanks @supriyo-biswas!)timeit command.How can I help you explore Laravel packages today?