test: 添加AOT测试及eval()作用域限制文档

pull/1/head
韩天峰 3 months ago
parent 184090ec84
commit 05a409ac37
  1. 58
      docs/UNSUPPORTED_SYNTAX.md
  2. 55
      tests/aot/assign-op-extra.phpt
  3. 20
      tests/aot/break-continue-level.phpt
  4. 55
      tests/aot/casts.phpt
  5. 21
      tests/aot/error-suppress.phpt
  6. 58
      tests/aot/foreach-complex.phpt
  7. 42
      tests/aot/goto.phpt
  8. 31
      tests/aot/heredoc-nowdoc.phpt
  9. 34
      tests/aot/instanceof-test.phpt
  10. 48
      tests/aot/isset-complex.phpt
  11. 23
      tests/aot/object-cast.phpt
  12. 58
      tests/aot/ternary-shorthand.phpt
  13. 45
      tests/aot/unset.phpt

@ -2203,6 +2203,64 @@ $firstChar = safeCharAccess($str, 0);
---
### 12. eval() 中的变量作用域限制
**状态**: 部分支持
**PHP 版本**: 所有版本
**描述**: AOT 编译器支持 `eval()` 语言结构,但 eval 内执行的 PHP 代码无法访问编译后函数的局部变量,只能通过 `$GLOBALS``return` 语句与编译代码交互。
**示例代码**:
```
<?php
function main() {
$x = 10;
// ❌ 限制:eval 中无法访问编译后的局部变量
eval('$x += 5;'); // Warning: Undefined variable $x
var_dump($x); // 仍然是 10,未被修改
// ✅ 正确:通过 return 返回值
$result = eval('return 10 + 5;');
var_dump($result); // int(15)
// ✅ 正确:通过 $GLOBALS 交互
eval('$GLOBALS["__v"] = 42;');
var_dump($GLOBALS["__v"]); // int(42)
// ❌ 限制:复杂表达式中的局部变量也不可用
$name = "world";
eval('echo "Hello, $name";'); // Warning: Undefined variable $name
}
```
**原因**:
- AOT 编译器将 PHP 局部变量编译为 C++ 栈变量,eval 在独立的 PHP 运行时上下文中执行
- eval 内的 PHP 代码由 Zend VM 解释执行,无法感知编译后的 C++ 栈帧
- 这是 AOT 编译模式与动态解释执行的本质冲突
**相关测试文件**:
- `tests/aot/eval-test.phpt`
**替代方案**:
1. **使用 return 返回值**
```php
$result = eval('return some_computation();');
```
2. **使用 $GLOBALS 进行数据交换**
```php
eval('$GLOBALS["key"] = $value;');
```
3. **将变量值嵌入 eval 字符串**
```php
$x = 10;
eval('$result = ' . $x . ' + 5;');
return $result;
```
---
### 7. 游离代码(全局可执行表达式)
**状态**: 不支持

@ -0,0 +1,55 @@
--TEST--
Extended assignment operators: %=, <<=, >>=, &=, |=, ^=
--FILE--
<?php
function main() {
// Modulo assignment
$a = 17;
$a %= 5;
var_dump($a);
// Left shift assignment (Expr_AssignOp_ShiftLeft)
$b = 1;
$b <<= 3;
var_dump($b);
// Right shift assignment
$c = 16;
$c >>= 2;
var_dump($c);
// Bitwise AND assignment
$d = 0xFF;
$d &= 0x0F;
var_dump($d);
// Bitwise OR assignment
$e = 0xF0;
$e |= 0x0F;
var_dump($e);
// Bitwise XOR assignment
$f = 0xFF;
$f ^= 0x0F;
var_dump($f);
// Combined
$x = 100;
$x %= 7;
$x += 10;
var_dump($x);
echo "done\n";
}
?>
--EXPECT--
int(2)
int(8)
int(4)
int(15)
int(255)
int(240)
int(12)
done

