1--TEST--
2Union types in reflection
3--FILE--
4<?php
5
6function dumpType(ReflectionUnionType $rt) {
7    echo "Type $rt:\n";
8    echo "Allows null: " . ($rt->allowsNull() ? "true" : "false") . "\n";
9    foreach ($rt->getTypes() as $type) {
10        echo "  Name: " . $type->getName() . "\n";
11        echo "  String: " . (string) $type . "\n";
12        echo "  Allows Null: " . ($type->allowsNull() ? "true" : "false") . "\n";
13    }
14}
15
16function dumpBCType(ReflectionNamedType $rt) {
17    echo "Type $rt:\n";
18    echo "  Name: " . $rt->getName() . "\n";
19    echo "  String: " . (string) $rt . "\n";
20    echo "  Allows Null: " . ($rt->allowsNull() ? "true" : "false") . "\n";
21}
22
23function test1(): X|Y|int|float|false|null { }
24function test2(): X|iterable|bool { }
25function test3(): null|false { }
26function test4(): ?false { }
27function test5(): X|iterable|true { }
28function test6(): null|true { }
29function test7(): ?true { }
30
31class Test {
32    public X|Y|int $prop;
33}
34
35dumpType((new ReflectionFunction('test1'))->getReturnType());
36dumpType((new ReflectionFunction('test2'))->getReturnType());
37dumpBCType((new ReflectionFunction('test3'))->getReturnType());
38dumpBCType((new ReflectionFunction('test4'))->getReturnType());
39dumpType((new ReflectionFunction('test5'))->getReturnType());
40dumpBCType((new ReflectionFunction('test6'))->getReturnType());
41dumpBCType((new ReflectionFunction('test7'))->getReturnType());
42
43$rc = new ReflectionClass(Test::class);
44$rp = $rc->getProperty('prop');
45dumpType($rp->getType());
46
47/* Force CE resolution of the property type */
48
49class x {}
50$test = new Test;
51$test->prop = new x;
52
53$rp = $rc->getProperty('prop');
54dumpType($rp->getType());
55
56class y {}
57$test->prop = new y;
58
59$rp = $rc->getProperty('prop');
60dumpType($rp->getType());
61
62?>
63--EXPECT--
64Type X|Y|int|float|false|null:
65Allows null: true
66  Name: X
67  String: X
68  Allows Null: false
69  Name: Y
70  String: Y
71  Allows Null: false
72  Name: int
73  String: int
74  Allows Null: false
75  Name: float
76  String: float
77  Allows Null: false
78  Name: false
79  String: false
80  Allows Null: false
81  Name: null
82  String: null
83  Allows Null: true
84Type X|Traversable|array|bool:
85Allows null: false
86  Name: X
87  String: X
88  Allows Null: false
89  Name: Traversable
90  String: Traversable
91  Allows Null: false
92  Name: array
93  String: array
94  Allows Null: false
95  Name: bool
96  String: bool
97  Allows Null: false
98Type ?false:
99  Name: false
100  String: ?false
101  Allows Null: true
102Type ?false:
103  Name: false
104  String: ?false
105  Allows Null: true
106Type X|Traversable|array|true:
107Allows null: false
108  Name: X
109  String: X
110  Allows Null: false
111  Name: Traversable
112  String: Traversable
113  Allows Null: false
114  Name: array
115  String: array
116  Allows Null: false
117  Name: true
118  String: true
119  Allows Null: false
120Type ?true:
121  Name: true
122  String: ?true
123  Allows Null: true
124Type ?true:
125  Name: true
126  String: ?true
127  Allows Null: true
128Type X|Y|int:
129Allows null: false
130  Name: X
131  String: X
132  Allows Null: false
133  Name: Y
134  String: Y
135  Allows Null: false
136  Name: int
137  String: int
138  Allows Null: false
139Type X|Y|int:
140Allows null: false
141  Name: X
142  String: X
143  Allows Null: false
144  Name: Y
145  String: Y
146  Allows Null: false
147  Name: int
148  String: int
149  Allows Null: false
150Type X|Y|int:
151Allows null: false
152  Name: X
153  String: X
154  Allows Null: false
155  Name: Y
156  String: Y
157  Allows Null: false
158  Name: int
159  String: int
160  Allows Null: false
161