diff --git a/bin/compiler.php b/bin/compiler.php index c1b819b7..01c495fb 100755 --- a/bin/compiler.php +++ b/bin/compiler.php @@ -39,6 +39,7 @@ foreach ($list as $file) { } $sourceFiles[] = $cppFile; } catch (Unsupported $e) { + echo " unsupported syntax: " . $e->getMessage() . "\n"; echo " skip: " . $file . "\n"; } } diff --git a/src/Php/Translator.php b/src/Php/Translator.php index 375cac9f..3c0e471c 100644 --- a/src/Php/Translator.php +++ b/src/Php/Translator.php @@ -116,6 +116,7 @@ class Translator extends \PhpAot\Core\Translator 'argv' => self::TYPE_ARRAY, ]; private array $localVars = []; + private array $objectWrappers = []; const string PREFIX = 'php_'; private string $rootPath; @@ -656,6 +657,10 @@ class Translator extends \PhpAot\Core\Translator return $this->parseTernary($expr); case 'Expr_FuncCall': return $this->parseFuncCall($expr); + case 'Expr_MethodCall': + return $this->parseMethodCall($expr); + case 'Expr_StaticCall': + return $this->parseStaticCall($expr); case 'Expr_Include': return $this->parseInclude($expr); case 'Expr_Eval': @@ -1302,7 +1307,6 @@ class Translator extends \PhpAot\Core\Translator break; } } - if (empty($expr->args)) { return 'php::call(' . $fn . ')'; } else { @@ -1310,9 +1314,14 @@ class Translator extends \PhpAot\Core\Translator } } - private function parseCallArgs(array $args, string $funcName = ''): string + private function parseCallArgs(array $args, string $funcName = '', string $className = ''): string { - $internalFunction = $this->isInternalFunction($funcName); + if (!$className) { + $internalFunction = $this->isInternalFunction($funcName); + } else { + $internalFunction = false; + } + $list_args = []; foreach ($args as $i => $arg) { if ($arg->value->getType() === self::EXPR_VARIABLE) { @@ -1320,7 +1329,7 @@ class Translator extends \PhpAot\Core\Translator // 调用了不存在的变量,可能是引用 if (!$this->hasVar($name)) { $this->addLocalVar($name, self::TYPE_REF); - } else { + } else if ($funcName) { $funcArg = Reflection::getFunctionParameter($funcName, 0); // 需要引用类型的参数,使用临时变量作为引用,并替换掉实际的参数 if ($funcArg and $funcArg->isPassedByReference()) { @@ -1593,7 +1602,7 @@ class Translator extends \PhpAot\Core\Translator if (empty($args)) { return 'php::newObject("' . $className . '")'; } else { - return 'php::newObject("' . $className . '"(' . $this->parseCallArgs($args) . ')'; + return 'php::newObject("' . $className . '", ' . $this->parseCallArgs($args) . ')'; } } @@ -1795,7 +1804,7 @@ class Translator extends \PhpAot\Core\Translator private function parsePropertyFetch(Node $expr): string { - return $this->parseIdentifier($expr->var) . '.getProperty(' . $this->parseIdentifier($expr->name) . ')'; + return $this->convertToObject($expr->var) . '.getProperty("' . $this->parseIdentifier($expr->name) . '")'; } private function parseAssignOpShiftRight(Node $node): string @@ -2069,6 +2078,30 @@ class Translator extends \PhpAot\Core\Translator return $expr; } + private function convertToObject(Node $object): string + { + $id = $this->parseIdentifier($object); + if (!$this->hasVar($id)) { + $this->addLocalVar($id, self::TYPE_OBJECT); + return $id; + } + + $type = $this->getVarType($id); + if ($type === self::TYPE_OBJECT) { + return $id; + } + + if (isset($this->objectWrappers[$id])) { + return $this->objectWrappers[$id]; + } + + $tmpVar = $this->genTmpVarName(); + $this->addLocalVar($tmpVar, self::TYPE_OBJECT); + $this->beforeStmtLines[] = $this->getIndent() . $tmpVar . ' = ' . $id . ';'; + $this->objectWrappers[$id] = $tmpVar; + return $tmpVar; + } + public function genFunctionDeclaration(string $file): void { $code = ''; @@ -2101,4 +2134,27 @@ class Translator extends \PhpAot\Core\Translator } abort($expr); } + + private function parseMethodCall(mixed $expr): string + { + $object = $this->convertToObject($expr->var); + $method = $this->parseIdentifier($expr->name); + if (empty($expr->args)) { + return $object . '.exec("' . $method . '")'; + } else { + return $object . '.exec("' . $method . '", ' . $this->parseCallArgs($expr->args) . ')'; + } + } + + private function parseStaticCall(mixed $expr): string + { + $class = $this->parseIdentifier($expr->class); + $method = $this->parseIdentifier($expr->name); + $fn = $class . '::' . $method; + if (empty($expr->args)) { + return 'php::call("' . $fn . '")'; + } else { + return 'php::call("' . $fn . '", {' . $this->parseCallArgs($expr->args) . '})'; + } + } } diff --git a/tests/aot/datetime_builtin_class.phpt b/tests/aot/datetime_builtin_class.phpt new file mode 100644 index 00000000..310d6d90 --- /dev/null +++ b/tests/aot/datetime_builtin_class.phpt @@ -0,0 +1,64 @@ +--TEST-- +DateTime Built-in Class - Creating objects, calling methods, reading/writing properties +--FILE-- +format('Y-m-d H:i:s') . "\n"; + +// Test modifying date +$date->modify('+1 day'); +echo $date->format('Y-m-d H:i:s') . "\n"; + +// Test setting date and time +$date->setDate(2024, 1, 1); +$date->setTime(12, 0, 0); +echo $date->format('Y-m-d H:i:s') . "\n"; + +// Test timestamp +$timestamp = $date->getTimestamp(); +echo $timestamp . "\n"; + +// Create from timestamp +$date2 = new DateTime(); +$date2->setTimestamp($timestamp); +echo $date2->format('Y-m-d H:i:s') . "\n"; + +// Test timezone +$date->setTimezone(new DateTimeZone('UTC')); +echo $date->format('Y-m-d H:i:s T') . "\n"; + +// Test diff method +$date3 = new DateTime('2023-01-01'); +$date4 = new DateTime('2023-12-31'); +$interval = $date3->diff($date4); +echo $interval->days . " days\n"; + +// Test static create methods +$now = DateTime::createFromFormat('U', time()); +echo $now->format('Y-m-d H:i:s') . "\n"; + +// Test add/subtract intervals +$date->add(new DateInterval('P1D')); // Add 1 day +echo $date->format('Y-m-d H:i:s') . "\n"; +$date->sub(new DateInterval('P1D')); // Subtract 1 day +echo $date->format('Y-m-d H:i:s') . "\n"; + +// Test formatting options +echo $date->format('c') . "\n"; // ISO 8601 format +echo $date->format('r') . "\n"; // RFC 2822 format +?> +--EXPECTF-- +2023-06-15 10:30:45 +2023-06-16 10:30:45 +2024-01-01 12:00:00 +1704110400 +2024-01-01 12:00:00 +2024-01-01 12:00:00 UTC +364 days +%d-%d-%d %d:%d:%d +2024-01-02 12:00:00 +2024-01-01 12:00:00 +2024-01-01T12:00:00+00:00 +Mon, 01 Jan 2024 12:00:00 +0000 \ No newline at end of file