- 实现了函数调用、方法调用、静态调用和属性获取后的通用方法调用 - 新增 stream_cast 伪函数用于流类型转换测试 - 支持数组方法链式调用和可变操作 - 添加表达式上下文中的流转换功能 - 扩展了对内部函数反射返回类型的处理 - 优化了数组操作的返回值一致性 - 完善了通用方法调用的错误处理机制pull/1/head
parent
6309346c9a
commit
12d44084ec
6 changed files with 187 additions and 4 deletions
@ -0,0 +1,7 @@ |
||||
<?php |
||||
function main() |
||||
{ |
||||
$arr = []; |
||||
$arr->push(1); |
||||
($arr->slice(0, 1))->push(2); |
||||
} |
||||
@ -0,0 +1,7 @@ |
||||
<?php |
||||
function main() |
||||
{ |
||||
$tmpfile = tempnam(sys_get_temp_dir(), 'aot'); |
||||
$fp = fopen($tmpfile, 'w'); |
||||
stream_cast()->write('data'); |
||||
} |
||||
@ -0,0 +1,6 @@ |
||||
<?php |
||||
function main() |
||||
{ |
||||
$tmpfile = tempnam(sys_get_temp_dir(), 'aot'); |
||||
stream_cast(fopen($tmpfile, 'w'))->unknownMethod(); |
||||
} |
||||
@ -0,0 +1,49 @@ |
||||
--TEST-- |
||||
stream_cast() pseudo-function for stream type casting |
||||
--FILE-- |
||||
<?php |
||||
|
||||
function main() |
||||
{ |
||||
require __DIR__ . '/../../../src/Assert.php'; |
||||
|
||||
// Test 1: stream_cast identity — on an already-inferred stream variable |
||||
$tmpfile = tempnam(sys_get_temp_dir(), 'aot'); |
||||
$fp = fopen($tmpfile, 'w+'); |
||||
$fp->write("hello world"); |
||||
$fp->seek(0); |
||||
$data = stream_cast($fp)->read(5); |
||||
var_dump($data); |
||||
$fp->close(); |
||||
|
||||
// Test 2: stream_cast on array elements where type isn't known |
||||
$pipes = []; |
||||
$pipes[0] = fopen($tmpfile, 'w'); |
||||
$w = stream_cast($pipes[0]); |
||||
$w->write("test data from pipe"); |
||||
$w->close(); |
||||
|
||||
$pipes[1] = fopen($tmpfile, 'r'); |
||||
$r = stream_cast($pipes[1]); |
||||
$data2 = $r->read(1024); |
||||
var_dump($data2); |
||||
$r->close(); |
||||
|
||||
// Test 3: stream_cast in expression context (method chaining) |
||||
$fp2 = fopen($tmpfile, 'w'); |
||||
stream_cast($fp2)->write("chain test"); |
||||
stream_cast($fp2)->close(); |
||||
|
||||
$fp3 = fopen($tmpfile, 'r'); |
||||
var_dump(stream_cast($fp3)->read(10)); |
||||
stream_cast($fp3)->close(); |
||||
|
||||
unlink($tmpfile); |
||||
echo "OK\n"; |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
string(5) "hello" |
||||
string(19) "test data from pipe" |
||||
string(10) "chain test" |
||||
OK |
||||
@ -0,0 +1,114 @@ |
||||
--TEST-- |
||||
Universal method call on expression results (FuncCall, MethodCall, StaticCall, PropertyFetch) |
||||
--FILE-- |
||||
<?php |
||||
|
||||
// --- Helpers: functions and classes --- |
||||
|
||||
function getString(): string |
||||
{ |
||||
return "hello world"; |
||||
} |
||||
|
||||
function getInt(): int |
||||
{ |
||||
return 42; |
||||
} |
||||
|
||||
function getFloat(): float |
||||
{ |
||||
return 3.14; |
||||
} |
||||
|
||||
function getArray(): array |
||||
{ |
||||
return [1, 2, 3, 4, 5]; |
||||
} |
||||
|
||||
class Helper { |
||||
public string $name = "hello"; |
||||
|
||||
public function getName(): string |
||||
{ |
||||
return "test"; |
||||
} |
||||
|
||||
public function getInt(): int |
||||
{ |
||||
return 100; |
||||
} |
||||
|
||||
public static function staticName(): string |
||||
{ |
||||
return "STATIC"; |
||||
} |
||||
|
||||
public static function staticArray(): array |
||||
{ |
||||
return ["a", "b", "c"]; |
||||
} |
||||
} |
||||
|
||||
function main() |
||||
{ |
||||
require __DIR__ . '/../../src/Assert.php'; |
||||
|
||||
// --- Test 1: FuncCall -> universal method --- |
||||
Assert::eq(getString()->length(), 11); |
||||
Assert::eq(getString()->upper(), "HELLO WORLD"); |
||||
Assert::eq(getString()->contains("world"), true); |
||||
Assert::eq(getInt()->toFloat(), 42.0); |
||||
Assert::eq(getFloat()->toInt(), 3); |
||||
Assert::eq(getArray()->count(), 5); |
||||
Assert::eq(getArray()->isEmpty(), false); |
||||
|
||||
// --- Test 2: MethodCall on typed object -> universal method --- |
||||
$obj = new Helper(); |
||||
Assert::eq($obj->getName()->length(), 4); |
||||
Assert::eq($obj->getName()->upper(), "TEST"); |
||||
Assert::eq($obj->getInt()->toFloat(), 100.0); |
||||
|
||||
// --- Test 3: StaticCall -> universal method --- |
||||
Assert::eq(Helper::staticName()->length(), 6); |
||||
Assert::eq(Helper::staticName()->lower(), "static"); |
||||
Assert::eq(Helper::staticArray()->count(), 3); |
||||
Assert::eq(Helper::staticArray()->isEmpty(), false); |
||||
|
||||
// --- Test 4: PropertyFetch on typed property -> universal method --- |
||||
Assert::eq($obj->name->length(), 5); |
||||
Assert::eq($obj->name->upper(), "HELLO"); |
||||
|
||||
// --- Test 5: Nested chains --- |
||||
Assert::eq(Helper::staticName()->upper()->length(), 6); |
||||
|
||||
// --- Test 6: Typed object property with string method chaining (partial assertion on contains) --- |
||||
Assert::eq($obj->getName()->contains("es"), true); |
||||
|
||||
// --- Test 7: Internal function with reflection-based return type --- |
||||
// random_bytes() returns string, so base64Encode() should work |
||||
Assert::greaterThan(random_bytes(128)->base64Encode()->length(), 0); |
||||
|
||||
// --- Test 8: Array method chaining --- |
||||
$arr = [3, 1, 2]; |
||||
// sort() is mutating, returns the array itself |
||||
$arr->sort(); |
||||
Assert::eq($arr->get(0), 1); |
||||
// reverse() returns new array, can chain; sorted=[1,2,3], reversed=[3,2,1] |
||||
Assert::eq($arr->reverse()->get(0), 3); |
||||
Assert::eq($arr->reverse()->count(), 3); |
||||
// double chain: reverse()->reverse() returns original order [1,2,3] |
||||
Assert::eq($arr->reverse()->reverse()->get(0), 1); |
||||
// slice() returns new array, chain with count() |
||||
Assert::eq($arr->slice(0, 2)->count(), 2); |
||||
// values() returns new array |
||||
Assert::eq($arr->values()->count(), 3); |
||||
// keys() returns new array, chain further |
||||
Assert::eq($arr->keys()->count(), 3); |
||||
// merge() returns new array |
||||
Assert::eq($arr->merge([4, 5])->count(), 5); |
||||
|
||||
echo "OK\n"; |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
OK |
||||
Loading…
Reference in new issue