diff --git a/src/Php/Generator/ClosureGenerator.php b/src/Php/Generator/ClosureGenerator.php index c3dfa378..07e29909 100644 --- a/src/Php/Generator/ClosureGenerator.php +++ b/src/Php/Generator/ClosureGenerator.php @@ -27,7 +27,9 @@ trait ClosureGenerator protected function genClosure(NodeAbstract $expr, array $params, callable $bodyGenCb, array $uses = [], bool $useCurrentScope = false): string { $tmpVar = $this->genTmpVarName(); - $capture = $useCurrentScope ? '&' : ''; + // 必须使用 = 捕获,不能使用 & ,否则可能会出现悬空指针 + // 在 PHP 中 = 赋值是浅拷贝,仅增加一次引用计数,和 zval (16 字节) 封装的赋值 + $capture = $useCurrentScope ? '=' : ''; $code = $this->getIndent() . 'php::ClosureFn ' . $tmpVar . ' = [' . $capture . '](' diff --git a/tests/aot/backed-enum.phpt b/tests/aot/backed-enum.phpt new file mode 100644 index 00000000..81c58014 --- /dev/null +++ b/tests/aot/backed-enum.phpt @@ -0,0 +1,87 @@ +--TEST-- +Backed Enums (PHP 8.1+) +--FILE-- + 'Pending', + self::Active => 'Active', + self::Suspended => 'Suspended', + self::Closed => 'Closed', + }; + } +} + +// Test backed enum with string values +enum Color: string { + case Red = 'red'; + case Green = 'green'; + case Blue = 'blue'; + + public function rgb(): array { + return match($this) { + self::Red => [255, 0, 0], + self::Green => [0, 255, 0], + self::Blue => [0, 0, 255], + }; + } +} + +// Test enum in switch +function processStatus(Status $status): string { + return match($status) { + Status::Pending => 'Task is pending', + Status::Active => 'Task is active', + Status::Suspended => 'Task is suspended', + Status::Closed => 'Task is closed', + }; +} + +function main() { + var_dump(Status::Pending->value); + var_dump(Status::Active->label()); + var_dump(Status::from(2)->name); + + var_dump(Color::Red->value); + var_dump(Color::Green->rgb()); + + var_dump(processStatus(Status::Active)); + + // Test enum comparison + var_dump(Status::Active === Status::Active); + var_dump(Status::Pending !== Status::Active); + + // Test enum array usage + $statuses = [Status::Pending, Status::Active, Status::Closed]; + foreach ($statuses as $status) { + var_dump($status->label()); + } +} +?> +--EXPECT-- +int(0) +string(6) "Active" +string(9) "Suspended" +string(3) "red" +array(3) { + [0]=> + int(0) + [1]=> + int(255) + [2]=> + int(0) +} +string(14) "Task is active" +bool(true) +bool(true) +string(7) "Pending" +string(6) "Active" +string(6) "Closed" diff --git a/tests/aot/callable/arrow-fn.phpt b/tests/aot/callable/arrow-fn.phpt new file mode 100644 index 00000000..dcb5e911 --- /dev/null +++ b/tests/aot/callable/arrow-fn.phpt @@ -0,0 +1,22 @@ +--TEST-- +First-Class Callable Syntax (PHP 8.1+) +--FILE-- + $value * $factor; +} + +function main() { + // Test callable returning callable + $double = multiplier(2); + $triple = multiplier(3); + + var_dump($double(5)); + var_dump($triple(5)); +} +?> +--EXPECT-- +int(10) +int(15) diff --git a/tests/aot/callable/misc.phpt b/tests/aot/callable/misc.phpt new file mode 100644 index 00000000..4aaa361f --- /dev/null +++ b/tests/aot/callable/misc.phpt @@ -0,0 +1,110 @@ +--TEST-- +First-Class Callable Syntax (PHP 8.1+) +--FILE-- + $value * $factor; +} + +function main() { + $callable = 'add'; + var_dump(array_map($callable, [1, 2, 3], [4, 5, 6])); + + $staticCallable = ['Math', 'multiply']; + var_dump(array_reduce([[2, 3], [4, 5]], fn($carry, $item) => $carry + call_user_func_array($staticCallable, $item), 0)); + + $math = new Math(); + $instanceCallable = [$math, 'divide']; + var_dump(call_user_func($instanceCallable, 10, 4)); + + // Test first-class callable with arrow functions + $filter = fn(array $arr): array => array_filter($arr, fn($n) => $n > 0); + var_dump($filter([1, -2, 3, -4, 5])); + + // Test callable in array operations + $numbers = range(1, 5); + $doubled = array_map(fn($n) => $n * 2, $numbers); + var_dump($doubled); + + $squared = array_map(fn($n) => $n ** 2, $numbers); + var_dump($squared); + + // Test callable with usort + $unsorted = [5, 2, 8, 1, 9]; + usort($unsorted, fn($a, $b) => $a <=> $b); + var_dump($unsorted); +} +?> +--EXPECT-- +array(3) { + [0]=> + int(5) + [1]=> + int(7) + [2]=> + int(9) +} +int(26) +float(2.5) +array(3) { + [0]=> + int(1) + [2]=> + int(3) + [4]=> + int(5) +} +array(5) { + [0]=> + int(2) + [1]=> + int(4) + [2]=> + int(6) + [3]=> + int(8) + [4]=> + int(10) +} +array(5) { + [0]=> + int(1) + [1]=> + int(4) + [2]=> + int(9) + [3]=> + int(16) + [4]=> + int(25) +} +array(5) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(5) + [3]=> + int(8) + [4]=> + int(9) +}