1--TEST--
2Behavior of union type checks (strict)
3--FILE--
4<?php
5
6declare(strict_types=1);
7
8function dump($value) {
9    if (is_object($value)) {
10        return 'new ' . get_class($value);
11    }
12    if ($value === INF) {
13        return 'INF';
14    }
15    return json_encode($value, JSON_PRESERVE_ZERO_FRACTION);
16}
17
18function test(string $type, array $values) {
19    $alignment = 16;
20
21    echo "\nType $type:\n";
22    $fn = eval("return function($type \$arg) { return \$arg; };");
23    foreach ($values as $value) {
24        echo str_pad(dump($value), $alignment), ' => ';
25
26        try {
27            error_clear_last();
28            $value = @$fn($value);
29            echo dump($value);
30            if ($e = error_get_last()) {
31                echo ' (', $e['message'], ')';
32            }
33        } catch (TypeError $e) {
34            $msg = $e->getMessage();
35            $msg = strstr($msg, ', called in', true);
36            $msg = str_replace('{closure}(): Argument #1 ($arg)', 'Argument ...', $msg);
37            echo $msg;
38        }
39        echo "\n";
40    }
41}
42
43class WithToString {
44    public function __toString() {
45        return "__toString()";
46    }
47}
48
49$values = [
50    42, 42.0, INF, "42", "42.0", "42x", "x", "",
51    true, false, null, [], new stdClass, new WithToString,
52];
53test('int|float', $values);
54test('int|float|false', $values);
55test('int|float|bool', $values);
56test('int|bool', $values);
57test('int|string|null', $values);
58test('string|bool', $values);
59test('float|array', $values);
60test('string|array', $values);
61test('bool|array', $values);
62
63?>
64--EXPECTF--
65Type int|float:
6642               => 42
6742.0             => 42.0
68INF              => INF
69"42"             => {closure:%s:%d}(): Argument #1 ($arg) must be of type int|float, string given
70"42.0"           => {closure:%s:%d}(): Argument #1 ($arg) must be of type int|float, string given
71"42x"            => {closure:%s:%d}(): Argument #1 ($arg) must be of type int|float, string given
72"x"              => {closure:%s:%d}(): Argument #1 ($arg) must be of type int|float, string given
73""               => {closure:%s:%d}(): Argument #1 ($arg) must be of type int|float, string given
74true             => {closure:%s:%d}(): Argument #1 ($arg) must be of type int|float, true given
75false            => {closure:%s:%d}(): Argument #1 ($arg) must be of type int|float, false given
76null             => {closure:%s:%d}(): Argument #1 ($arg) must be of type int|float, null given
77[]               => {closure:%s:%d}(): Argument #1 ($arg) must be of type int|float, array given
78new stdClass     => {closure:%s:%d}(): Argument #1 ($arg) must be of type int|float, stdClass given
79new WithToString => {closure:%s:%d}(): Argument #1 ($arg) must be of type int|float, WithToString given
80
81Type int|float|false:
8242               => 42
8342.0             => 42.0
84INF              => INF
85"42"             => {closure:%s:%d}(): Argument #1 ($arg) must be of type int|float|false, string given
86"42.0"           => {closure:%s:%d}(): Argument #1 ($arg) must be of type int|float|false, string given
87"42x"            => {closure:%s:%d}(): Argument #1 ($arg) must be of type int|float|false, string given
88"x"              => {closure:%s:%d}(): Argument #1 ($arg) must be of type int|float|false, string given
89""               => {closure:%s:%d}(): Argument #1 ($arg) must be of type int|float|false, string given
90true             => {closure:%s:%d}(): Argument #1 ($arg) must be of type int|float|false, true given
91false            => false
92null             => {closure:%s:%d}(): Argument #1 ($arg) must be of type int|float|false, null given
93[]               => {closure:%s:%d}(): Argument #1 ($arg) must be of type int|float|false, array given
94new stdClass     => {closure:%s:%d}(): Argument #1 ($arg) must be of type int|float|false, stdClass given
95new WithToString => {closure:%s:%d}(): Argument #1 ($arg) must be of type int|float|false, WithToString given
96
97Type int|float|bool:
9842               => 42
9942.0             => 42.0
100INF              => INF
101"42"             => {closure:%s:%d}(): Argument #1 ($arg) must be of type int|float|bool, string given
102"42.0"           => {closure:%s:%d}(): Argument #1 ($arg) must be of type int|float|bool, string given
103"42x"            => {closure:%s:%d}(): Argument #1 ($arg) must be of type int|float|bool, string given
104"x"              => {closure:%s:%d}(): Argument #1 ($arg) must be of type int|float|bool, string given
105""               => {closure:%s:%d}(): Argument #1 ($arg) must be of type int|float|bool, string given
106true             => true
107false            => false
108null             => {closure:%s:%d}(): Argument #1 ($arg) must be of type int|float|bool, null given
109[]               => {closure:%s:%d}(): Argument #1 ($arg) must be of type int|float|bool, array given
110new stdClass     => {closure:%s:%d}(): Argument #1 ($arg) must be of type int|float|bool, stdClass given
111new WithToString => {closure:%s:%d}(): Argument #1 ($arg) must be of type int|float|bool, WithToString given
112
113Type int|bool:
11442               => 42
11542.0             => {closure:%s:%d}(): Argument #1 ($arg) must be of type int|bool, float given
116INF              => {closure:%s:%d}(): Argument #1 ($arg) must be of type int|bool, float given
117"42"             => {closure:%s:%d}(): Argument #1 ($arg) must be of type int|bool, string given
118"42.0"           => {closure:%s:%d}(): Argument #1 ($arg) must be of type int|bool, string given
119"42x"            => {closure:%s:%d}(): Argument #1 ($arg) must be of type int|bool, string given
120"x"              => {closure:%s:%d}(): Argument #1 ($arg) must be of type int|bool, string given
121""               => {closure:%s:%d}(): Argument #1 ($arg) must be of type int|bool, string given
122true             => true
123false            => false
124null             => {closure:%s:%d}(): Argument #1 ($arg) must be of type int|bool, null given
125[]               => {closure:%s:%d}(): Argument #1 ($arg) must be of type int|bool, array given
126new stdClass     => {closure:%s:%d}(): Argument #1 ($arg) must be of type int|bool, stdClass given
127new WithToString => {closure:%s:%d}(): Argument #1 ($arg) must be of type int|bool, WithToString given
128
129Type int|string|null:
13042               => 42
13142.0             => {closure:%s:%d}(): Argument #1 ($arg) must be of type string|int|null, float given
132INF              => {closure:%s:%d}(): Argument #1 ($arg) must be of type string|int|null, float given
133"42"             => "42"
134"42.0"           => "42.0"
135"42x"            => "42x"
136"x"              => "x"
137""               => ""
138true             => {closure:%s:%d}(): Argument #1 ($arg) must be of type string|int|null, true given
139false            => {closure:%s:%d}(): Argument #1 ($arg) must be of type string|int|null, false given
140null             => null
141[]               => {closure:%s:%d}(): Argument #1 ($arg) must be of type string|int|null, array given
142new stdClass     => {closure:%s:%d}(): Argument #1 ($arg) must be of type string|int|null, stdClass given
143new WithToString => {closure:%s:%d}(): Argument #1 ($arg) must be of type string|int|null, WithToString given
144
145Type string|bool:
14642               => {closure:%s:%d}(): Argument #1 ($arg) must be of type string|bool, int given
14742.0             => {closure:%s:%d}(): Argument #1 ($arg) must be of type string|bool, float given
148INF              => {closure:%s:%d}(): Argument #1 ($arg) must be of type string|bool, float given
149"42"             => "42"
150"42.0"           => "42.0"
151"42x"            => "42x"
152"x"              => "x"
153""               => ""
154true             => true
155false            => false
156null             => {closure:%s:%d}(): Argument #1 ($arg) must be of type string|bool, null given
157[]               => {closure:%s:%d}(): Argument #1 ($arg) must be of type string|bool, array given
158new stdClass     => {closure:%s:%d}(): Argument #1 ($arg) must be of type string|bool, stdClass given
159new WithToString => {closure:%s:%d}(): Argument #1 ($arg) must be of type string|bool, WithToString given
160
161Type float|array:
16242               => 42.0
16342.0             => 42.0
164INF              => INF
165"42"             => {closure:%s:%d}(): Argument #1 ($arg) must be of type array|float, string given
166"42.0"           => {closure:%s:%d}(): Argument #1 ($arg) must be of type array|float, string given
167"42x"            => {closure:%s:%d}(): Argument #1 ($arg) must be of type array|float, string given
168"x"              => {closure:%s:%d}(): Argument #1 ($arg) must be of type array|float, string given
169""               => {closure:%s:%d}(): Argument #1 ($arg) must be of type array|float, string given
170true             => {closure:%s:%d}(): Argument #1 ($arg) must be of type array|float, true given
171false            => {closure:%s:%d}(): Argument #1 ($arg) must be of type array|float, false given
172null             => {closure:%s:%d}(): Argument #1 ($arg) must be of type array|float, null given
173[]               => []
174new stdClass     => {closure:%s:%d}(): Argument #1 ($arg) must be of type array|float, stdClass given
175new WithToString => {closure:%s:%d}(): Argument #1 ($arg) must be of type array|float, WithToString given
176
177Type string|array:
17842               => {closure:%s:%d}(): Argument #1 ($arg) must be of type array|string, int given
17942.0             => {closure:%s:%d}(): Argument #1 ($arg) must be of type array|string, float given
180INF              => {closure:%s:%d}(): Argument #1 ($arg) must be of type array|string, float given
181"42"             => "42"
182"42.0"           => "42.0"
183"42x"            => "42x"
184"x"              => "x"
185""               => ""
186true             => {closure:%s:%d}(): Argument #1 ($arg) must be of type array|string, true given
187false            => {closure:%s:%d}(): Argument #1 ($arg) must be of type array|string, false given
188null             => {closure:%s:%d}(): Argument #1 ($arg) must be of type array|string, null given
189[]               => []
190new stdClass     => {closure:%s:%d}(): Argument #1 ($arg) must be of type array|string, stdClass given
191new WithToString => {closure:%s:%d}(): Argument #1 ($arg) must be of type array|string, WithToString given
192
193Type bool|array:
19442               => {closure:%s:%d}(): Argument #1 ($arg) must be of type array|bool, int given
19542.0             => {closure:%s:%d}(): Argument #1 ($arg) must be of type array|bool, float given
196INF              => {closure:%s:%d}(): Argument #1 ($arg) must be of type array|bool, float given
197"42"             => {closure:%s:%d}(): Argument #1 ($arg) must be of type array|bool, string given
198"42.0"           => {closure:%s:%d}(): Argument #1 ($arg) must be of type array|bool, string given
199"42x"            => {closure:%s:%d}(): Argument #1 ($arg) must be of type array|bool, string given
200"x"              => {closure:%s:%d}(): Argument #1 ($arg) must be of type array|bool, string given
201""               => {closure:%s:%d}(): Argument #1 ($arg) must be of type array|bool, string given
202true             => true
203false            => false
204null             => {closure:%s:%d}(): Argument #1 ($arg) must be of type array|bool, null given
205[]               => []
206new stdClass     => {closure:%s:%d}(): Argument #1 ($arg) must be of type array|bool, stdClass given
207new WithToString => {closure:%s:%d}(): Argument #1 ($arg) must be of type array|bool, WithToString given
208