@ -0,0 +1,20 @@
--TEST--
Break and continue with numeric levels
--SKIPIF--
<?php
echo "skip Break/continue with levels > 1 not supported in AOT";
?>
--FILE--
<?php
// break 2
for ($i = 0; $i < 3; $i++) {
for ($j = 0; $j < 3; $j++) {
if ($i == 1 && $j == 2) {
break 2;
}
}
}
echo "done\n";
?>
--EXPECT--
done

@ -0,0 +1,55 @@
--TEST--
Type casts: (int), (float), (string), (bool)
--FILE--
<?php
function main() {
// Integer cast
var_dump((int) "123");
var_dump((int) 3.14);
// Float cast
var_dump((float) "3.14");
var_dump((float) 10);
// Bool cast with literals — triggers toBool(0L)/toBool(1L) code path
var_dump((bool) 1);
var_dump((bool) 0);
var_dump((bool) "hello");
var_dump((bool) "");
// String cast
var_dump((string) 123);
var_dump((string) true);
var_dump((string) false);
// Bool cast with variables
$one = 1;
$zero = 0;
var_dump((bool) $one);
var_dump((bool) $zero);
// Nested casts
$val = (int) (string) (float) "5.7";
var_dump($val);
echo "done\n";
}
?>
--EXPECT--
int(123)
int(3)
float(3.14)
float(10)
bool(true)
bool(false)
bool(true)
bool(false)
string(3) "123"
string(1) "1"
string(0) ""
bool(true)
bool(false)
int(5)
done

@ -0,0 +1,21 @@
--TEST--
Error suppression operator @
--FILE--
<?php
function main() {
$arr = ["x" => 1];
$ret = @$arr["missing"];
var_dump($ret);
$result = @file_get_contents("/nonexistent/file/path");
var_dump($result);
echo "done\n";
}
?>
--EXPECT--
NULL
bool(false)
done

@ -0,0 +1,58 @@
--TEST--
foreach with key-value and nested structures
--FILE--
<?php
function main() {
// Simple key-value foreach
$arr = ["a" => 1, "b" => 2, "c" => 3];
$keys = [];
$vals = [];
foreach ($arr as $k => $v) {
$keys[] = $k;
$vals[] = $v;
}
var_dump($keys);
var_dump($vals);
// Nested foreach
$matrix = [[1, 2], [3, 4]];
$sum = 0;
foreach ($matrix as $row) {
foreach ($row as $val) {
$sum += $val;
}
}
var_dump($sum);
// Foreach on array literal
$count = 0;
foreach ([10, 20, 30] as $item) {
$count += $item;
}
var_dump($count);
echo "done\n";
}
?>
--EXPECT--
array(3) {
[0]=>
string(1) "a"
[1]=>
string(1) "b"
[2]=>
string(1) "c"
}
array(3) {
[0]=>
int(1)
[1]=>
int(2)
[2]=>
int(3)
}
int(10)
int(60)
done

@ -0,0 +1,42 @@
--TEST--
Goto and label statements
--FILE--
<?php
function main() {
$result = 0;
// Forward goto
goto forward;
$result += 100; // skipped
forward:
$result += 1;
// Backward goto
$i = 0;
loop_start:
$i++;
if ($i < 10) {
goto loop_start;
}
$result += $i;
// Goto out of nested structure
$found = false;
for ($j = 0; $j < 5; $j++) {
for ($k = 0; $k < 5; $k++) {
if ($j == 3 && $k == 2) {
$found = true;
goto found_label;
}
}
}
found_label:
$result += ($found ? 10 : 0);
var_dump($result);
}
?>
--EXPECT--
int(21)

@ -0,0 +1,31 @@
--TEST--
Heredoc and nowdoc string syntax
--FILE--
<?php
function main() {
$name = "World";
$heredoc1 = <<<EOT
Hello, $name!
This is a heredoc string.
EOT;
$nowdoc1 = <<<'NOW'
Hello, $name!
This does NOT interpolate.
NOW;
var_dump($heredoc1);
var_dump($nowdoc1);
echo "done\n";
}
?>
--EXPECT--
string(39) "Hello, World!
This is a heredoc string."
string(40) "Hello, $name!
This does NOT interpolate."
done

