Hi internals,
`php -a` currently provides an interactive shell with tab completion written in
C, in readline/libedit.
This currently has a number of limitations:
- Adding new features or bug fixes would require a time getting familiar with
C programming, PHP's internals and memory management, and with how readline
works internally.
Making it possible to write replace more of the REPL implementation in PHP
instead of C
may open this up to more contributors.
(at the cost of a completion performance slowdown/memory
increase/installation size,
which should be unimportant if only some interactive shells are affected or
if there are few candidates for completion)
- Completions for syntax such as constants, member variables/properties, etc.
(`$x->`) have been a TODO for years.
```
// ext/readline/readline_cli.c
static char *cli_completion_generator(const char *text, int index) /* {{{ */
{
/*
TODO:
- constants
- maybe array keys
- language constructs and other things outside a hashtable (echo, try,
function, class, ...)
- object/class members
- future: respect scope ("php > function foo() { $[tab]" should only expand to
local variables...)
*/
```
So, my ideas for improving the situation in 8.1 or later
- Allow overriding readline_completion_function() in auto_prepend_file
(I think that that not working is a bug, and hope it could get fixed in 8.0)
https://github.com/php/php-src/pull/5872
https://github.com/phan/phan/commit/228827516df606de23e9ac94873c7b953f4bf4c1
(`tool/phan_repl_helpers.php`) includes a prototype
of replacing PHP's C readline completion with a readline callback written in
pure PHP.
(I started working on that file today and it's missing some completion
functionality, but it may be a good starting point)
- Add a hook to `readline` such as
`readline_set_php_result_handler_callback(function(mixed $result, string
$snippet){...})`
that can be used to create a user-defined function to print/process the
result of expressions.
(Create a clone of `zend_eval_stringl` to support that)
(The result value would instead be freed after the callback was called)
Many other REPLs (Read-Eval-Print Loops) that I'm familiar with print a
representation of the result of expressions, but PHP doesn't.
Some values such as $GLOBALS, extremely long binary strings, etc. may need to
have the representation truncated in some way.
A tiny example implementation of `readline_set_php_result_handler_callback`
would be `if ($result !== null) { var_dump($result); }`
- Add a hook such as `readline_before_evaluate_php_snippet(string $snippet) :
bool {}`
that gets called before attempting to evaluate snippets (whether or not they
have parse errors).
Returning true would indicate that the snippet should not be evaluated.
This could be useful for adding meta commands that don't use PHP syntax, such
as `help;` `help SomeClass::someClassElement;`, `cd /dirname`, etc.
- Add some way to provide the contents of all previous lines during `php -a` to
provide better completions for multi-line snippets
(e.g. suggest local variables in multi-line function declarations instead of
global variables)
Finally, it would be useful to have something to tie those together:
- Provide an `iphp`, `phpi`, `php-interactive`, or some other entry point to
prepend a bundled phar script
that uses those hooks to provide a better user experience.
(Analogous to how `ipython` and `irb` start a different interactive shell
than `python` or `ruby`)
Miscellaneous thoughts on implementation details:
- Bundling an actual parser (e.g. https://github.com/nikic/PHP-Parser) would
help in properly analyzing `Foo::$var-><TAB>`
by being less reliant on heuristics (e.g. checking if $var was a variable or
a property, making it easier to collect local variables, etc).
Is packaging a parser practical for a `phpi` binary (e.g. for package
managers, maintainers of php, other reasons)?
- A parser may fail for code using new token types until the parser gets
updated to handle the new token types. This stops being a concern after feature
freezes.
Looping over `@token_get_all()` and bailing out on an unknown token type
may help with that.
- How would crash/bug fixes of `phpi` or the parser be handled in patch
releases of php if this was released with php?
- Automatically rewriting the code to namespace the parser and its
dependencies with `\PHP\Internal\BundledPhpParser`
would let `phpi` be used with projects that depend on a different
php-parser version.
(clarifications may be necessary to indicate to end users that the bundled
parser copy won't get updates or support outside of php minor releases,
should not be used by libraries/applications and that it won't support
newer php syntax, and possibly other clarifications)
- It may be useful to have an ini setting to disable these new hooks,
in case bugs/crashes in libraries using those hooks interfered with
debugging.
P.S. What do developers here use for an interactive shell?
I've seen https://github.com/bobthecow/psysh mentioned as an alternative for
`php -a` while investigating options but I haven't gotten around to using it.
It's useful that `php -a` doesn't die from otherwise unrecoverable errors.
Thanks,
- Tyson
--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php