diff --git a/src/Php/Translator.php b/src/Php/Translator.php index 5cc05d3b..73da867d 100644 --- a/src/Php/Translator.php +++ b/src/Php/Translator.php @@ -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 diff --git a/tests/zend/dynamic_call/dynamic_call_005.phpt b/tests/zend/dynamic_call/dynamic_call_005.phpt new file mode 100644 index 00000000..b4391285 --- /dev/null +++ b/tests/zend/dynamic_call/dynamic_call_005.phpt @@ -0,0 +1,38 @@ +--TEST-- +Dynamic calls to scope introspection functions are forbidden +--FILE-- + 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 diff --git a/tests/zend/dynamic_call/dynamic_call_006.phpt b/tests/zend/dynamic_call/dynamic_call_006.phpt new file mode 100644 index 00000000..097678a9 --- /dev/null +++ b/tests/zend/dynamic_call/dynamic_call_006.phpt @@ -0,0 +1,59 @@ +--TEST-- +Dynamic calls to scope introspection functions are forbidden (function variations) +--FILE-- + '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 diff --git a/tests/zend/dynamic_call/dynamic_call_007.phpt b/tests/zend/dynamic_call/dynamic_call_007.phpt new file mode 100644 index 00000000..d4a4edbb --- /dev/null +++ b/tests/zend/dynamic_call/dynamic_call_007.phpt @@ -0,0 +1,23 @@ +--TEST-- +Dynamic calls to scope introspection functions are forbidden (misoptimization) +--FILE-- + new stdClass]]); + } catch (\Error $e) { + echo $e->getMessage() . "\n"; + } + $i += 1; + var_dump($i); +} + +function main() { + test(); +} +?> +--EXPECT-- +Cannot call extract() dynamically +int(2) diff --git a/tests/zend/dynamic_call/dynamic_call_008.phpt b/tests/zend/dynamic_call/dynamic_call_008.phpt new file mode 100644 index 00000000..3d41a7db --- /dev/null +++ b/tests/zend/dynamic_call/dynamic_call_008.phpt @@ -0,0 +1,20 @@ +--TEST-- +Don't optimize dynamic call to non-dynamic one if it drops the warning +--FILE-- + 42]); + } catch (\Error $e) { + echo $e->getMessage() . "\n"; + } +} + +function main() { + test(); +} + +?> +--EXPECT-- +Cannot call extract() dynamically diff --git a/tests/zend/dynamic_call/dynamic_call_freeing.phpt b/tests/zend/dynamic_call/dynamic_call_freeing.phpt new file mode 100644 index 00000000..7af120d5 --- /dev/null +++ b/tests/zend/dynamic_call/dynamic_call_freeing.phpt @@ -0,0 +1,32 @@ +--TEST-- +Freeing of function "name" when dynamic call fails +--FILE-- +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 \ No newline at end of file