rfc:readline_interactive_shell_result_function

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
rfc:readline_interactive_shell_result_function [2020/12/26 14:45] tandrerfc:readline_interactive_shell_result_function [2021/02/03 00:39] (current) tandre
Line 1: Line 1:
-====== PHP RFC: Configurable callback to dump results of expressions in `php -a` ====== +====== PHP RFC: Dump results of expressions in `php -a` ====== 
-  * Version: 0.1+  * Version: 0.2
   * Date: 2020-12-19   * Date: 2020-12-19
   * Author: Tyson Andre, tandre@php.net   * Author: Tyson Andre, tandre@php.net
-  * Status: Under Discussion+  * Status: Declined
   * Implementation: https://github.com/php/php-src/pull/5962/files   * Implementation: https://github.com/php/php-src/pull/5962/files
   * First Published at: https://wiki.php.net/rfc/readline_interactive_shell_result_function   * First Published at: https://wiki.php.net/rfc/readline_interactive_shell_result_function
Line 14: Line 14:
 (I've seen https://github.com/bobthecow/psysh mentioned as an alternative for ''php -a'' while investigating this, but that's a shell written from scratch, and doesn't have some functionality from ''php -a'' such as tolerance of fatal errors) (I've seen https://github.com/bobthecow/psysh mentioned as an alternative for ''php -a'' while investigating this, but that's a shell written from scratch, and doesn't have some functionality from ''php -a'' such as tolerance of fatal errors)
  
-Because PHP's interactive shell is written in C, adding new features or bug fixes would require a lot of time getting familiar with 
-C programming, PHP's internals and memory management, and with PHP's internal C ast representation. It would be easier and more accessible to extend PHP's interactive shell through code written in PHP rather than code written in C. 
  
 ===== Proposal ===== ===== Proposal =====
  
-Add a new function ''readline_interactive_shell_result_function'' when starting an interactive PHP session with ''php -a''. This function only affects interactive shells - it can be used to set or clear a closure when ''extension_loaded('readline') === true'', but that closure would only be called in interactive shells (i.e. ''php -a'').+By default, in ''php -a'', dump the results of non-null expressions using ''var_dump''/''var_export()''
 +Additionally, add a new function ''readline_interactive_shell_result_function'' to the ''readline'' PHP module. This function only affects interactive shells - it can be used to set or clear a closure when ''extension_loaded('readline') === true'', but that closure would only be called in interactive shells (i.e. ''php -a'').
  
-Using this, users or applications can configure callables to be run every time a statement containing a single expression such as ''2+2;'' or ''$x = call_function();'' is evaluated (but not non-expressions such as ''class X{}'' or combinations of expressions such as ''$x = 1; $y = $x*2;''A very basic example of using this would be:+This will dump the results of expressions every time a statement containing a single expression such as ''2+2;'' or ''$x = call_function();'' is evaluated (but not non-expressions such as ''class X{}'', statement blocks such as ''{ $x = 1; }'', or combinations of expressions such as ''$x = 1; $y = $x*2;''. 
 + 
 +An example of the behavior of the default expression dumper is below: 
 + 
 +<code php> 
 +$ php -a 
 +Interactive shell 
 + 
 +php > 1+1; 
 +=> 2 
 +php > 0.5 * 2; 
 +=> 1.0 
 +php > namespace\MyClass::class; 
 +=> 'MyClass' 
 +php > fn()=>true; 
 +=> object(Closure)#1 (0) { 
 +
 +php > $x = ["foo", "bar"]; 
 +=> array(2) { 
 +  [0]=> 
 +  string(3) "foo" 
 +  [1]=> 
 +  string(3) "bar" 
 +
 +php > asort($x); 
 +=> true 
 +php > $x; 
 +=> array(2) { 
 +  [1]=> 
 +  string(3) "bar" 
 +  [0]=> 
 +  string(3) "foo" 
 +
 +php > json_encode($x); 
 +=> '{"1":"bar","0":"foo"}' 
 +php > unset($x); 
 +php > function do_something() { echo "in do_something()\n";
 +php > do_something(); 
 +in do_something() 
 +php > json_decode('{"key": "value"}'); 
 +=> object(stdClass)#1 (1) { 
 +  ["key"]=> 
 +  string(5) "value" 
 +
 +php > throw new RuntimeException("test"); 
 + 
 +Warning: Uncaught RuntimeException: test in php shell code:1 
 +Stack trace: 
 +#0 {main} 
 +  thrown in php shell code on line 1 
 +php > printf("newline is automatically appended by shell"); 
 +newline is automatically appended by shell 
 +=> 42 
 +php > printf("newline not automatically appended by shell\n"); 
 +newline not automatically appended by shell 
 +=> 44 
 +php > { print("test\n"); } // statement blocks can be used to avoid dumping expression results 
 +test 
 +php > 
 +</code> 
 + 
 +Using the function ''readline_interactive_shell_result_function()'', users or applications can override the default expression dumper, or remove it entirely.
  
 <code php> <code php>
-php -a+php -a
 Interactive shell Interactive shell
  
Line 32: Line 92:
 php (         echo json_encode($result); php (         echo json_encode($result);
 php (     }); php (     });
 +Saw readline_interactive_shell_result_function(
 +     function (string $code, $result) {
 +         echo "Saw " . trim($code) . "\n";
 +         echo json_encode($result);
 +     });
 +true
 php > 2+2; php > 2+2;
 Saw 2+2; Saw 2+2;
 4 4
 +php > readline_interactive_shell_result_function(null);  // useful if output would be extremely long
 +php > 2+2;
 +php > 
 </code> </code>
  
Line 51: Line 120:
 </code> </code>
  
-A new system ini boolean setting ''cli.enable_interactive_shell_result_function'' is added as part of this RFC, defaulting to enabled. It can be disabled to prevent the closures from being called on expression results, e.g. if the closures have bugs, are excessivly verbose, or have unpredictable performance.+The default implementation added as part of this RFC is effectively identical to the below implementation, but written in C. It can be replaced with a userland implementation or disabled at any time, even in [[https://www.php.net/manual/en/ini.core.php#ini.auto-prepend-file|auto_prepend_file]]. (Because the default implementation is written in C, it will work even if the ini setting ''disable_functions'' includes var_dump and var_export.) 
 + 
 +<code php> 
 +readline_interactive_shell_result_function( 
 +    function(string $code, $result) { 
 +        if (!isset($result)) { 
 +            return; 
 +        } 
 +        if (is_scalar($result)) { 
 +            echo "=> " . var_export($result, true) . "\n"; 
 +        } else { 
 +            echo "=> "; var_dump($result); 
 +        }}); 
 +</code> 
 + 
 +A new system ini boolean setting ''cli.enable_interactive_shell_result_function'' is added as part of this RFC, defaulting to enabled. It can be disabled to prevent expression results from being dumped at all (by the internal implementation or by overrides), e.g. if the closures have bugs, are excessivly verbose, or have unpredictable performance.
  
 ===== Backward Incompatible Changes ===== ===== Backward Incompatible Changes =====
-None, only interactive sessions are affected, and only when this functionality is enabled and callback is installed.+ 
 +Only interactive sessions (''php -a''are affected, by difference in the output sent to stdout. The dumping of expression results can be disabled entirely with the ini setting ''cli.enable_interactive_shell_result_function = Off'', or temporarily by calling ''readline_interactive_shell_result_function(null)'' 
 + 
 +In interactive sessions, this will start calling [[https://www.php.net/manual/en/language.oop5.magic.php#object.debuginfo|__debugInfo()]] if it exists due to calling ''var_dump()'' on objects. Implementations of ''__debugInfo()'' may throw or have other side effects after the expression is evaluated. 
  
 ===== Proposed PHP Version(s) ===== ===== Proposed PHP Version(s) =====
Line 70: Line 158:
  
 ===== Future Scope ===== ===== Future Scope =====
- 
-==== Providing a default callback to dump expression results ==== 
- 
-This RFC is minimal and does not provide a default implementation for rendering values of expressions (i.e. there is no callback by default). Providing a default is feasible, but there are a variety of choices that could be made (e.g. var_dump vs var_export vs shorter JSON-like equivalents for simple arrays, handling of recursive data structures, truncating output, etc). 
- 
-  * In order to provide more extensive default handlers for tab completion and rendering of evaluated expressions, it may be useful to bundle a parser written in php with the interactive shell. The heuristics currently written in C are very limited and not aware of context beyond the most recent tokens. 
  
 From https://externals.io/message/111073#111073 From https://externals.io/message/111073#111073
Line 97: Line 179:
   * i.e. change ''foo(); bar();'' to ''foo(); return (bar());''   * i.e. change ''foo(); bar();'' to ''foo(); return (bar());''
  
-===== Proposed Voting Choices =====+==== Rendering a result limit ====
  
-Voting starts on 2021-01-03 and ends 2021-01-17.+https://externals.io/message/112934#113039 was brought up after the start of the vote. While some REPLs don't limit results, others do, and it's useful to have for extremely large objects. 
 +The setting ''cli.pager'' can be used to limit results, but is disabled by default. 
 + 
 +<blockquote> 
 +My main concern in this iteration of the RFC is: what happens with big/deeply nested objects? 
 +They tend to spew tons of lines if var_dump()'ed. Do we have reasonable depth/output limitations in default dumping mode? 
 + 
 +I'm often enough using php -a to do some quick ad-hoc processing (example, read a big json file, and then access a value; instantiating a mediawiki bot framework and calling replace on it; ...). 
 + 
 +It's really cool to have any interactive feedback at all, but please, at least by default, limit the output. (An example is the JS REPL in browser console - it shows you a minimal preview of the object, and then you can expand with your mouse. Obviously with a pure cli application, this needs different - intuitive - navigation.) 
 + 
 +As it currently stands, this makes ''php -a'' unusable in any but the simplest cases, without just disabling the whole feature. 
 + 
 +I like the whole feature, but the missing output limitation (I have yet enough nightmares from var_dump()'ing the wrong object filling my shell with tons of irrelevant information… I don't need that potentially happening on every single evaluated expression) 
 + 
 +Thus I'm voting no, for now. 
 +</blockquote> 
 +===== Vote ===== 
 + 
 +Voting starts on 2021-01-19 and ended 2021-02-02
  
 Yes/No, requiring 2/3 majority Yes/No, requiring 2/3 majority
 +
 +<doodle title="Dump results of expressions in `php -a` as described in this RFC" auth="tandre" voteType="single" closed="true">
 +   * Yes
 +   * No
 +</doodle>
  
 ===== References ===== ===== References =====
  
 +  * https://externals.io/message/112568 "[RFC] Configurable callback to dump results of expressions in `php -a`"
   * https://externals.io/message/111073 "Improving the usability of PHP's interactive shell? (completions, displaying expression results, custom syntax)"   * https://externals.io/message/111073 "Improving the usability of PHP's interactive shell? (completions, displaying expression results, custom syntax)"
 +  * https://wiki.php.net/rfc/readline_interactive_shell_result_function_straw_poll "Straw poll: Interest in configurable callback to dump results of expressions in ''php -a''"
 +
 +===== Changelog =====
 +
 +0.2: Dump non-null expression results by default with var_dump()/var_export()
 +0.3: Document the default implementation used in the implementation
rfc/readline_interactive_shell_result_function.1608993943.txt.gz · Last modified: 2020/12/26 14:45 by tandre