diff --git a/features/shell.feature b/features/shell.feature index 0b219302..dfb63c44 100644 --- a/features/shell.feature +++ b/features/shell.feature @@ -147,6 +147,44 @@ Feature: WordPress REPL history: -1: invalid option """ + Scenario: Exception handling preserves session state + Given a WP install + And a session file: + """ + $foo = 'test_value'; + require 'nonexistent_file.txt'; + echo $foo; + """ + + When I try `wp shell --basic < session` + Then STDOUT should contain: + """ + test_value + """ + And STDERR should contain: + """ + Failed opening required 'nonexistent_file.txt' + """ + + Scenario: Exception handling for expression errors + Given a WP install + And a session file: + """ + $bar = 'preserved'; + nonexistent_function(); + $bar; + """ + + When I try `wp shell --basic < session` + Then STDOUT should contain: + """ + string(9) "preserved" + """ + And STDERR should contain: + """ + Error: Call to undefined function nonexistent_function() + """ + Scenario: User can define variable named $line Given a WP install And a session file: diff --git a/src/WP_CLI/Shell/REPL.php b/src/WP_CLI/Shell/REPL.php index ce9c9856..5dcff93b 100644 --- a/src/WP_CLI/Shell/REPL.php +++ b/src/WP_CLI/Shell/REPL.php @@ -68,8 +68,13 @@ public function start() { if ( self::starts_with( self::non_expressions(), $__repl_input_line ) ) { ob_start(); - // phpcs:ignore Squiz.PHP.Eval.Discouraged -- This is meant to be a REPL, no way to avoid eval. - eval( $__repl_input_line ); + try { + // phpcs:ignore Squiz.PHP.Eval.Discouraged -- This is meant to be a REPL, no way to avoid eval. + eval( $__repl_input_line ); + } catch ( \Throwable $e ) { + // Display the error message but continue the session + fwrite( STDERR, get_class( $e ) . ': ' . $e->getMessage() . "\n" ); + } $__repl_output = (string) ob_get_clean(); if ( 0 < strlen( $__repl_output ) ) { $__repl_output = rtrim( $__repl_output, "\n" ) . "\n"; @@ -82,12 +87,23 @@ public function start() { // Write directly to STDOUT, to sidestep any output buffers created by plugins ob_start(); - // phpcs:ignore Squiz.PHP.Eval.Discouraged -- This is meant to be a REPL, no way to avoid eval. - $__repl_eval_result = eval( $__repl_input_line ); - $__repl_output = (string) ob_get_clean(); + $__repl_eval_had_error = false; + try { + // phpcs:ignore Squiz.PHP.Eval.Discouraged -- This is meant to be a REPL, no way to avoid eval. + $__repl_eval_result = eval( $__repl_input_line ); + } catch ( \Throwable $e ) { + // Display the error message but continue the session + fwrite( STDERR, get_class( $e ) . ': ' . $e->getMessage() . "\n" ); + $__repl_eval_had_error = true; + $__repl_eval_result = null; + } + $__repl_output = (string) ob_get_clean(); if ( 0 < strlen( $__repl_output ) ) { echo rtrim( $__repl_output, "\n" ) . "\n"; } + if ( $__repl_eval_had_error ) { + continue; + } ob_start(); if ( ! $this->quiet ) { echo '=> ';