An uncatchable fatal ends the session and loses all state. The common way to hit it is loading a file twice:
phunkie > :load helpers.php
// file helpers.php loaded
phunkie > :load helpers.php
Error: Cannot redeclare greet()
$
installDiagnosticsRendering() (src/Repl/ReplLoop.php) already renders these cleanly via a shutdown handler instead of dumping a raw stack trace, but formatting is all it can do: E_ERROR and the compile-time fatals reach neither try/catch nor a custom error handler. Surviving one means each evaluation running somewhere the REPL can outlive.
Prior exploration ruled out the psysh-style "child evaluates, returns state" model. Session state cannot cross a process boundary: ReplSession->variables is an ImmMap backed by SplObjectStorage holding closures, resources, PDO handles and Function1 (which wraps ReflectionFunction), none of which serialize. State is also split, with variables and user functions on the immutable session while classes, interfaces, traits and enums are eval'd into the global runtime with nothing replayable recorded.
Three models remain:
A. Supervisor plus replay log (recommended). A thin supervisor spawns the REPL as a worker via proc_open, and logs each successfully evaluated input as source. On a fatal it prints the error, respawns, silently replays the log to rebuild state, and carries on. Survives every fatal and the log is plain strings, so nothing needs serializing. Cost: side effects re-run once during the rare recovery, and it must be gated off the in-process test harness (DirectReplManager).
B. Supervisor plus restart fresh. Survives fatals but resets the session to empty. Least work, weakest recovery.
C. Prevent rather than isolate. Detect the redeclaration before eval so :load twice is a clean error with zero loss. Solves the reported pain and nothing else.
PHP 8.5 shrinks the problem slightly: a missing trait is now a catchable Error rather than a fatal, so that is one fewer way to lose a session.
Whichever model is chosen, verification is the same: :load a file twice, then confirm the REPL is still alive and prior variables and classes survived.
An uncatchable fatal ends the session and loses all state. The common way to hit it is loading a file twice:
installDiagnosticsRendering()(src/Repl/ReplLoop.php) already renders these cleanly via a shutdown handler instead of dumping a raw stack trace, but formatting is all it can do:E_ERRORand the compile-time fatals reach neithertry/catchnor a custom error handler. Surviving one means each evaluation running somewhere the REPL can outlive.Prior exploration ruled out the psysh-style "child evaluates, returns state" model. Session state cannot cross a process boundary:
ReplSession->variablesis anImmMapbacked bySplObjectStorageholding closures, resources, PDO handles andFunction1(which wrapsReflectionFunction), none of which serialize. State is also split, with variables and user functions on the immutable session while classes, interfaces, traits and enums areeval'd into the global runtime with nothing replayable recorded.Three models remain:
A. Supervisor plus replay log (recommended). A thin supervisor spawns the REPL as a worker via
proc_open, and logs each successfully evaluated input as source. On a fatal it prints the error, respawns, silently replays the log to rebuild state, and carries on. Survives every fatal and the log is plain strings, so nothing needs serializing. Cost: side effects re-run once during the rare recovery, and it must be gated off the in-process test harness (DirectReplManager).B. Supervisor plus restart fresh. Survives fatals but resets the session to empty. Least work, weakest recovery.
C. Prevent rather than isolate. Detect the redeclaration before
evalso:loadtwice is a clean error with zero loss. Solves the reported pain and nothing else.PHP 8.5 shrinks the problem slightly: a missing trait is now a catchable
Errorrather than a fatal, so that is one fewer way to lose a session.Whichever model is chosen, verification is the same:
:loada file twice, then confirm the REPL is still alive and prior variables and classes survived.