- 移除测试文件中不必要的 use native_types 语句 - 新增 call-interface-method.phpt 测试接口方法调用功能 - 新增 call-magic-fallback-typed-object.phpt 测试魔法方法回调功能 - 新增 call-multilevel-override.phpt 测试多层级继承方法重写功能 - 新增 call-parent-sibling-dispatch.phpt 测试父类参数分发到兄弟类方法功能 - 新增 call-protected-override.phpt 测试受保护方法重写功能 - 添加命名空间重写键名使用完整类名的单元测试 - 添加 static 属性 array_pop 操作的调试测试用例pull/3/head
parent
a7b5b248fb
commit
23f1a1eef9
10 changed files with 271 additions and 6 deletions
@ -0,0 +1,60 @@ |
|||||||
|
<?php |
||||||
|
class StackHolder |
||||||
|
{ |
||||||
|
public static array $stack = []; |
||||||
|
|
||||||
|
public static function push(string $item): void |
||||||
|
{ |
||||||
|
self::$stack[] = $item; |
||||||
|
} |
||||||
|
|
||||||
|
public static function pop(): void |
||||||
|
{ |
||||||
|
// 触发 bug: AOT 对 static 属性 array_pop 生成 php::toReference(v) 缺第 2 参数 |
||||||
|
array_pop(self::$stack); |
||||||
|
} |
||||||
|
|
||||||
|
public static function count(): int |
||||||
|
{ |
||||||
|
return count(self::$stack); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// ── 对照:array_pop($localVar) 走不同代码生成路径,应该正常 ── |
||||||
|
function local_pop_test(): string |
||||||
|
{ |
||||||
|
$arr = ['x', 'y', 'z']; |
||||||
|
array_pop($arr); |
||||||
|
|
||||||
|
if (count($arr) !== 2) { |
||||||
|
return 'FAIL: local_pop_test expected 2, got ' . count($arr); |
||||||
|
} |
||||||
|
if ($arr[0] !== 'x' || $arr[1] !== 'y') { |
||||||
|
return 'FAIL: local_pop_test wrong elements'; |
||||||
|
} |
||||||
|
|
||||||
|
return 'OK'; |
||||||
|
} |
||||||
|
|
||||||
|
function main(): int |
||||||
|
{ |
||||||
|
// 测试 static 属性(已知 bug,预期编译失败) |
||||||
|
StackHolder::push('a'); |
||||||
|
StackHolder::push('b'); |
||||||
|
StackHolder::pop(); |
||||||
|
|
||||||
|
if (StackHolder::count() !== 1) { |
||||||
|
echo "FAIL: static_pop expected 1, got " . StackHolder::count() . "\n"; |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
// 测试局部变量(应该正常) |
||||||
|
$result = local_pop_test(); |
||||||
|
echo $result . "\n"; |
||||||
|
if ($result !== 'OK') { |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
echo "ALL OK\n"; |
||||||
|
return 0; |
||||||
|
} |
||||||
@ -0,0 +1,32 @@ |
|||||||
|
--TEST-- |
||||||
|
call interface method through concrete object |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
interface Runner |
||||||
|
{ |
||||||
|
public function run(): string; |
||||||
|
} |
||||||
|
|
||||||
|
class FastRunner implements Runner |
||||||
|
{ |
||||||
|
public function run(): string |
||||||
|
{ |
||||||
|
return 'fast'; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function invoke(Runner $runner): string |
||||||
|
{ |
||||||
|
$alias = $runner; |
||||||
|
return $alias->run(); |
||||||
|
} |
||||||
|
|
||||||
|
function main(): int |
||||||
|
{ |
||||||
|
$r = invoke(new FastRunner()); |
||||||
|
echo "result: $r\n"; |
||||||
|
return $r === 'fast' ? 0 : 1; |
||||||
|
} |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
result: fast |
||||||
@ -0,0 +1,29 @@ |
|||||||
|
--TEST-- |
||||||
|
missing method on typed object dispatches to __call |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
use native_types; |
||||||
|
|
||||||
|
class DynamicHandler |
||||||
|
{ |
||||||
|
public function __call(string $name, array $args): string |
||||||
|
{ |
||||||
|
return $name . ':' . implode(',', $args); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function invoke(DynamicHandler $handler): string |
||||||
|
{ |
||||||
|
$alias = $handler; |
||||||
|
return $alias->missing('a', 'b'); |
||||||
|
} |
||||||
|
|
||||||
|
function main(): int |
||||||
|
{ |
||||||
|
$r = invoke(new DynamicHandler()); |
||||||
|
echo "result: $r\n"; |
||||||
|
return $r === 'missing:a,b' ? 0 : 1; |
||||||
|
} |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
result: missing:a,b |
||||||
@ -0,0 +1,41 @@ |
|||||||
|
--TEST-- |
||||||
|
call method overridden in grandchild through base parameter |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
use native_types; |
||||||
|
|
||||||
|
class Base |
||||||
|
{ |
||||||
|
public function value(): string |
||||||
|
{ |
||||||
|
return 'base'; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class Middle extends Base |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
class Leaf extends Middle |
||||||
|
{ |
||||||
|
public function value(): string |
||||||
|
{ |
||||||
|
return 'leaf'; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function readValue(Base $obj): string |
||||||
|
{ |
||||||
|
$alias = $obj; |
||||||
|
return $alias->value(); |
||||||
|
} |
||||||
|
|
||||||
|
function main(): int |
||||||
|
{ |
||||||
|
$r = readValue(new Leaf()); |
||||||
|
echo "result: $r\n"; |
||||||
|
return $r === 'leaf' ? 0 : 1; |
||||||
|
} |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
result: leaf |
||||||
@ -0,0 +1,48 @@ |
|||||||
|
--TEST-- |
||||||
|
call overridden method on sibling subclasses through parent parameter |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
use native_types; |
||||||
|
|
||||||
|
class Base |
||||||
|
{ |
||||||
|
public function name(): string |
||||||
|
{ |
||||||
|
return 'base'; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class Left extends Base |
||||||
|
{ |
||||||
|
public function name(): string |
||||||
|
{ |
||||||
|
return 'left'; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class Right extends Base |
||||||
|
{ |
||||||
|
public function name(): string |
||||||
|
{ |
||||||
|
return 'right'; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function readName(Base $obj): string |
||||||
|
{ |
||||||
|
$alias = $obj; |
||||||
|
return $alias->name(); |
||||||
|
} |
||||||
|
|
||||||
|
function main(): int |
||||||
|
{ |
||||||
|
$left = readName(new Left()); |
||||||
|
$right = readName(new Right()); |
||||||
|
echo "left: $left\n"; |
||||||
|
echo "right: $right\n"; |
||||||
|
return $left === 'left' && $right === 'right' ? 0 : 1; |
||||||
|
} |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
left: left |
||||||
|
right: right |
||||||
@ -0,0 +1,41 @@ |
|||||||
|
--TEST-- |
||||||
|
protected overridden method remains virtual through parent wrapper |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
use native_types; |
||||||
|
|
||||||
|
class Base |
||||||
|
{ |
||||||
|
protected function token(): string |
||||||
|
{ |
||||||
|
return 'base'; |
||||||
|
} |
||||||
|
|
||||||
|
public function expose(): string |
||||||
|
{ |
||||||
|
return $this->token(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class Child extends Base |
||||||
|
{ |
||||||
|
protected function token(): string |
||||||
|
{ |
||||||
|
return 'child'; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function main(): int |
||||||
|
{ |
||||||
|
$base = new Base(); |
||||||
|
$child = new Child(); |
||||||
|
$a = $base->expose(); |
||||||
|
$b = $child->expose(); |
||||||
|
echo "base: $a\n"; |
||||||
|
echo "child: $b\n"; |
||||||
|
return $a === 'base' && $b === 'child' ? 0 : 1; |
||||||
|
} |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
base: base |
||||||
|
child: child |
||||||
Loading…
Reference in new issue