try/catch 语法支持

pull/1/head
韩天峰 8 months ago
parent ec836bcb31
commit 4162b79113
  1. 8
      bin/compiler.php
  2. 2
      examples/nbody.php-3.php
  3. 15
      examples/throw-in-php.php
  4. 14
      main.cc
  5. 30
      src/Php/FileScanner.php
  6. 69
      src/Php/Translator.php
  7. 6
      src/functions.php
  8. 23
      tests/aot/throw-in-php.phpt
  9. 28
      tests/aot/try-catch.phpt

@ -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) {

@ -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];

@ -0,0 +1,15 @@
<?php
function inverse($x) {
return number_format(1.0 / $x, 2);
}
function main() {
try {
echo inverse(5.0) . "\n";
echo inverse(0) . "\n";
} catch (DivisionByZeroError $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
} finally {
echo "Finally\n";
}
}

@ -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");

@ -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) {

@ -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;
}
}

@ -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);

@ -0,0 +1,23 @@
--TEST--
try catch
--FILE--
<?php
function inverse($x) {
return number_format(1.0 / $x, 2);
}
function main() {
try {
echo inverse(5.0) . "\n";
echo inverse(0) . "\n";
} catch (DivisionByZeroError $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
} finally {
echo "Finally\n";
}
}
?>
--EXPECT--
0.20
Caught exception: Division by zero
Finally

@ -0,0 +1,28 @@
--TEST--
try catch
--FILE--
<?php
function inverse($x) {
if (!$x) {
throw new Exception('Division by zero.');
}
return number_format(1.0 / $x, 2);
}
function main() {
try {
echo inverse(5.0) . "\n";
echo inverse(0) . "\n";
} catch (Exception $e) {
echo 'Caught exception: ', $e->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
Loading…
Cancel
Save