@ -0,0 +1,34 @@
--TEST--
instanceof operator
--FILE--
<?php
class Base {}
class Derived extends Base {}
interface MyInterface {}
class Implementor implements MyInterface {}
function main() {
$base = new Base();
$derived = new Derived();
$implem = new Implementor();
var_dump($base instanceof Base);
var_dump($derived instanceof Base);
var_dump($base instanceof Derived);
var_dump($implem instanceof MyInterface);
var_dump($base instanceof MyInterface);
var_dump(null instanceof Base);
echo "done\n";
}
?>
--EXPECT--
bool(true)
bool(true)
bool(false)
bool(true)
bool(false)
bool(false)
done

@ -0,0 +1,48 @@
--TEST--
isset with multiple arguments and edge cases
--FILE--
<?php
function main() {
$a = "hello";
$b = null;
$c = 0;
// Single isset
var_dump(isset($a));
var_dump(isset($b));
var_dump(isset($c));
// Multi-arg isset
var_dump(isset($a, $b));
var_dump(isset($a, $c));
// Array isset
$arr = ["x" => 1, "y" => null];
var_dump(isset($arr["x"]));
var_dump(isset($arr["y"]));
var_dump(isset($arr["z"]));
// Nested isset
$data = ["user" => ["name" => "Alice"]];
var_dump(isset($data["user"]["name"]));
var_dump(isset($data["user"]["email"]));
var_dump(isset($data["missing"]["key"]));
echo "done\n";
}
?>
--EXPECT--
bool(true)
bool(false)
bool(true)
bool(false)
bool(true)
bool(true)
bool(false)
bool(false)
bool(true)
bool(false)
bool(false)
done

@ -0,0 +1,23 @@
--TEST--
Object cast (object) from array
--FILE--
<?php
function main() {
$data = (object) ["name" => "test", "value" => 42];
var_dump($data->name);
var_dump($data->value);
$empty = (object) [];
var_dump((array) $empty);
echo "done\n";
}
?>
--EXPECT--
string(4) "test"
int(42)
array(0) {
}
done

@ -0,0 +1,58 @@
--TEST--
Ternary shorthand operator ?:
--FILE--
<?php
function main() {
// Basic ternary shorthand
$a = "hello";
$result1 = $a ?: "default";
var_dump($result1);
// Falsy value with shorthand
$b = "";
$result2 = $b ?: "empty";
var_dump($result2);
// Zero with shorthand
$c = 0;
$result3 = $c ?: 42;
var_dump($result3);
// Null with shorthand
$result4 = null ?: "fallback";
var_dump($result4);
// Chained shorthand
$val = null;
$result5 = $val ?: false ?: "final";
var_dump($result5);
// Array item with shorthand
$arr = ["key" => "present"];
$result6 = $arr["missing"] ?: "not found";
var_dump($result6);
$result7 = $arr["key"] ?: "not found";
var_dump($result7);
// Nested ternary shorthand
$x = "";
$y = "hello";
$result8 = $x ?: ($y ?: "neither");
var_dump($result8);
echo "done\n";
}
?>
--EXPECT--
string(5) "hello"
string(5) "empty"
int(42)
string(8) "fallback"
string(5) "final"
string(9) "not found"
string(7) "present"
string(5) "hello"
done

@ -0,0 +1,45 @@
--TEST--
unset on arrays and variables
--FILE--
<?php
function main() {
// Unset variable
$a = 10;
unset($a);
var_dump(isset($a));
// Unset array element
$arr = ["x" => 1, "y" => 2, "z" => 3];
unset($arr["y"]);
var_dump(count($arr));
var_dump(isset($arr["y"]));
var_dump($arr["x"]);
var_dump($arr["z"]);
// Unset last element
$arr2 = [1, 2, 3, 4];
unset($arr2[3]);
var_dump(count($arr2));
// Unset with variable key
$items = ["a" => 1, "b" => 2, "c" => 3];
$key = "b";
unset($items[$key]);
var_dump(count($items));
var_dump(isset($items["b"]));
echo "done\n";
}
?>
--EXPECT--
bool(false)
int(2)
bool(false)
int(1)
int(3)
int(3)
int(2)
bool(false)
done
Loading…
Cancel
Save