diff --git a/src/Php/CompilerBase.php b/src/Php/CompilerBase.php index a07a0f5e..735ab9d4 100644 --- a/src/Php/CompilerBase.php +++ b/src/Php/CompilerBase.php @@ -6556,12 +6556,26 @@ class CompilerBase extends \PhpAot\Core\Translator implements PropertyAccessCont $finally = $v->finally; $stmts = $finally ? $this->injectFinallyBeforeReturn($v->stmts, $finally->stmts) : $v->stmts; + $catches = $v->catches; + + // Pre-register catch variables before parsing the try block so that + // injected finally code referencing catch variables (e.g. $e) does not + // trigger undefined-variable errors. + if ($finally) { + foreach ($catches as $catch) { + if ($catch->var) { + $varName = $this->parseIdentifier($catch->var); + if (!$this->hasVar($varName)) { + $this->addLocalVar($varName, self::TYPE_OBJECT); + } + } + } + } + $code .= PHP_EOL; $code .= $this->parseBlockStmts($stmts); $code .= $this->getIndent() . '}' . PHP_EOL; - $catches = $v->catches; - $exVar = $this->genTmpVarName(); $this->addLocalVar($exVar, self::TYPE_VAR); @@ -6695,7 +6709,19 @@ class CompilerBase extends \PhpAot\Core\Translator implements PropertyAccessCont $this->indentLevel++; $code .= $this->getIndent() . "{$exVar} = php::null;" . PHP_EOL; $stmts = $finallyStmts ? $this->injectFinallyBeforeReturn($catch->stmts, $finallyStmts) : $catch->stmts; - $code .= $this->parseStmts($stmts); + if ($finallyStmts) { + $code .= $this->getIndent() . 'try {' . PHP_EOL; + $this->indentLevel++; + $code .= $this->parseStmts($stmts); + $this->indentLevel--; + $code .= $this->getIndent() . '} catch(zend_object *_catch_throw_ex) {' . PHP_EOL; + $this->indentLevel++; + $code .= $this->getIndent() . "{$exVar} = php::catchException();" . PHP_EOL; + $this->indentLevel--; + $code .= $this->getIndent() . '}' . PHP_EOL; + } else { + $code .= $this->parseStmts($stmts); + } $this->indentLevel--; $code .= $this->getIndent() . '}'; diff --git a/tests/aot/exception/finally-use-var.phpt b/tests/aot/exception/finally-use-var.phpt new file mode 100644 index 00000000..2fb09e61 --- /dev/null +++ b/tests/aot/exception/finally-use-var.phpt @@ -0,0 +1,29 @@ +--TEST-- +try-catch-finally: finally block must execute when exception is re-thrown inside catch block. Verifies that finally can access the caught exception variable ($e) even when the catch block re-throws it. +--FILE-- +getMessage(), "\n"; + throw $e; + } finally { + var_dump($a); + echo 'Finally exception: ', $e->getMessage(), "\n"; + } + var_dump('This should not be reached'); +} +?> +--EXPECT-- +Caught exception: test +int(1) +Finally exception: test + +Fatal error: Uncaught RuntimeException: test in Unknown(0) : eval():1 +Stack trace: +#0 Unknown(0) : eval()(1): main() +#1 {main} + thrown in Unknown(0) : eval() on line 1