Skip to content

Commit f127dad

Browse files
committed
test(compiler): add comprehensive tests for late static binding and typed array conversions
- Added test for toStdList and toStdDict methods validating PHP array conversion with type preservation - Included tests for late static calls from instance methods retaining receiver on inherited and overridden methods - Added tests verifying late static calls distinguish parent and child private methods in instance context - Covered various scenarios including scalar conversions, object arrays, and error handling cases - Verified proper behavior with numeric keys, class inheritance, and dynamic method calls - Tested type validation for list and dictionary conversions with multiple data types
1 parent a3b5d61 commit f127dad

3 files changed

Lines changed: 296 additions & 0 deletions

File tree

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
--TEST--
2+
Late static calls from instance methods retain the receiver on inherited and overridden methods
3+
--FILE--
4+
<?php
5+
class InstanceCallBase
6+
{
7+
public string $value = 'base-value';
8+
9+
protected function helper(string $suffix = ''): string
10+
{
11+
return $this->value . $suffix;
12+
}
13+
14+
protected static function label(): string
15+
{
16+
return static::class;
17+
}
18+
19+
public function fixed(): string
20+
{
21+
return static::helper();
22+
}
23+
24+
public function withArgs(): string
25+
{
26+
return static::helper('-arg');
27+
}
28+
29+
public function dynamic(): string
30+
{
31+
$name = 'helper';
32+
return static::{$name}('-dynamic');
33+
}
34+
35+
public function staticFixed(): string
36+
{
37+
return static::label();
38+
}
39+
40+
public function staticDynamic(): string
41+
{
42+
$name = 'label';
43+
return static::{$name}();
44+
}
45+
}
46+
47+
class InstanceCallInherited extends InstanceCallBase
48+
{
49+
}
50+
51+
class InstanceCallOverride extends InstanceCallBase
52+
{
53+
protected function helper(string $suffix = ''): string
54+
{
55+
return 'override-' . $this->value . $suffix;
56+
}
57+
58+
protected static function label(): string
59+
{
60+
return 'override:' . static::class;
61+
}
62+
}
63+
64+
function main(): void
65+
{
66+
foreach ([new InstanceCallBase(), new InstanceCallInherited(), new InstanceCallOverride()] as $item) {
67+
echo $item->fixed(), "\n";
68+
echo $item->withArgs(), "\n";
69+
echo $item->dynamic(), "\n";
70+
echo $item->staticFixed(), "\n";
71+
echo $item->staticDynamic(), "\n";
72+
}
73+
}
74+
?>
75+
--EXPECT--
76+
base-value
77+
base-value-arg
78+
base-value-dynamic
79+
InstanceCallBase
80+
InstanceCallBase
81+
base-value
82+
base-value-arg
83+
base-value-dynamic
84+
InstanceCallInherited
85+
InstanceCallInherited
86+
override-base-value
87+
override-base-value-arg
88+
override-base-value-dynamic
89+
override:InstanceCallOverride
90+
override:InstanceCallOverride
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
--TEST--
2+
Late static calls distinguish parent and child private methods in an instance context
3+
--FILE--
4+
<?php
5+
class LatePrivateBase
6+
{
7+
private function value(): string
8+
{
9+
return 'base';
10+
}
11+
12+
public function viaThis(): string
13+
{
14+
return $this->value();
15+
}
16+
17+
public function viaStatic(): string
18+
{
19+
return static::value();
20+
}
21+
22+
public function viaDynamic(): string
23+
{
24+
$name = 'value';
25+
return static::{$name}();
26+
}
27+
}
28+
29+
class LatePrivateInherited extends LatePrivateBase
30+
{
31+
}
32+
33+
class LatePrivateRedeclared extends LatePrivateBase
34+
{
35+
private function value(): string
36+
{
37+
return 'child';
38+
}
39+
}
40+
41+
function main(): void
42+
{
43+
$inherited = new LatePrivateInherited();
44+
echo $inherited->viaThis(), "\n";
45+
echo $inherited->viaStatic(), "\n";
46+
echo $inherited->viaDynamic(), "\n";
47+
48+
$redeclared = new LatePrivateRedeclared();
49+
echo $redeclared->viaThis(), "\n";
50+
try {
51+
echo $redeclared->viaStatic(), "\n";
52+
} catch (Error $error) {
53+
echo get_class($error), "\n";
54+
}
55+
try {
56+
echo $redeclared->viaDynamic(), "\n";
57+
} catch (Error $error) {
58+
echo get_class($error), "\n";
59+
}
60+
}
61+
?>
62+
--EXPECT--
63+
base
64+
base
65+
base
66+
base
67+
Error
68+
Error
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
--TEST--
2+
toStdList and toStdDict validate converted PHP arrays and preserve typed assignment
3+
--FILE--
4+
<?php
5+
class ConvertedValue {}
6+
class ConvertedChild extends ConvertedValue {}
7+
8+
class ConvertedSource {
9+
public function toArray(): array { return ['answer' => 42]; }
10+
}
11+
12+
#[Native]
13+
class ConvertedNativeSource {
14+
public function toArray(): array { return [12]; }
15+
}
16+
17+
function make_source(): array {
18+
echo "source called\n";
19+
return [13];
20+
}
21+
22+
function receive_list(#[StdList(Type::Int)] array $values): int {
23+
return $values[0];
24+
}
25+
26+
function main(): void {
27+
$rawList = [4, 5];
28+
$list = $rawList->toStdList(Type::Int);
29+
$copy = $list->toStdList(Type::Int);
30+
$copy[] = 6;
31+
var_dump($list, $copy);
32+
33+
$rawDict = ['answer' => 42];
34+
$dict = $rawDict->toStdDict(Type::Str, Type::Int);
35+
var_dump($dict['answer']);
36+
37+
$number = 9;
38+
$fromScalar = $number->toStdList(Type::Int);
39+
var_dump($fromScalar);
40+
41+
$object = new ConvertedSource();
42+
$fromObject = $object->toStdDict(Type::Str, Type::Int);
43+
var_dump($fromObject);
44+
45+
$dynamic = std::any([7, 8]);
46+
$fromDynamic = $dynamic->toStdList(Type::Int);
47+
var_dump($fromDynamic[1], receive_list($rawList->toStdList(Type::Int)));
48+
49+
$integerKeys = [10 => 'ten'];
50+
$integerDict = $integerKeys->toStdDict(Type::Int, Type::Str);
51+
var_dump($integerDict[10]);
52+
53+
$vector = std::vector(Type::Int);
54+
$vector[] = 11;
55+
$fromVector = $vector->toStdList(Type::Int);
56+
var_dump($fromVector[0]);
57+
58+
$native = new ConvertedNativeSource();
59+
$fromNative = $native->toStdList(Type::Int);
60+
var_dump($fromNative[0]);
61+
$fromCall = make_source()->toStdList(Type::Int);
62+
var_dump($fromCall[0]);
63+
64+
$rawObjects = [new ConvertedValue(), new ConvertedChild()];
65+
$objects = $rawObjects->toStdList(ConvertedValue::class);
66+
var_dump(count($objects), $objects[1] instanceof ConvertedChild);
67+
68+
try {
69+
$badListKeys = ['name' => 1];
70+
$badListKeys->toStdList(Type::Int);
71+
} catch (TypeError $error) { echo "list key rejected\n"; }
72+
try {
73+
$badListValues = [1, 'two'];
74+
$badListValues->toStdList(Type::Int);
75+
} catch (TypeError $error) { echo "list value rejected\n"; }
76+
try {
77+
$badDictKeys = [1 => 1];
78+
$badDictKeys->toStdDict(Type::Str, Type::Int);
79+
} catch (TypeError $error) { echo "dict key rejected\n"; }
80+
try {
81+
$normalizedNumericKey = ['123' => 1];
82+
$normalizedNumericKey->toStdDict(Type::Str, Type::Int);
83+
} catch (TypeError $error) { echo "normalized key rejected\n"; }
84+
try {
85+
$badDictValues = ['one' => 'wrong'];
86+
$badDictValues->toStdDict(Type::Str, Type::Int);
87+
} catch (TypeError $error) { echo "dict value rejected\n"; }
88+
try {
89+
$badObjects = [new stdClass()];
90+
$badObjects->toStdList(ConvertedValue::class);
91+
} catch (TypeError $error) { echo "class rejected\n"; }
92+
try {
93+
$otherType = std::list(Type::Str);
94+
$otherType[] = 'not an integer';
95+
$otherType->toStdList(Type::Int);
96+
} catch (TypeError $error) { echo "different contract rejected\n"; }
97+
}
98+
?>
99+
--EXPECT--
100+
array(2) {
101+
[0]=>
102+
int(4)
103+
[1]=>
104+
int(5)
105+
}
106+
array(3) {
107+
[0]=>
108+
int(4)
109+
[1]=>
110+
int(5)
111+
[2]=>
112+
int(6)
113+
}
114+
int(42)
115+
array(1) {
116+
[0]=>
117+
int(9)
118+
}
119+
array(1) {
120+
["answer"]=>
121+
int(42)
122+
}
123+
int(8)
124+
int(4)
125+
string(3) "ten"
126+
int(11)
127+
int(12)
128+
source called
129+
int(13)
130+
int(2)
131+
bool(true)
132+
list key rejected
133+
list value rejected
134+
dict key rejected
135+
normalized key rejected
136+
dict value rejected
137+
class rejected
138+
different contract rejected

0 commit comments

Comments
 (0)