- 新增匿名类功能测试文件 tests/aot/anonymous-classes.phpt - 新增数组展开操作符测试文件 tests/aot/array-spread.phpt - 新增箭头函数测试文件 tests/aot/arrow-functions.phpt - 新增属性注解测试文件 tests/aot/attributes.phpt - 新增构造函数属性提升测试文件 tests/aot/constructor-promotion.phpt - 新增生成器测试文件 tests/aot/generators.phpt - 新增 trait 基础功能测试文件 tests/aot/trait-basic.phpt - 更新测试运行脚本支持 main 函数自动调用 - 新增 AOT 编译器语法支持文档 docs/UNSUPPORTED_SYNTAX.md - 记录 ZVAL 与原生类型性能对比和使用建议 - 文档化扩展模式和二进制可执行文件模式差异 - 详细说明不支持的语法特性包括生成器、可变变量等pull/1/head
parent
1441a7e5be
commit
5b0346a4ad
9 changed files with 2058 additions and 0 deletions
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,171 @@ |
||||
--TEST-- |
||||
Anonymous Classes - Runtime class definition |
||||
--FILE-- |
||||
<?php |
||||
// Test basic anonymous class |
||||
function test_basic_anonymous() { |
||||
$basic = new class { |
||||
public function greet(): string { |
||||
return "Hello from anonymous class"; |
||||
} |
||||
}; |
||||
var_dump($basic->greet()); |
||||
} |
||||
|
||||
// Test anonymous class with constructor |
||||
class Greeter { |
||||
private string $greeting; |
||||
|
||||
public function __construct(string $greeting = "Hello") { |
||||
$this->greeting = $greeting; |
||||
} |
||||
|
||||
public function getGreeting(): string { |
||||
return $this->greeting; |
||||
} |
||||
} |
||||
|
||||
function test_anonymous_with_constructor() { |
||||
$withConstructor = new class("Hi") extends Greeter { |
||||
public function getGreeting(): string { |
||||
return parent::getGreeting() . " World!"; |
||||
} |
||||
}; |
||||
var_dump($withConstructor->getGreeting()); |
||||
} |
||||
|
||||
// Test anonymous class implementing interface |
||||
interface LoggerInterface { |
||||
public function log(string $message): void; |
||||
public function getLogs(): array; |
||||
} |
||||
|
||||
function test_anonymous_interface() { |
||||
$logger = new class implements LoggerInterface { |
||||
private array $logs = []; |
||||
|
||||
public function log(string $message): void { |
||||
$this->logs[] = date('Y-m-d H:i:s') . " - " . $message; |
||||
} |
||||
|
||||
public function getLogs(): array { |
||||
return $this->logs; |
||||
} |
||||
}; |
||||
|
||||
$logger->log("First message"); |
||||
$logger->log("Second message"); |
||||
var_dump(count($logger->getLogs())); |
||||
} |
||||
|
||||
// Test anonymous class with properties |
||||
function test_anonymous_properties() { |
||||
$config = new class { |
||||
public string $name = "Test"; |
||||
private int $value = 42; |
||||
|
||||
public function getValue(): int { |
||||
return $this->value; |
||||
} |
||||
|
||||
public function setValue(int $value): void { |
||||
$this->value = $value; |
||||
} |
||||
}; |
||||
|
||||
var_dump($config->name); |
||||
var_dump($config->getValue()); |
||||
$config->setValue(100); |
||||
var_dump($config->getValue()); |
||||
} |
||||
|
||||
// Test nested anonymous classes |
||||
function test_nested_anonymous() { |
||||
$outer = new class { |
||||
private object $inner; |
||||
|
||||
public function __construct() { |
||||
$this->inner = new class { |
||||
public function getMessage(): string { |
||||
return "From inner class"; |
||||
} |
||||
}; |
||||
} |
||||
|
||||
public function getInnerMessage(): string { |
||||
return $this->inner->getMessage(); |
||||
} |
||||
}; |
||||
|
||||
var_dump($outer->getInnerMessage()); |
||||
} |
||||
|
||||
// Test anonymous class in array |
||||
function test_anonymous_array() { |
||||
$classes = [ |
||||
new class { public function getType() { return "A"; } }, |
||||
new class { public function getType() { return "B"; } }, |
||||
new class { public function getType() { return "C"; } }, |
||||
]; |
||||
|
||||
foreach ($classes as $class) { |
||||
echo $class->getType() . "\n"; |
||||
} |
||||
} |
||||
|
||||
// Test static method in anonymous class |
||||
function test_anonymous_static() { |
||||
$static = new class { |
||||
private static int $counter = 0; |
||||
|
||||
public static function increment(): int { |
||||
return ++self::$counter; |
||||
} |
||||
|
||||
public static function getCounter(): int { |
||||
return self::$counter; |
||||
} |
||||
}; |
||||
|
||||
var_dump($static::increment()); |
||||
var_dump($static::increment()); |
||||
var_dump($static::getCounter()); |
||||
} |
||||
|
||||
function main() { |
||||
// Test basic anonymous |
||||
test_basic_anonymous(); |
||||
|
||||
// Test anonymous with constructor |
||||
test_anonymous_with_constructor(); |
||||
|
||||
// Test anonymous interface |
||||
test_anonymous_interface(); |
||||
|
||||
// Test anonymous properties |
||||
test_anonymous_properties(); |
||||
|
||||
// Test nested anonymous |
||||
test_nested_anonymous(); |
||||
|
||||
// Test anonymous array |
||||
test_anonymous_array(); |
||||
|
||||
// Test anonymous static |
||||
test_anonymous_static(); |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
string(26) "Hello from anonymous class" |
||||
string(9) "Hi World!" |
||||
int(2) |
||||
string(4) "Test" |
||||
int(42) |
||||
int(100) |
||||
string(16) "From inner class" |
||||
A |
||||
B |
||||
C |
||||
int(1) |
||||
int(2) |
||||
int(2) |
||||
@ -0,0 +1,195 @@ |
||||
--TEST-- |
||||
Spread Operator in Arrays - Array unpacking with ... |
||||
--FILE-- |
||||
<?php |
||||
// Test basic array spreading |
||||
function test_basic_spread() { |
||||
$part1 = [1, 2, 3]; |
||||
$part2 = [4, 5, 6]; |
||||
return [...$part1, ...$part2]; |
||||
} |
||||
|
||||
// Test spread with additional elements |
||||
function test_spread_with_elements() { |
||||
$middle = [2, 3, 4]; |
||||
return [1, ...$middle, 5]; |
||||
} |
||||
|
||||
// Test multiple spreads |
||||
function test_multiple_spreads() { |
||||
$a = [1, 2]; |
||||
$b = [3, 4]; |
||||
$c = [5, 6]; |
||||
return [...$a, ...$b, ...$c]; |
||||
} |
||||
|
||||
// Test spread with keys |
||||
function test_spread_with_keys() { |
||||
$arr1 = ['a' => 1, 'b' => 2]; |
||||
$arr2 = ['c' => 3, 'd' => 4]; |
||||
return [...$arr1, ...$arr2]; |
||||
} |
||||
|
||||
// Test spread in middle of array |
||||
function create_user_record($id, $name, $extra = []) { |
||||
return [ |
||||
'id' => $id, |
||||
'name' => $name, |
||||
...$extra, |
||||
'active' => true, |
||||
]; |
||||
} |
||||
|
||||
// Test nested spreading |
||||
function test_nested_spread() { |
||||
$inner = [7, 8]; |
||||
$outer = [1, 2, [...$inner], 9, 10]; |
||||
return $outer; |
||||
} |
||||
|
||||
// Test spread with string keys (overwriting) |
||||
function test_string_key_spread() { |
||||
$defaults = ['status' => 'active', 'role' => 'user']; |
||||
$override = ['role' => 'admin']; |
||||
return [...$defaults, ...$override]; |
||||
} |
||||
|
||||
// Test spread empty arrays |
||||
function test_spread_empty() { |
||||
$empty = []; |
||||
$data = [1, 2, 3]; |
||||
return [...$empty, ...$data, ...$empty]; |
||||
} |
||||
|
||||
function main() { |
||||
// Test basic spread |
||||
var_dump(test_basic_spread()); |
||||
|
||||
// Test spread with elements |
||||
var_dump(test_spread_with_elements()); |
||||
|
||||
// Test multiple spreads |
||||
var_dump(test_multiple_spreads()); |
||||
|
||||
// Test spread with keys |
||||
var_dump(test_spread_with_keys()); |
||||
|
||||
// Test spread in function return |
||||
var_dump(create_user_record(1, 'John', ['email' => 'john@example.com'])); |
||||
|
||||
// Test nested spread (note: this creates a nested array) |
||||
var_dump(test_nested_spread()); |
||||
|
||||
// Test string key spread (last value wins) |
||||
var_dump(test_string_key_spread()); |
||||
|
||||
// Test spread empty arrays |
||||
var_dump(test_spread_empty()); |
||||
|
||||
// Test complex real-world example |
||||
$baseConfig = ['debug' => false, 'timeout' => 30]; |
||||
$envConfig = ['timeout' => 60, 'retries' => 3]; |
||||
$config = [...$baseConfig, ...$envConfig]; |
||||
var_dump($config); |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
array(6) { |
||||
[0]=> |
||||
int(1) |
||||
[1]=> |
||||
int(2) |
||||
[2]=> |
||||
int(3) |
||||
[3]=> |
||||
int(4) |
||||
[4]=> |
||||
int(5) |
||||
[5]=> |
||||
int(6) |
||||
} |
||||
array(5) { |
||||
[0]=> |
||||
int(1) |
||||
[1]=> |
||||
int(2) |
||||
[2]=> |
||||
int(3) |
||||
[3]=> |
||||
int(4) |
||||
[4]=> |
||||
int(5) |
||||
} |
||||
array(6) { |
||||
[0]=> |
||||
int(1) |
||||
[1]=> |
||||
int(2) |
||||
[2]=> |
||||
int(3) |
||||
[3]=> |
||||
int(4) |
||||
[4]=> |
||||
int(5) |
||||
[5]=> |
||||
int(6) |
||||
} |
||||
array(4) { |
||||
["a"]=> |
||||
int(1) |
||||
["b"]=> |
||||
int(2) |
||||
["c"]=> |
||||
int(3) |
||||
["d"]=> |
||||
int(4) |
||||
} |
||||
array(4) { |
||||
["id"]=> |
||||
int(1) |
||||
["name"]=> |
||||
string(4) "John" |
||||
["email"]=> |
||||
string(16) "john@example.com" |
||||
["active"]=> |
||||
bool(true) |
||||
} |
||||
array(5) { |
||||
[0]=> |
||||
int(1) |
||||
[1]=> |
||||
int(2) |
||||
[2]=> |
||||
array(2) { |
||||
[0]=> |
||||
int(7) |
||||
[1]=> |
||||
int(8) |
||||
} |
||||
[3]=> |
||||
int(9) |
||||
[4]=> |
||||
int(10) |
||||
} |
||||
array(2) { |
||||
["status"]=> |
||||
string(6) "active" |
||||
["role"]=> |
||||
string(5) "admin" |
||||
} |
||||
array(3) { |
||||
[0]=> |
||||
int(1) |
||||
[1]=> |
||||
int(2) |
||||
[2]=> |
||||
int(3) |
||||
} |
||||
array(3) { |
||||
["debug"]=> |
||||
bool(false) |
||||
["timeout"]=> |
||||
int(60) |
||||
["retries"]=> |
||||
int(3) |
||||
} |
||||
@ -0,0 +1,201 @@ |
||||
--TEST-- |
||||
Arrow Functions - PHP 8.1+ short closure syntax |
||||
--FILE-- |
||||
<?php |
||||
// Test basic arrow function |
||||
function test_basic_arrow() { |
||||
$numbers = [1, 2, 3, 4, 5]; |
||||
$doubled = array_map(fn($n) => $n * 2, $numbers); |
||||
return $doubled; |
||||
} |
||||
|
||||
// Test arrow function with multiple parameters |
||||
function test_multi_param_arrow() { |
||||
$pairs = [[1, 2], [3, 4], [5, 6]]; |
||||
$sums = array_map(fn($a, $b) => $a + $b, ...$pairs); |
||||
return $sums; |
||||
} |
||||
|
||||
// Test arrow function capturing variables (by value) |
||||
function test_captured_variable($multiplier) { |
||||
$numbers = [1, 2, 3]; |
||||
$multiplied = array_map(fn($n) => $n * $multiplier, $numbers); |
||||
return $multiplied; |
||||
} |
||||
|
||||
// Test nested arrow functions |
||||
function test_nested_arrow() { |
||||
$numbers = [1, 2, 3, 4]; |
||||
$result = array_map( |
||||
fn($n) => array_reduce([1, 2], fn($carry, $x) => $carry * $x, $n), |
||||
$numbers |
||||
); |
||||
return $result; |
||||
} |
||||
|
||||
// Test arrow function in filter |
||||
function test_filter_arrow() { |
||||
$numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; |
||||
$evens = array_filter($numbers, fn($n) => $n % 2 === 0); |
||||
return array_values($evens); |
||||
} |
||||
|
||||
// Test arrow function in reduce |
||||
function test_reduce_arrow() { |
||||
$numbers = [1, 2, 3, 4, 5]; |
||||
$product = array_reduce($numbers, fn($carry, $n) => $carry * $n, 1); |
||||
return $product; |
||||
} |
||||
|
||||
// Test arrow function returning arrays |
||||
function test_array_return_arrow() { |
||||
$items = [1, 2, 3]; |
||||
$transformed = array_map(fn($item) => [$item, $item * 2], $items); |
||||
return $transformed; |
||||
} |
||||
|
||||
// Test chained arrow function calls |
||||
class Calculator { |
||||
private array $numbers; |
||||
|
||||
public function __construct(array $numbers) { |
||||
$this->numbers = $numbers; |
||||
} |
||||
|
||||
public function transform(callable $callback): self { |
||||
$this->numbers = array_map($callback, $this->numbers); |
||||
return $this; |
||||
} |
||||
|
||||
public function filter(callable $callback): self { |
||||
$this->numbers = array_values(array_filter($this->numbers, $callback)); |
||||
return $this; |
||||
} |
||||
|
||||
public function getNumbers(): array { |
||||
return $this->numbers; |
||||
} |
||||
} |
||||
|
||||
function main() { |
||||
// Test basic arrow |
||||
var_dump(test_basic_arrow()); |
||||
|
||||
// Test multi-param arrow |
||||
var_dump(test_multi_param_arrow()); |
||||
|
||||
// Test captured variable |
||||
var_dump(test_captured_variable(10)); |
||||
var_dump(test_captured_variable(100)); |
||||
|
||||
// Test nested arrow |
||||
var_dump(test_nested_arrow()); |
||||
|
||||
// Test filter arrow |
||||
var_dump(test_filter_arrow()); |
||||
|
||||
// Test reduce arrow |
||||
var_dump(test_reduce_arrow()); |
||||
|
||||
// Test array return |
||||
var_dump(test_array_return_arrow()); |
||||
|
||||
// Test chained operations |
||||
$calc = new Calculator([1, 2, 3, 4, 5]); |
||||
$result = $calc |
||||
->transform(fn($n) => $n * 2) |
||||
->filter(fn($n) => $n > 5) |
||||
->transform(fn($n) => $n + 1) |
||||
->getNumbers(); |
||||
var_dump($result); |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
array(5) { |
||||
[0]=> |
||||
int(2) |
||||
[1]=> |
||||
int(4) |
||||
[2]=> |
||||
int(6) |
||||
[3]=> |
||||
int(8) |
||||
[4]=> |
||||
int(10) |
||||
} |
||||
array(2) { |
||||
[0]=> |
||||
int(4) |
||||
[1]=> |
||||
int(6) |
||||
} |
||||
array(3) { |
||||
[0]=> |
||||
int(10) |
||||
[1]=> |
||||
int(20) |
||||
[2]=> |
||||
int(30) |
||||
} |
||||
array(3) { |
||||
[0]=> |
||||
int(100) |
||||
[1]=> |
||||
int(200) |
||||
[2]=> |
||||
int(300) |
||||
} |
||||
array(4) { |
||||
[0]=> |
||||
int(2) |
||||
[1]=> |
||||
int(4) |
||||
[2]=> |
||||
int(6) |
||||
[3]=> |
||||
int(8) |
||||
} |
||||
array(5) { |
||||
[0]=> |
||||
int(2) |
||||
[1]=> |
||||
int(4) |
||||
[2]=> |
||||
int(6) |
||||
[3]=> |
||||
int(8) |
||||
[4]=> |
||||
int(10) |
||||
} |
||||
int(120) |
||||
array(3) { |
||||
[0]=> |
||||
array(2) { |
||||
[0]=> |
||||
int(1) |
||||
[1]=> |
||||
int(2) |
||||
} |
||||
[1]=> |
||||
array(2) { |
||||
[0]=> |
||||
int(2) |
||||
[1]=> |
||||
int(4) |
||||
} |
||||
[2]=> |
||||
array(2) { |
||||
[0]=> |
||||
int(3) |
||||
[1]=> |
||||
int(6) |
||||
} |
||||
} |
||||
array(3) { |
||||
[0]=> |
||||
int(7) |
||||
[1]=> |
||||
int(9) |
||||
[2]=> |
||||
int(11) |
||||
} |
||||
@ -0,0 +1,120 @@ |
||||
--TEST-- |
||||
Attributes (Annotations) - PHP 8+ metadata syntax |
||||
--SKIPIF-- |
||||
<?php |
||||
echo "skip Attributes/Annotations not supported in AOT"; |
||||
?> |
||||
--FILE-- |
||||
<?php |
||||
// Define attribute classes |
||||
#[Attribute(Attribute::TARGET_CLASS)] |
||||
class Route { |
||||
public string $path; |
||||
public array $methods; |
||||
|
||||
public function __construct(string $path, array $methods = ['GET']) { |
||||
$this->path = $path; |
||||
$this->methods = $methods; |
||||
} |
||||
} |
||||
|
||||
#[Attribute(Attribute::TARGET_METHOD)] |
||||
class Cache { |
||||
public int $ttl; |
||||
|
||||
public function __construct(int $ttl = 3600) { |
||||
$this->ttl = $ttl; |
||||
} |
||||
} |
||||
|
||||
#[Attribute(Attribute::TARGET_PROPERTY)] |
||||
class Column { |
||||
public string $name; |
||||
public string $type; |
||||
|
||||
public function __construct(string $name, string $type = 'string') { |
||||
$this->name = $name; |
||||
$this->type = $type; |
||||
} |
||||
} |
||||
|
||||
// Use attributes |
||||
#[Route('/api/users', ['GET', 'POST'])] |
||||
class UserController { |
||||
#[Column('id', 'int')] |
||||
private int $id; |
||||
|
||||
#[Column('name', 'string')] |
||||
private string $name; |
||||
|
||||
#[Cache(ttl: 1800)] |
||||
public function getUsers() { |
||||
return "Getting users"; |
||||
} |
||||
|
||||
#[Cache(ttl: 3600)] |
||||
public function getUser($id) { |
||||
return "Getting user: " . $id; |
||||
} |
||||
} |
||||
|
||||
#[Route('/api/posts')] |
||||
class PostController { |
||||
#[Cache] |
||||
public function getPosts() { |
||||
return "Getting posts"; |
||||
} |
||||
} |
||||
|
||||
function main() { |
||||
// Test class attributes |
||||
$userController = new ReflectionClass(UserController::class); |
||||
$attributes = $userController->getAttributes(); |
||||
var_dump(count($attributes)); |
||||
|
||||
$routeAttr = $attributes[0]->newInstance(); |
||||
var_dump($routeAttr->path); |
||||
var_dump($routeAttr->methods); |
||||
|
||||
// Test method attributes |
||||
$getMethod = $userController->getMethod('getUsers'); |
||||
$methodAttrs = $getMethod->getAttributes(); |
||||
var_dump(count($methodAttrs)); |
||||
|
||||
$cacheAttr = $methodAttrs[0]->newInstance(); |
||||
var_dump($cacheAttr->ttl); |
||||
|
||||
// Test property attributes |
||||
$idProp = $userController->getProperty('id'); |
||||
$propAttrs = $idProp->getAttributes(); |
||||
var_dump(count($propAttrs)); |
||||
|
||||
$columnAttr = $propAttrs[0]->newInstance(); |
||||
var_dump($columnAttr->name); |
||||
var_dump($columnAttr->type); |
||||
|
||||
// Test another class |
||||
$postController = new ReflectionClass(PostController::class); |
||||
$postAttrs = $postController->getAttributes(); |
||||
var_dump(count($postAttrs)); |
||||
|
||||
$postRoute = $postAttrs[0]->newInstance(); |
||||
var_dump($postRoute->path); |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
int(1) |
||||
string(11) "/api/users" |
||||
array(2) { |
||||
[0]=> |
||||
string(3) "GET" |
||||
[1]=> |
||||
string(4) "POST" |
||||
} |
||||
int(1) |
||||
int(1800) |
||||
int(1) |
||||
string(2) "id" |
||||
string(3) "int" |
||||
int(1) |
||||
string(11) "/api/posts" |
||||
@ -0,0 +1,140 @@ |
||||
--TEST-- |
||||
Constructor Property Promotion - PHP 8+ concise class syntax |
||||
--FILE-- |
||||
<?php |
||||
// Test basic constructor promotion |
||||
class Point { |
||||
public function __construct( |
||||
public float $x = 0.0, |
||||
public float $y = 0.0 |
||||
) {} |
||||
|
||||
public function distance(): float { |
||||
return sqrt($this->x * $this->x + $this->y * $this->y); |
||||
} |
||||
} |
||||
|
||||
// Test with visibility modifiers |
||||
class User { |
||||
public function __construct( |
||||
public string $name, |
||||
private string $email, |
||||
protected int $age = 18 |
||||
) {} |
||||
|
||||
public function getEmail(): string { |
||||
return $this->email; |
||||
} |
||||
|
||||
public function getAge(): int { |
||||
return $this->age; |
||||
} |
||||
} |
||||
|
||||
// Test with nullable types |
||||
class Product { |
||||
public function __construct( |
||||
public string $name, |
||||
public float $price, |
||||
public ?string $description = null, |
||||
public int $quantity = 0 |
||||
) {} |
||||
|
||||
public function getDescription(): string { |
||||
return $this->description ?? 'No description'; |
||||
} |
||||
} |
||||
|
||||
// Test mixed traditional and promoted |
||||
class Book { |
||||
private static int $count = 0; |
||||
|
||||
public function __construct( |
||||
public string $title, |
||||
public string $author, |
||||
private float $price |
||||
) { |
||||
self::$count++; |
||||
} |
||||
|
||||
public function getPriceWithTax(float $taxRate): float { |
||||
return $this->price * (1 + $taxRate); |
||||
} |
||||
|
||||
public static function getCount(): int { |
||||
return self::$count; |
||||
} |
||||
} |
||||
|
||||
// Test readonly properties (PHP 8.1+) |
||||
class Coordinate { |
||||
public function __construct( |
||||
public readonly float $latitude, |
||||
public readonly float $longitude |
||||
) {} |
||||
} |
||||
|
||||
function main() { |
||||
// Test basic promotion |
||||
$point = new Point(3.0, 4.0); |
||||
var_dump($point->x); |
||||
var_dump($point->y); |
||||
var_dump($point->distance()); |
||||
|
||||
$origin = new Point(); |
||||
var_dump($origin->x); |
||||
var_dump($origin->y); |
||||
|
||||
// Test with visibility |
||||
$user = new User('Alice', 'alice@example.com', 25); |
||||
var_dump($user->name); |
||||
var_dump($user->getEmail()); |
||||
var_dump($user->getAge()); |
||||
|
||||
// Test nullable |
||||
$product1 = new Product('Laptop', 999.99, 'High-performance laptop', 10); |
||||
var_dump($product1->name); |
||||
var_dump($product1->price); |
||||
var_dump($product1->getDescription()); |
||||
var_dump($product1->quantity); |
||||
|
||||
$product2 = new Product('Mouse', 29.99); |
||||
var_dump($product2->name); |
||||
var_dump($product2->getDescription()); |
||||
|
||||
// Test mixed |
||||
$book1 = new Book('PHP Guide', 'John Doe', 49.99); |
||||
var_dump($book1->title); |
||||
var_dump($book1->author); |
||||
var_dump($book1->getPriceWithTax(0.1)); |
||||
|
||||
$book2 = new Book('Advanced PHP', 'Jane Smith', 59.99); |
||||
var_dump(Book::getCount()); |
||||
|
||||
// Test readonly |
||||
$coord = new Coordinate(40.7128, -74.0060); |
||||
var_dump($coord->latitude); |
||||
var_dump($coord->longitude); |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
float(3) |
||||
float(4) |
||||
float(5) |
||||
float(0) |
||||
float(0) |
||||
string(5) "Alice" |
||||
string(17) "alice@example.com" |
||||
int(25) |
||||
string(6) "Laptop" |
||||
float(999.99) |
||||
string(23) "High-performance laptop" |
||||
int(10) |
||||
string(5) "Mouse" |
||||
string(14) "No description" |
||||
string(9) "PHP Guide" |
||||
string(8) "John Doe" |
||||
float(54.989000000000004) |
||||
int(2) |
||||
float(40.7128) |
||||
float(-74.006) |
||||
@ -0,0 +1,92 @@ |
||||
--TEST-- |
||||
Generators - Yield keyword and generator functions |
||||
--SKIPIF-- |
||||
<?php |
||||
echo "skip Generator syntax not supported in AOT"; |
||||
?> |
||||
--FILE-- |
||||
<?php |
||||
// Test basic generator |
||||
function range_generator($start, $end) { |
||||
for ($i = $start; $i <= $end; $i++) { |
||||
yield $i; |
||||
} |
||||
} |
||||
|
||||
// Test generator with keys |
||||
function keyed_generator() { |
||||
yield 'a' => 1; |
||||
yield 'b' => 2; |
||||
yield 'c' => 3; |
||||
} |
||||
|
||||
// Test generator sending values |
||||
function echo_generator() { |
||||
while (true) { |
||||
$value = yield; |
||||
echo "Received: " . $value . "\n"; |
||||
} |
||||
} |
||||
|
||||
// Test infinite generator |
||||
function infinite_sequence() { |
||||
$i = 1; |
||||
while (true) { |
||||
yield $i++; |
||||
if ($i > 5) break; |
||||
} |
||||
} |
||||
|
||||
function main() { |
||||
// Test basic generator |
||||
echo "Basic generator:\n"; |
||||
foreach (range_generator(1, 5) as $num) { |
||||
var_dump($num); |
||||
} |
||||
|
||||
// Test keyed generator |
||||
echo "\nKeyed generator:\n"; |
||||
foreach (keyed_generator() as $key => $value) { |
||||
echo $key . ": "; |
||||
var_dump($value); |
||||
} |
||||
|
||||
// Test limited infinite generator |
||||
echo "\nInfinite sequence (limited):\n"; |
||||
foreach (infinite_sequence() as $num) { |
||||
var_dump($num); |
||||
} |
||||
|
||||
// Test generator object |
||||
$gen = range_generator(10, 12); |
||||
var_dump($gen->valid()); |
||||
$gen->rewind(); |
||||
var_dump($gen->valid()); |
||||
var_dump($gen->current()); |
||||
$gen->next(); |
||||
var_dump($gen->current()); |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
Basic generator: |
||||
int(1) |
||||
int(2) |
||||
int(3) |
||||
int(4) |
||||
int(5) |
||||
|
||||
Keyed generator: |
||||
a: int(1) |
||||
b: int(2) |
||||
c: int(3) |
||||
|
||||
Infinite sequence (limited): |
||||
int(1) |
||||
int(2) |
||||
int(3) |
||||
int(4) |
||||
int(5) |
||||
bool(true) |
||||
bool(true) |
||||
int(1) |
||||
int(2) |
||||
@ -0,0 +1,97 @@ |
||||
--TEST-- |
||||
Traits - Basic functionality and method inheritance |
||||
--SKIPIF-- |
||||
<?php |
||||
echo "skip Traits not yet supported in AOT"; |
||||
?> |
||||
--FILE-- |
||||
<?php |
||||
// Test basic trait usage |
||||
trait Greeting { |
||||
public function sayHello() { |
||||
return "Hello"; |
||||
} |
||||
|
||||
public function sayGoodbye() { |
||||
return "Goodbye"; |
||||
} |
||||
} |
||||
|
||||
class Person { |
||||
use Greeting; |
||||
|
||||
private $name; |
||||
|
||||
public function __construct($name) { |
||||
$this->name = $name; |
||||
} |
||||
|
||||
public function getName() { |
||||
return $this->name; |
||||
} |
||||
} |
||||
|
||||
// Test trait with abstract methods |
||||
trait Loggable { |
||||
abstract public function getTableName(); |
||||
|
||||
public function log($message) { |
||||
return "[" . $this->getTableName() . "] " . $message; |
||||
} |
||||
} |
||||
|
||||
class User { |
||||
use Loggable; |
||||
|
||||
public function getTableName() { |
||||
return "users"; |
||||
} |
||||
} |
||||
|
||||
// Test multiple traits |
||||
trait Timestamps { |
||||
public function getCreatedAt() { |
||||
return "2024-01-01 00:00:00"; |
||||
} |
||||
|
||||
public function getUpdatedAt() { |
||||
return "2024-01-02 00:00:00"; |
||||
} |
||||
} |
||||
|
||||
class Post { |
||||
use Greeting, Timestamps; |
||||
|
||||
public function getTitle() { |
||||
return "Test Post"; |
||||
} |
||||
} |
||||
|
||||
function main() { |
||||
// Test basic trait |
||||
$person = new Person("John"); |
||||
var_dump($person->sayHello()); |
||||
var_dump($person->sayGoodbye()); |
||||
var_dump($person->getName()); |
||||
|
||||
// Test trait with abstract method |
||||
$user = new User(); |
||||
var_dump($user->log("User created")); |
||||
|
||||
// Test multiple traits |
||||
$post = new Post(); |
||||
var_dump($post->sayHello()); |
||||
var_dump($post->getTitle()); |
||||
var_dump($post->getCreatedAt()); |
||||
var_dump($post->getUpdatedAt()); |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
string(5) "Hello" |
||||
string(7) "Goodbye" |
||||
string(4) "John" |
||||
string(22) "[users] User created" |
||||
string(5) "Hello" |
||||
string(9) "Test Post" |
||||
string(19) "2024-01-01 00:00:00" |
||||
string(19) "2024-01-02 00:00:00" |
||||
Loading…
Reference in new issue