动态函数调用语法

pull/1/head
韩天峰 8 months ago
parent 739152ba14
commit fb10132b6c
  1. 3
      main.cc
  2. 88
      src/Php/Translator.php
  3. 9
      tests/aot/static-call.phpt
  4. 15
      tests/zend/dynamic_call/dynamic_call_002.phpt
  5. 16
      tests/zend/dynamic_call/dynamic_call_003.phpt
  6. 17
      tests/zend/dynamic_call/dynamic_call_004.phpt

@ -37,6 +37,9 @@ int main(int cpp_argc, char **cpp_argv) {
}
zend_catch {
rc = EG(exit_status);
if (EG(exception)) {
zend_exception_error(EG(exception), E_ERROR);
}
}
zend_end_try();
#if PPROF_ON

@ -91,6 +91,11 @@ class Translator extends \PhpAot\Core\Translator
'pipe',
];
private array $unsupportedFunctions = [
'compact',
'extract'
];
private array $nativeFunctions = [];
private array $internalFunctions = [];
private int $optimizeLevel = 0;
@ -1315,28 +1320,33 @@ class Translator extends \PhpAot\Core\Translator
private function parseFuncCall(mixed $expr): string
{
$name = $this->parseIdentifier($expr->name);
if ($this->isNativeFunction($name)) {
return self::PREFIX . $name . '(' . $this->parseCallArgs($expr->args, $name) . ')';
}
if ($this->isInternalFunction($name)) {
$fn = 'php::' . $name;
if ($expr->name->getType() === self::EXPR_VARIABLE) {
$fn = $this->parseIdentifier($expr->name);
$name = '';
} else {
$fn = '"' . $name . '"';
}
if ($name === 'strlen' or $name === 'sizeof' or $name === 'count') {
return 'php::len(' . $this->parseIdentifier($expr->args[0]->value) . ')';
}
if (count($expr->args) == 1) {
switch ($name) {
case 'intval':
return $this->convertIntExpr($this->parseExpr($expr->args[0]->value));
case 'floatval':
return $this->convertFloatExpr($this->parseExpr($expr->args[0]->value));
case 'boolval':
return $this->convertBoolExpr($this->parseExpr($expr->args[0]->value));
default:
break;
$name = $this->parseIdentifier($expr->name);
if ($this->isNativeFunction($name)) {
return self::PREFIX . $name . '(' . $this->parseCallArgs($expr->args, $name) . ')';
}
if ($this->isInternalFunction($name)) {
$fn = 'php::' . $name;
} else {
$fn = '"' . $name . '"';
}
if ($name === 'strlen' or $name === 'sizeof' or $name === 'count') {
return 'php::len(' . $this->parseIdentifier($expr->args[0]->value) . ')';
}
if (count($expr->args) == 1) {
switch ($name) {
case 'intval':
return $this->convertIntExpr($this->parseExpr($expr->args[0]->value));
case 'floatval':
return $this->convertFloatExpr($this->parseExpr($expr->args[0]->value));
case 'boolval':
return $this->convertBoolExpr($this->parseExpr($expr->args[0]->value));
default:
break;
}
}
}
if (empty($expr->args)) {
@ -2254,15 +2264,39 @@ class Translator extends \PhpAot\Core\Translator
}
}
private function identifierToStr(Node $node, bool $require = true): string
{
$id = $this->parseIdentifier($node);
if ($node->getType() === self::EXPR_VARIABLE) {
if ($require) {
$this->requireVar($node, $id);
}
return $id;
} else {
return '"'. $id . '"';
}
}
private function requireVar($node, string $var): void
{
if (!$this->hasVar($var)) {
$this->fatalError($node, 'The variable `' . $var . '` is not defined');
}
}
private function parseStaticCall(mixed $expr): string
{
$class = $this->parseIdentifier($expr->class);
$method = $this->parseIdentifier($expr->name);
$fn = $class . '::' . $method;
if ($expr->class->getType() === self::EXPR_VARIABLE or $expr->name->getType() === self::EXPR_VARIABLE) {
$fn = 'php::concat({' . $this->identifierToStr($expr->class). ', "::", ' . $this->identifierToStr($expr->name) . '})';
} else {
$class = $this->parseIdentifier($expr->class);
$method = $this->parseIdentifier($expr->name);
$fn = '"'. $class . '::' . $method . '"';
}
if (empty($expr->args)) {
return 'php::call("' . $fn . '")';
return 'php::call(' . $fn . ')';
} else {
return 'php::call("' . $fn . '", {' . $this->parseCallArgs($expr->args) . '})';
return 'php::call(' . $fn . ', {' . $this->parseCallArgs($expr->args) . '})';
}
}
@ -2320,7 +2354,7 @@ class Translator extends \PhpAot\Core\Translator
}
$code = $this->getIndent() . $var . ' = ' . $exVar . ';' . PHP_EOL;
$code .= $this->getIndent() . 'if (';
$code .= $this->getIndent() . 'if (' . $var . ' && ';
foreach ($types as $type) {
$code .= 'php::instanceOf(' . $var . ', "' . $this->parseIdentifier($type) . '")';
}

@ -0,0 +1,9 @@
--TEST--
static calls
--FILE--
<?php
$cls = 'DateTime';
$method = 'createFromFormat';
$now = $cls::$method('U', time());
?>
--EXPECTF--

@ -0,0 +1,15 @@
--TEST--
Testing dynamic call with invalid value for method name
--FILE--
<?php
$a = new stdClass;
$a::$a();
?>
--EXPECTF--
Fatal error: Uncaught Error: %s
Stack trace:
#0 {main}
thrown in %s on line %d

@ -0,0 +1,16 @@
--TEST--
Testing dynamic call with invalid method name
--FILE--
<?php
$a = new stdClass;
$b = 1;
$a::$b();
?>
--EXPECTF--
Fatal error: Uncaught Error: %s
Stack trace:
#0 {main}
thrown in %s on line %d

@ -0,0 +1,17 @@
--TEST--
Testing dynamic call with undefined variables
--SKIPIF--
<?php die("skip");?>
--FILE--
<?php
$a::$b();
?>
--EXPECTF--
Warning: Undefined variable $a in %s on line %d
Fatal error: Uncaught Error: Class name must be a valid object or a string in %s:%d
Stack trace:
#0 {main}
thrown in %s on line %d
Loading…
Cancel
Save