动态函数调用语法 [2]

pull/1/head
韩天峰 8 months ago
parent fb10132b6c
commit c1d759de57
  1. 32
      src/Php/Translator.php
  2. 38
      tests/zend/dynamic_call/dynamic_call_005.phpt
  3. 59
      tests/zend/dynamic_call/dynamic_call_006.phpt
  4. 23
      tests/zend/dynamic_call/dynamic_call_007.phpt
  5. 20
      tests/zend/dynamic_call/dynamic_call_008.phpt
  6. 32
      tests/zend/dynamic_call/dynamic_call_freeing.phpt

@ -702,6 +702,14 @@ class Translator extends \PhpAot\Core\Translator
return $this->parseInterpolatedString($expr);
case 'Expr_Cast_Int':
return $this->parseCastInt($expr);
case 'Expr_Cast_Double':
return $this->parseCastDouble($expr);
case 'Expr_Cast_Bool':
return $this->parseCastBool($expr);
case 'Expr_Cast_String':
return $this->parseCastString($expr);
case 'Expr_Cast_Array':
return $this->parseCastArray($expr);
case 'Expr_ConstFetch':
return $this->parseConstFetch($expr);
case 'Expr_UnaryMinus':
@ -710,10 +718,6 @@ class Translator extends \PhpAot\Core\Translator
return $this->parseUnaryPlus($expr);
case 'InterpolatedStringPart':
return $this->parseInterpolatedStringPart($expr);
case 'Expr_Cast_Array':
return $this->parseCastArray($expr);
case 'Expr_Cast_Double':
return $this->parseCastDouble($expr);
case 'Expr_Exit':
return $this->parseExit($expr);
default:
@ -1323,7 +1327,7 @@ class Translator extends \PhpAot\Core\Translator
if ($expr->name->getType() === self::EXPR_VARIABLE) {
$fn = $this->parseIdentifier($expr->name);
$name = '';
} else {
} elseif ($expr->name->getType() === 'Name') {
$name = $this->parseIdentifier($expr->name);
if ($this->isNativeFunction($name)) {
return self::PREFIX . $name . '(' . $this->parseCallArgs($expr->args, $name) . ')';
@ -1348,6 +1352,12 @@ class Translator extends \PhpAot\Core\Translator
break;
}
}
} else {
$tmpVar = $this->genTmpVarName();
$this->addLocalVar($tmpVar, self::TYPE_VAR);
$this->beforeStmtLines[] = $tmpVar . ' = ' . $this->parseExpr($expr->name) . ';';
$fn = $tmpVar;
$name = '';
}
if (empty($expr->args)) {
return 'php::call(' . $fn . ')';
@ -1749,6 +1759,16 @@ class Translator extends \PhpAot\Core\Translator
return $this->convertIntExpr($this->parseExpr($node->expr));
}
private function parseCastString(mixed $node): string
{
return $this->convertStringExpr($this->parseExpr($node->expr));
}
private function parseCastBool(mixed $node): string
{
return $this->convertBoolExpr($this->parseExpr($node->expr));
}
private function parseConstFetch(Node $expr): string
{
$name = $this->parseIdentifier($expr->name);
@ -2162,7 +2182,7 @@ class Translator extends \PhpAot\Core\Translator
private function parseCastDouble(mixed $expr): string
{
return 'php::to_float(' . $this->parseIdentifier($expr->expr) . ')';
return $this->convertFloatExpr($this->parseIdentifier($expr->expr));
}
private function detectFuncCallReturnType($expr): string

@ -0,0 +1,38 @@
--TEST--
Dynamic calls to scope introspection functions are forbidden
--FILE--
<?php
function test_calls($func) {
$i = 1;
try {
array_map($func, [['i' => new stdClass]]);
var_dump($i);
} catch (\Error $e) {
echo 'array_map: ' . $e->getMessage() . "\n";
}
try {
call_user_func($func, ['i' => new stdClass]);
var_dump($i);
} catch (\Error $e) {
echo 'call_user_func: ' . $e->getMessage() . "\n";
}
try {
$func(['i' => new stdClass]);
var_dump($i);
} catch (\Error $e) {
echo '$func: ' . $e->getMessage() . "\n";
}
}
function main() {
test_calls('extract');
}
?>
--EXPECTF--
array_map: Cannot call extract() dynamically
call_user_func: Cannot call extract() dynamically
$func: Cannot call extract() dynamically

@ -0,0 +1,59 @@
--TEST--
Dynamic calls to scope introspection functions are forbidden (function variations)
--FILE--
<?php
function test() {
try {
$func = 'extract';
$func(['a' => 'b']);
} catch (\Error $e) {
echo $e->getMessage() . "\n";
}
try {
$func = 'compact';
$func(['a']);
} catch (\Error $e) {
echo $e->getMessage() . "\n";
}
try {
$func = 'get_defined_vars';
$func();
} catch (\Error $e) {
echo $e->getMessage() . "\n";
}
try {
$func = 'func_get_args';
$func();
} catch (\Error $e) {
echo $e->getMessage() . "\n";
}
try {
$func = 'func_get_arg';
$func(1);
} catch (\Error $e) {
echo $e->getMessage() . "\n";
}
try {
$func = 'func_num_args';
$func();
} catch (\Error $e) {
echo $e->getMessage() . "\n";
}
}
function main() {
test();
}
?>
--EXPECT--
Cannot call extract() dynamically
Cannot call compact() dynamically
Cannot call get_defined_vars() dynamically
Cannot call func_get_args() dynamically
Cannot call func_get_arg() dynamically
Cannot call func_num_args() dynamically

@ -0,0 +1,23 @@
--TEST--
Dynamic calls to scope introspection functions are forbidden (misoptimization)
--FILE--
<?php
function test() {
$i = 1;
try {
array_map('extract', [['i' => new stdClass]]);
} catch (\Error $e) {
echo $e->getMessage() . "\n";
}
$i += 1;
var_dump($i);
}
function main() {
test();
}
?>
--EXPECT--
Cannot call extract() dynamically
int(2)

@ -0,0 +1,20 @@
--TEST--
Don't optimize dynamic call to non-dynamic one if it drops the warning
--FILE--
<?php
function test() {
try {
((string) 'extract')(['a' => 42]);
} catch (\Error $e) {
echo $e->getMessage() . "\n";
}
}
function main() {
test();
}
?>
--EXPECT--
Cannot call extract() dynamically

@ -0,0 +1,32 @@
--TEST--
Freeing of function "name" when dynamic call fails
--FILE--
<?php
try {
$bar = "bar";
("foo" . $bar)();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
$bar = ["bar"];
(["foo"] + $bar)();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
(new stdClass)();
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
?>
--EXPECT--
Invalid callback foobar, function "foobar" not found or invalid function name
Warning: Array to string conversion in Unknown on line 0
Fatal error: Invalid callback Array, array callback must have exactly two members in Unknown on line 0
Fatal error: Invalid callback stdClass::__invoke, no array or string given in Unknown on line 0
Loading…
Cancel
Save