From 4162b79113ec6d3387f0c4c811f2359ff55e0247 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 6 Jan 2026 16:13:10 +0800 Subject: [PATCH] =?UTF-8?q?try/catch=20=E8=AF=AD=E6=B3=95=E6=94=AF?= =?UTF-8?q?=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bin/compiler.php | 8 +++-- examples/nbody.php-3.php | 2 +- examples/throw-in-php.php | 15 ++++++++ main.cc | 14 ++++++++ src/Php/FileScanner.php | 30 +++++++++++++--- src/Php/Translator.php | 69 +++++++++++++++++++++++++++++++++++++ src/functions.php | 6 ++-- tests/aot/throw-in-php.phpt | 23 +++++++++++++ tests/aot/try-catch.phpt | 28 +++++++++++++++ 9 files changed, 183 insertions(+), 12 deletions(-) create mode 100644 examples/throw-in-php.php create mode 100644 tests/aot/throw-in-php.phpt create mode 100644 tests/aot/try-catch.phpt diff --git a/bin/compiler.php b/bin/compiler.php index f33ca40f..2764eb20 100755 --- a/bin/compiler.php +++ b/bin/compiler.php @@ -20,7 +20,7 @@ if (is_dir($path)) { $targetFile = basename($path); } else { $list = [$path]; - $targetFile = basename($path, '.php'); + $targetFile = FileScanner::getFileName($path); } $sourceFiles = []; @@ -29,13 +29,15 @@ $objectFiles = []; // 分析 PHP 文件,生成 C++ 文件 foreach ($list as $file) { try { - if (str_ends_with($file, '.php')) { + if (FileScanner::isPhpFile($file)) { $code = $translator->convert($file); $info = pathinfo($file); $cppFile = $info['dirname'] . '/' . $info['filename'] . '.cc'; $translator->save($code, $cppFile); - } else { + } elseif (FileScanner::isCppFile($file)) { $cppFile = $file; + } else { + continue; } $sourceFiles[] = $cppFile; } catch (Unsupported $e) { diff --git a/examples/nbody.php-3.php b/examples/nbody.php-3.php index a497382e..f7ec25dc 100644 --- a/examples/nbody.php-3.php +++ b/examples/nbody.php-3.php @@ -61,7 +61,7 @@ function main() -9.51592254519715870E-05 * $days_per_year, 5.15138902046611451E-05 * $solar_mass)); -// offset_momentum + // offset_momentum $px = $py = $pz = 0.0; foreach ($bodies as $e) { $px += $e[3] * $e[6]; diff --git a/examples/throw-in-php.php b/examples/throw-in-php.php new file mode 100644 index 00000000..9f1007c7 --- /dev/null +++ b/examples/throw-in-php.php @@ -0,0 +1,15 @@ +getMessage(), "\n"; + } finally { + echo "Finally\n"; + } +} diff --git a/main.cc b/main.cc index 5703a6ee..b298acc0 100644 --- a/main.cc +++ b/main.cc @@ -10,8 +10,22 @@ extern php::Var argc; extern php::Var argv; extern void php_unset_all_global_vars(); +static void throw_exception(zend_object *ex) { + zend_bailout(); +} + int main(int cpp_argc, char **cpp_argv) { php_embed_init(cpp_argc, cpp_argv); + + zend_execute_data fake_execute_data; + memset(&fake_execute_data, 0, sizeof(zend_execute_data)); + zend_function fake_func {}; + fake_func.type = ZEND_INTERNAL_FUNCTION; + fake_execute_data.func = &fake_func; + EG(current_execute_data) = &fake_execute_data; + + zend_throw_exception_hook = throw_exception; + int rc = 0; #if PPROF_ON ProfilerStart("myapp.prof"); diff --git a/src/Php/FileScanner.php b/src/Php/FileScanner.php index 57343232..1e6dcc60 100644 --- a/src/Php/FileScanner.php +++ b/src/Php/FileScanner.php @@ -7,20 +7,41 @@ use FilesystemIterator; class FileScanner { private string $directory; - private array $extensions; private array $excludePatterns; - public function __construct(string $directory, array $extensions = ['.php', '.cc', '.cpp', '.cxx', '.c', '.h', '.hpp']) + const array PHP_EXT = ['php']; + const array CPP_EXT = ['cpp', 'cxx', 'cc']; + + public function __construct(string $directory) { if (!is_dir($directory)) { throw new \InvalidArgumentException("Directory does not exist: $directory"); } $this->directory = rtrim($directory, DIRECTORY_SEPARATOR); - $this->extensions = $extensions; $this->excludePatterns = []; } + public static function getFileName(string $path): string + { + return pathinfo($path, PATHINFO_FILENAME); + } + + public static function getFileExt(string $path): string + { + return pathinfo($path, PATHINFO_EXTENSION); + } + + static function isPhpFile(string $file): bool + { + return in_array(self::getFileExt($file), self::PHP_EXT); + } + + static function isCppFile(string $file): bool + { + return in_array(self::getFileExt($file), self::CPP_EXT); + } + public function addExcludePattern(string $pattern): self { $this->excludePatterns[] = $pattern; @@ -36,8 +57,7 @@ class FileScanner foreach ($iterator as $file) { if ($file->isFile()) { - $extension = '.' . $file->getExtension(); - if (in_array($extension, $this->extensions)) { + if (self::isPhpFile($file)) { $filePath = $file->getPathname(); $excluded = false; foreach ($this->excludePatterns as $pattern) { diff --git a/src/Php/Translator.php b/src/Php/Translator.php index 1fc4d348..581b9b2a 100644 --- a/src/Php/Translator.php +++ b/src/Php/Translator.php @@ -537,6 +537,9 @@ class Translator extends \PhpAot\Core\Translator case 'Stmt_Unset': $result = $this->parseUnset($v); break; + case 'Stmt_TryCatch': + $result = $this->parseTryCatch($v); + break; default: abort($v); } @@ -671,6 +674,8 @@ class Translator extends \PhpAot\Core\Translator return $this->parseNew($expr); case 'Expr_Clone': return $this->parseClone($expr); + case 'Expr_Throw': + return $this->parseThrow($expr); case 'Name_FullyQualified': return $expr->name; case 'Scalar_Int': @@ -2245,4 +2250,68 @@ class Translator extends \PhpAot\Core\Translator { return $expr->getStartLine(); } + + private function parseThrow(mixed $expr): string + { + return 'php::throwException(' . $this->parseIdentifier($expr->expr). ')'; + } + + private function parseTryCatch(mixed $v): string + { + $code = 'zend_try {'; + $stmts = $v->stmts; + + $code .= PHP_EOL; + $this->indentLevel++; + $code .= $this->parseStmts($stmts); + $this->indentLevel--; + $code .= $this->getIndent() . '}' . PHP_EOL; + + $catches = $v->catches; + $finally = $v->finally; + + $exVar = $this->genTmpVarName(); + $this->addLocalVar($exVar, self::TYPE_OBJECT); + + $code .= 'zend_catch {' . PHP_EOL; + if ($catches) { + $code .= $this->getIndent() . $exVar . ' = php::catchException();' . PHP_EOL; + $this->indentLevel++; + foreach ($catches as $catch) { + $code .= $this->parseCatch($catch, $exVar); + } + $this->indentLevel--; + } + $code .= '}' . PHP_EOL . 'zend_end_try();' . PHP_EOL; + if ($finally) { + $code .= $this->parseStmts($finally->stmts); + $code .= PHP_EOL; + $code .= 'if (' . $exVar . ') {' . PHP_EOL . $this->getIndent() . 'php::throwException(' . $exVar . ');' . PHP_EOL . $this->getIndent() . '}'; + } + return $code; + } + + private function parseCatch(mixed $catch, string $exVar): string + { + $types = $catch->types; + $var = $this->parseIdentifier($catch->var); + if (!$this->hasVar($var)) { + $this->addLocalVar($var, self::TYPE_OBJECT); + } + $code = $this->getIndent() . $var . ' = ' . $exVar . ';' . PHP_EOL; + + $code .= $this->getIndent() . 'if ('; + foreach ($types as $type) { + $code .= 'php::instanceOf(' . $var . ', "' . $this->parseIdentifier($type) . '")'; + } + + $code .= ') {' . PHP_EOL; + $this->indentLevel++; + $code .= $this->parseStmts($catch->stmts); + $code .= $this->getIndent() . "$exVar.unset();" . PHP_EOL; + $this->indentLevel--; + $code .= $this->getIndent() . '}'; + + return $code; + } } diff --git a/src/functions.php b/src/functions.php index e55b2e4e..9767ee1b 100644 --- a/src/functions.php +++ b/src/functions.php @@ -13,9 +13,9 @@ function abort($v) $msg = 'Error: Unsupported ' . $lang . ' Syntax,'; $msg .= ' Line: ' . $translator->getLine($v) . ', Type: ' . $translator->getType($v) . PHP_EOL; if ($translator->mode == 'cli') { -// if (DEBUG) { -// var_dump($v); -// } + if (DEBUG) { + var_dump($v); + } } else { header('Content-Type: application/json'); echo json_encode($v, JSON_PRETTY_PRINT); diff --git a/tests/aot/throw-in-php.phpt b/tests/aot/throw-in-php.phpt new file mode 100644 index 00000000..62587faf --- /dev/null +++ b/tests/aot/throw-in-php.phpt @@ -0,0 +1,23 @@ +--TEST-- +try catch +--FILE-- +getMessage(), "\n"; + } finally { + echo "Finally\n"; + } +} +?> +--EXPECT-- +0.20 +Caught exception: Division by zero +Finally diff --git a/tests/aot/try-catch.phpt b/tests/aot/try-catch.phpt new file mode 100644 index 00000000..c24a2679 --- /dev/null +++ b/tests/aot/try-catch.phpt @@ -0,0 +1,28 @@ +--TEST-- +try catch +--FILE-- +getMessage(), "\n"; + } catch (RuntimeException $e) { + echo 'Caught runtime exception: ', $e->getMessage(), "\n"; + } finally { + echo "Finally\n"; + } +} +?> +--EXPECT-- +0.20 +Caught exception: Division by zero. +Finally