← All docs

Interactive REPL

Run PHP interactively through eval with persistent state, parser-driven multiline input, and a cached native session host.

Start an interactive session with:

elephc repl

Interactive sessions open with the compiler’s ASCII mascot and a random quote, followed by the session banner and prompt. Use --quiet to hide the welcome output.

Every submission runs through dynamic eval() and Magician. Variables, references, functions, classes, and constants survive between submissions. Each new invocation starts with fresh PHP state.

>>> $value = 10;
int(10)
>>> function twice($n) {
...     return $n * 2;
... }
>>> twice($value)
int(20)

Input and results

Enter PHP fragments without an opening <?php tag. Magician’s lexer and parser recognize incomplete expressions, blocks, strings, and comments and request another line at the ... prompt. Complete statements execute immediately. Paste a whole if/else or try/catch statement together when its first block could stand alone.

Supported expressions and simple variable assignments display their results using var_dump. Other statements run without printing an extra result. A final semicolon may be omitted when adding it completes the grammar. The parser checks candidate forms without executing them; calls and assignments execute once per submission.

Output from echo remains visible even without a trailing newline: the interactive prompt moves to a fresh line when needed. Piped sessions preserve PHP’s exact bytes.

Use arrow keys to edit and recall history. Ctrl-C cancels the input currently being edited, including a pending multiline fragment. Ctrl-D exits. Ctrl-C while PHP is executing follows normal process signal behavior and can terminate the session.

CommandAction
:help, :hShow input help.
:quit, :qEnd the session.

Commands are recognized only when there is no pending multiline fragment.

Errors and PHP behavior

Invalid submissions report a syntax error and return to the prompt. Escaping PHP Throwable values report their class and message and also return to the prompt. An undefined function, such as isseet() instead of isset(), raises a catchable Error naming the function and leaves the session available for the next command. The diagnostic preserves the resolved name’s original capitalization, including namespaces and use function imports. This also applies when creating a callable with StrLenX(...); function lookup remains case-insensitive. Errors returned by the eval interpreter also abort only the current submission. This includes unknown constants such as das, unsupported operations, invalid nested eval source, and trigger_error(..., E_USER_ERROR). The REPL reports the available diagnostic and accepts the next command. An internal eval failure may still have a generic message such as Error: eval() runtime failed.

Effects that happened before the error remain visible. Recovery does not roll back or replay previous commands. It applies only at the outer submission boundary; nested eval does not continue executing the failed command.

Explicit exit() ends the session. Recovery cannot safely resume after a native process crash, a runtime operation that exits directly, memory exhaustion, an internal panic, or a failed ABI safety check. Ordinary compiled programs keep their existing eval error behavior.

The REPL supports the same language and builtin surface as dynamic eval. Namespace and use declarations apply to their submitted fragment, following eval semantics. The variable $__elephc_repl_error and the __elephc_repl_* helper functions are reserved for session management.

Relative file operations use the invoking working directory. __DIR__ initially names that directory; the logical entry file is .elephc-repl.php, which is not created on disk. Files may be loaded with require or require_once inside the session. Composer PHP files are not automatically compiled into the host.

Configuration and optional capabilities

The command reads the invoking project’s PHP profile and elephc.toml INI settings. Command-line overrides follow normal compiler precedence:

elephc repl --php-version 8.4 --strict-php
elephc repl --ini precision=10 --heap-size=16777216
elephc repl --with-pdo

opcache.restrict_api compares its prefix with .elephc-repl.php under the canonical project directory. An allowing project prefix works without creating that logical file; a nonmatching prefix denies OPcache API calls.

Dynamic input is unavailable to compile-time feature detection. Enable optional capabilities before starting the session. Managed native packages must already be installed, just as for ordinary compilation:

elephc native add pcre2
elephc repl --with-regex

--with-web and monitoring modes are unavailable. opcache.preload is rejected; use require_once through eval instead. The REPL runs on macOS ARM64, Linux ARM64, and Linux x86_64 hosts. Cross-compilation and iOS library output remain ordinary compiler commands.

The complete option list is in the CLI reference.

Cache and history

On first use for a project/configuration, elephc compiles its embedded session bootstrap with the normal native toolchain and links Magician and the selected capabilities. Subsequent launches reuse that executable. Typed PHP is always interpreted through eval, including after a cache hit.

Hosts live under $XDG_CACHE_HOME/elephc/repl, or ~/.cache/elephc/repl when XDG_CACHE_HOME is unset. The existing temporary-cache fallback applies if neither XDG_CACHE_HOME nor HOME is available. Configuration entries account for the compiler build, host architecture, working directory, PHP profile, project files, INI settings, and selected capabilities. Receipts track the exact linked archives, their identities, and executable integrity. Changed compiler or dependency inputs and damaged executables trigger a rebuild. Concurrent first launches share a build lock, and only completed executables are published.

The native assembler/linker and matching bridge archives are needed to build a missing or invalidated host. A valid cache hit starts the existing executable. Deleting a REPL cache entry causes it to be rebuilt on its next use.

Interactive history is stored in repl/history under the same private cache root, with at most 1,000 entries. Lines beginning with a space are omitted. Use --no-history to disable history loading and persistence. --quiet hides the banner and cache-build notice while keeping interactive prompts.

Piped sessions

Pipes and redirected files have no banner, prompts, or persistent history:

printf '$n = 20;\n$n * 2\n' | elephc repl

Submissions still share one scope and use the same multiline parser. Input errors and caught exceptions make the final exit status nonzero while allowing later submissions to run. Incomplete input at EOF is reported. PHP exit($code) preserves its requested status. Each accumulated submission is limited to 1 MiB of UTF-8 source and cannot contain literal NUL bytes.

See examples/repl for a small interactive workflow and a PHP file to load into a session.