1--TEST-- 2Disjunctive Normal Form types in reflection 3--FILE-- 4<?php 5 6function dumpType(ReflectionType $rt, int $indent = 0) { 7 $str_indent = str_repeat(' ', 2 * $indent); 8 echo $str_indent . "Type $rt is " . $rt::class . ":\n"; 9 echo $str_indent . "Allows Null: " . json_encode($rt->allowsNull()) . "\n"; 10 foreach ($rt->getTypes() as $type) { 11 if ($type instanceof ReflectionNamedType) { 12 echo $str_indent . " Name: " . $type->getName() . "\n"; 13 echo $str_indent . " String: " . (string) $type . "\n"; 14 } else { 15 dumpType($type, $indent+1); 16 } 17 } 18} 19 20function test1(): (X&Y)|(Z&Traversable)|null { } 21 22class Test { 23 public (X&Y)|null $prop; 24} 25 26dumpType((new ReflectionFunction('test1'))->getReturnType()); 27 28$rc = new ReflectionClass(Test::class); 29$rp = $rc->getProperty('prop'); 30dumpType($rp->getType()); 31 32/* Force CE resolution of the property type */ 33 34interface y {} 35class x implements Y, Countable { 36 public function count(): int { return 0; } 37} 38$test = new Test; 39$test->prop = new x; 40 41$rp = $rc->getProperty('prop'); 42dumpType($rp->getType()); 43 44?> 45--EXPECT-- 46Type (X&Y)|(Z&Traversable)|null is ReflectionUnionType: 47Allows Null: true 48 Type X&Y is ReflectionIntersectionType: 49 Allows Null: false 50 Name: X 51 String: X 52 Name: Y 53 String: Y 54 Type Z&Traversable is ReflectionIntersectionType: 55 Allows Null: false 56 Name: Z 57 String: Z 58 Name: Traversable 59 String: Traversable 60 Name: null 61 String: null 62Type (X&Y)|null is ReflectionUnionType: 63Allows Null: true 64 Type X&Y is ReflectionIntersectionType: 65 Allows Null: false 66 Name: X 67 String: X 68 Name: Y 69 String: Y 70 Name: null 71 String: null 72Type (X&Y)|null is ReflectionUnionType: 73Allows Null: true 74 Type X&Y is ReflectionIntersectionType: 75 Allows Null: false 76 Name: X 77 String: X 78 Name: Y 79 String: Y 80 Name: null 81 String: null 82