diff --git a/features/shell.feature b/features/shell.feature index d5595506..3fcf6ec8 100644 --- a/features/shell.feature +++ b/features/shell.feature @@ -77,3 +77,41 @@ 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: + """ + Error: 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() + """ diff --git a/src/WP_CLI/Shell/REPL.php b/src/WP_CLI/Shell/REPL.php index 99b8f1f8..5008adc4 100644 --- a/src/WP_CLI/Shell/REPL.php +++ b/src/WP_CLI/Shell/REPL.php @@ -29,8 +29,13 @@ public function start() { if ( self::starts_with( self::non_expressions(), $line ) ) { ob_start(); - // phpcs:ignore Squiz.PHP.Eval.Discouraged -- This is meant to be a REPL, no way to avoid eval. - eval( $line ); + try { + // phpcs:ignore Squiz.PHP.Eval.Discouraged -- This is meant to be a REPL, no way to avoid eval. + eval( $line ); + } catch ( \Throwable $e ) { + // Display the error message but continue the session + fwrite( STDERR, get_class( $e ) . ': ' . $e->getMessage() . "\n" ); + } $out = (string) ob_get_clean(); if ( 0 < strlen( $out ) ) { $out = rtrim( $out, "\n" ) . "\n"; @@ -43,8 +48,14 @@ 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. - $evl = eval( $line ); + try { + // phpcs:ignore Squiz.PHP.Eval.Discouraged -- This is meant to be a REPL, no way to avoid eval. + $evl = eval( $line ); + } catch ( \Throwable $e ) { + // Display the error message but continue the session + fwrite( STDERR, get_class( $e ) . ': ' . $e->getMessage() . "\n" ); + $evl = null; + } $out = (string) ob_get_clean(); if ( 0 < strlen( $out ) ) { echo rtrim( $out, "\n" ) . "\n";