- 修改generators.phpt使用exit替代echo进行跳过提示 - 新增iterators.phpt测试文件实现Iterator和IteratorAggregate接口 - 添加Range类测试基于生成器的迭代器聚合 - 实现FilterIterator类测试过滤功能 - 新增match-expression.phpt测试PHP8+匹配表达式功能 - 测试基本匹配、多重条件、严格比较和嵌套匹配场景pull/1/head
parent
23f2fa48af
commit
8bf5c9c806
3 changed files with 315 additions and 1 deletions
@ -0,0 +1,193 @@ |
|||||||
|
--TEST-- |
||||||
|
Iterable and Iterator - Custom iteration with Traversable |
||||||
|
--SKIPIF-- |
||||||
|
<?php |
||||||
|
exit("skip: Generator syntax not supported in AOT"); |
||||||
|
?> |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
// Test implementing Iterator interface |
||||||
|
class NumberIterator implements Iterator { |
||||||
|
private int $start; |
||||||
|
private int $end; |
||||||
|
private int $current = 0; |
||||||
|
|
||||||
|
public function __construct(int $start, int $end) { |
||||||
|
$this->start = $start; |
||||||
|
$this->end = $end; |
||||||
|
$this->current = $start; |
||||||
|
} |
||||||
|
|
||||||
|
public function rewind(): void { |
||||||
|
$this->current = $this->start; |
||||||
|
} |
||||||
|
|
||||||
|
public function current(): int { |
||||||
|
return $this->current; |
||||||
|
} |
||||||
|
|
||||||
|
public function key(): int { |
||||||
|
return $this->current - $this->start; |
||||||
|
} |
||||||
|
|
||||||
|
public function next(): void { |
||||||
|
$this->current++; |
||||||
|
} |
||||||
|
|
||||||
|
public function valid(): bool { |
||||||
|
return $this->current <= $this->end; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Test implementing IteratorAggregate interface |
||||||
|
class NumberCollection implements IteratorAggregate { |
||||||
|
private array $numbers; |
||||||
|
|
||||||
|
public function __construct(array $numbers) { |
||||||
|
$this->numbers = $numbers; |
||||||
|
} |
||||||
|
|
||||||
|
public function getIterator(): Traversable { |
||||||
|
return new ArrayIterator($this->numbers); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Test a simple iterable range |
||||||
|
class Range implements IteratorAggregate { |
||||||
|
private int $start; |
||||||
|
private int $end; |
||||||
|
|
||||||
|
public function __construct(int $start, int $end) { |
||||||
|
$this->start = $start; |
||||||
|
$this->end = $end; |
||||||
|
} |
||||||
|
|
||||||
|
public function getIterator(): Traversable { |
||||||
|
for ($i = $this->start; $i <= $this->end; $i++) { |
||||||
|
yield $i; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Test filtering iterator |
||||||
|
class FilterIterator implements Iterator { |
||||||
|
private Iterator $iterator; |
||||||
|
private mixed $filterValue; |
||||||
|
|
||||||
|
public function __construct(Iterator $iterator, mixed $filterValue) { |
||||||
|
$this->iterator = $iterator; |
||||||
|
$this->filterValue = $filterValue; |
||||||
|
|
||||||
|
// Rewind to first valid element |
||||||
|
$this->iterator->rewind(); |
||||||
|
$this->advanceToValid(); |
||||||
|
} |
||||||
|
|
||||||
|
private function advanceToValid(): void { |
||||||
|
while ($this->iterator->valid() && $this->iterator->current() < $this->filterValue) { |
||||||
|
$this->iterator->next(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public function rewind(): void { |
||||||
|
$this->iterator->rewind(); |
||||||
|
$this->advanceToValid(); |
||||||
|
} |
||||||
|
|
||||||
|
public function current(): mixed { |
||||||
|
return $this->iterator->current(); |
||||||
|
} |
||||||
|
|
||||||
|
public function key(): mixed { |
||||||
|
return $this->iterator->key(); |
||||||
|
} |
||||||
|
|
||||||
|
public function next(): void { |
||||||
|
$this->iterator->next(); |
||||||
|
$this->advanceToValid(); |
||||||
|
} |
||||||
|
|
||||||
|
public function valid(): bool { |
||||||
|
return $this->iterator->valid(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function main() { |
||||||
|
// Test custom Iterator |
||||||
|
echo "Custom Iterator:\n"; |
||||||
|
$iterator = new NumberIterator(5, 10); |
||||||
|
foreach ($iterator as $key => $value) { |
||||||
|
echo "{$key}: {$value}\n"; |
||||||
|
} |
||||||
|
|
||||||
|
// Test IteratorAggregate with ArrayIterator |
||||||
|
echo "\nIteratorAggregate:\n"; |
||||||
|
$collection = new NumberCollection([10, 20, 30, 40, 50]); |
||||||
|
foreach ($collection as $index => $number) { |
||||||
|
echo "{$index}: {$number}\n"; |
||||||
|
} |
||||||
|
|
||||||
|
// Test Generator-based IteratorAggregate |
||||||
|
echo "\nGenerator-based:\n"; |
||||||
|
$range = new Range(1, 5); |
||||||
|
foreach ($range as $num) { |
||||||
|
echo "{$num}\n"; |
||||||
|
} |
||||||
|
|
||||||
|
// Test FilterIterator |
||||||
|
echo "\nFilterIterator:\n"; |
||||||
|
$baseIterator = new NumberIterator(1, 10); |
||||||
|
$filterIterator = new FilterIterator($baseIterator, 5); |
||||||
|
foreach ($filterIterator as $value) { |
||||||
|
echo "{$value}\n"; |
||||||
|
} |
||||||
|
|
||||||
|
// Test multiple iterations |
||||||
|
echo "\nMultiple iterations:\n"; |
||||||
|
$reusable = new Range(1, 3); |
||||||
|
foreach ($reusable as $num) { |
||||||
|
echo "First: {$num}\n"; |
||||||
|
} |
||||||
|
foreach ($reusable as $num) { |
||||||
|
echo "Second: {$num}\n"; |
||||||
|
} |
||||||
|
} |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
Custom Iterator: |
||||||
|
0: 5 |
||||||
|
1: 6 |
||||||
|
2: 7 |
||||||
|
3: 8 |
||||||
|
4: 9 |
||||||
|
5: 10 |
||||||
|
|
||||||
|
IteratorAggregate: |
||||||
|
0: 10 |
||||||
|
1: 20 |
||||||
|
2: 30 |
||||||
|
3: 40 |
||||||
|
4: 50 |
||||||
|
|
||||||
|
Generator-based: |
||||||
|
1 |
||||||
|
2 |
||||||
|
3 |
||||||
|
4 |
||||||
|
5 |
||||||
|
|
||||||
|
FilterIterator: |
||||||
|
5 |
||||||
|
6 |
||||||
|
7 |
||||||
|
8 |
||||||
|
9 |
||||||
|
10 |
||||||
|
|
||||||
|
Multiple iterations: |
||||||
|
First: 1 |
||||||
|
First: 2 |
||||||
|
First: 3 |
||||||
|
Second: 1 |
||||||
|
Second: 2 |
||||||
|
Second: 3 |
||||||
@ -0,0 +1,121 @@ |
|||||||
|
--TEST-- |
||||||
|
Match Expression - PHP 8+ enhanced switch statement |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
function test_basic_match($value) { |
||||||
|
return match($value) { |
||||||
|
1 => 'one', |
||||||
|
2 => 'two', |
||||||
|
3 => 'three', |
||||||
|
default => 'other', |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
function test_multiple_conditions($status) { |
||||||
|
return match($status) { |
||||||
|
200, 201, 202 => 'success', |
||||||
|
400, 401, 403, 404 => 'client_error', |
||||||
|
500, 501, 502, 503 => 'server_error', |
||||||
|
default => 'unknown', |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
function test_strict_comparison($value) { |
||||||
|
return match($value) { |
||||||
|
0 => 'integer zero', |
||||||
|
'0' => 'string zero', |
||||||
|
false => 'boolean false', |
||||||
|
null => 'null value', |
||||||
|
default => 'other', |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
function test_expression_result($a, $b, $op) { |
||||||
|
return match($op) { |
||||||
|
'add' => $a + $b, |
||||||
|
'sub' => $a - $b, |
||||||
|
'mul' => $a * $b, |
||||||
|
'div' => $b !== 0 ? $a / $b : 'error', |
||||||
|
default => 'invalid operation', |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
function test_nested_match($level) { |
||||||
|
return match($level) { |
||||||
|
1 => match(true) { |
||||||
|
true => 'level 1 - true', |
||||||
|
false => 'level 1 - false', |
||||||
|
}, |
||||||
|
2 => 'level 2', |
||||||
|
default => 'other level', |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
function test_no_default($value) { |
||||||
|
try { |
||||||
|
return match($value) { |
||||||
|
1 => 'one', |
||||||
|
2 => 'two', |
||||||
|
}; |
||||||
|
} catch (UnhandledMatchError $e) { |
||||||
|
return "Error: " . $e->getMessage(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function main() { |
||||||
|
// Test basic match |
||||||
|
var_dump(test_basic_match(1)); |
||||||
|
var_dump(test_basic_match(2)); |
||||||
|
var_dump(test_basic_match(5)); |
||||||
|
|
||||||
|
// Test multiple conditions |
||||||
|
var_dump(test_multiple_conditions(200)); |
||||||
|
var_dump(test_multiple_conditions(404)); |
||||||
|
var_dump(test_multiple_conditions(500)); |
||||||
|
var_dump(test_multiple_conditions(999)); |
||||||
|
|
||||||
|
// Test strict comparison |
||||||
|
var_dump(test_strict_comparison(0)); |
||||||
|
var_dump(test_strict_comparison('0')); |
||||||
|
var_dump(test_strict_comparison(false)); |
||||||
|
var_dump(test_strict_comparison(null)); |
||||||
|
|
||||||
|
// Test expression result |
||||||
|
var_dump(test_expression_result(10, 5, 'add')); |
||||||
|
var_dump(test_expression_result(10, 5, 'sub')); |
||||||
|
var_dump(test_expression_result(10, 5, 'mul')); |
||||||
|
var_dump(test_expression_result(10, 5, 'div')); |
||||||
|
var_dump(test_expression_result(10, 0, 'div')); |
||||||
|
|
||||||
|
// Test nested match |
||||||
|
var_dump(test_nested_match(1)); |
||||||
|
var_dump(test_nested_match(2)); |
||||||
|
var_dump(test_nested_match(3)); |
||||||
|
|
||||||
|
// Test no default (should throw error) |
||||||
|
var_dump(test_no_default(1)); |
||||||
|
var_dump(test_no_default(5)); |
||||||
|
} |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
string(3) "one" |
||||||
|
string(3) "two" |
||||||
|
string(5) "other" |
||||||
|
string(7) "success" |
||||||
|
string(12) "client_error" |
||||||
|
string(12) "server_error" |
||||||
|
string(7) "unknown" |
||||||
|
string(12) "integer zero" |
||||||
|
string(11) "string zero" |
||||||
|
string(13) "boolean false" |
||||||
|
string(10) "null value" |
||||||
|
int(15) |
||||||
|
int(5) |
||||||
|
int(50) |
||||||
|
int(2) |
||||||
|
string(5) "error" |
||||||
|
string(14) "level 1 - true" |
||||||
|
string(7) "level 2" |
||||||
|
string(11) "other level" |
||||||
|
string(3) "one" |
||||||
|
string(27) "Error: Unhandled match case" |
||||||
Loading…
Reference in new issue