Skip to content

Commit 57c628f

Browse files
committed
Add true as a type
1 parent fcba0a4 commit 57c628f

16 files changed

+189
-0
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
true type cannot take part in an intersection type
3+
--FILE--
4+
<?php
5+
6+
function foo(): true&Iterator {}
7+
8+
?>
9+
--EXPECTF--
10+
Fatal error: Type true cannot be part of an intersection type in %s on line %d
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
--TEST--
2+
No coercion should be applied to type false
3+
--FILE--
4+
<?php
5+
6+
function test(false $v) { var_dump($v); }
7+
8+
try {
9+
test(0);
10+
} catch (\TypeError $e) {
11+
echo $e->getMessage(), \PHP_EOL;
12+
}
13+
try {
14+
test('');
15+
} catch (\TypeError $e) {
16+
echo $e->getMessage(), \PHP_EOL;
17+
}
18+
try {
19+
test([]);
20+
} catch (\TypeError $e) {
21+
echo $e->getMessage(), \PHP_EOL;
22+
}
23+
24+
?>
25+
--EXPECTF--
26+
test(): Argument #1 ($v) must be of type false, int given, called in %s on line %d
27+
test(): Argument #1 ($v) must be of type false, string given, called in %s on line %d
28+
test(): Argument #1 ($v) must be of type false, array given, called in %s on line %d
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
--TEST--
2+
No coercion should be applied to type true
3+
--FILE--
4+
<?php
5+
6+
function test(true $v) { var_dump($v); }
7+
8+
try {
9+
test(1);
10+
} catch (\TypeError $e) {
11+
echo $e->getMessage(), \PHP_EOL;
12+
}
13+
try {
14+
test('1');
15+
} catch (\TypeError $e) {
16+
echo $e->getMessage(), \PHP_EOL;
17+
}
18+
try {
19+
test([1]);
20+
} catch (\TypeError $e) {
21+
echo $e->getMessage(), \PHP_EOL;
22+
}
23+
try {
24+
test(new stdClass());
25+
} catch (\TypeError $e) {
26+
echo $e->getMessage(), \PHP_EOL;
27+
}
28+
29+
?>
30+
--EXPECTF--
31+
test(): Argument #1 ($v) must be of type true, int given, called in %s on line %d
32+
test(): Argument #1 ($v) must be of type true, string given, called in %s on line %d
33+
test(): Argument #1 ($v) must be of type true, array given, called in %s on line %d
34+
test(): Argument #1 ($v) must be of type true, stdClass given, called in %s on line %d
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
--TEST--
2+
true can be used as a standalone type
3+
--FILE--
4+
<?php
5+
6+
function test(true $v): true {
7+
return $v;
8+
}
9+
10+
var_dump(test(true));
11+
12+
?>
13+
===DONE===
14+
--EXPECT--
15+
bool(true)
16+
===DONE===
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
--TEST--
2+
true can be used as a standalone type even with implicit nullability
3+
--FILE--
4+
<?php
5+
6+
function test(true $v = null) {}
7+
8+
?>
9+
===DONE===
10+
--EXPECT--
11+
===DONE===
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
--TEST--
2+
Test typed properties allow true
3+
--FILE--
4+
<?php
5+
class Foo {
6+
public true $value;
7+
}
8+
9+
$foo = new Foo();
10+
11+
try {
12+
$foo->value = false;
13+
} catch (\TypeError $e) {
14+
echo $e->getMessage();
15+
}
16+
17+
?>
18+
--EXPECT--
19+
Cannot assign bool to property Foo::$value of type true
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
--TEST--
2+
Using both bool and true in a union
3+
--FILE--
4+
<?php
5+
6+
function test(): bool|true {
7+
}
8+
9+
?>
10+
--EXPECTF--
11+
Fatal error: Duplicate type true is redundant in %s on line %d
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
--TEST--
2+
Using both false and true in a union instead of bool
3+
--FILE--
4+
<?php
5+
6+
function test(): false|true {
7+
}
8+
9+
?>
10+
--EXPECTF--
11+
Fatal error: Type contains both true and false, bool should be used instead in %s on line %d
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
--TEST--
2+
Using both true and false in a union instead of bool
3+
--FILE--
4+
<?php
5+
6+
function test(): true|false {
7+
}
8+
9+
?>
10+
--EXPECTF--
11+
Fatal error: Type contains both true and false, bool should be used instead in %s on line %d

Zend/zend_compile.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@ typedef struct _builtin_type_info {
216216

217217
static const builtin_type_info builtin_types[] = {
218218
{ZEND_STRL("null"), IS_NULL},
219+
{ZEND_STRL("true"), IS_TRUE},
219220
{ZEND_STRL("false"), IS_FALSE},
220221
{ZEND_STRL("int"), IS_LONG},
221222
{ZEND_STRL("float"), IS_DOUBLE},
@@ -1249,6 +1250,8 @@ zend_string *zend_type_to_string_resolved(zend_type type, zend_class_entry *scop
12491250
str = add_type_string(str, ZSTR_KNOWN(ZEND_STR_BOOL), /* is_intersection */ false);
12501251
} else if (type_mask & MAY_BE_FALSE) {
12511252
str = add_type_string(str, ZSTR_KNOWN(ZEND_STR_FALSE), /* is_intersection */ false);
1253+
} else if (type_mask & MAY_BE_TRUE) {
1254+
str = add_type_string(str, ZSTR_KNOWN(ZEND_STR_TRUE), /* is_intersection */ false);
12521255
}
12531256
if (type_mask & MAY_BE_VOID) {
12541257
str = add_type_string(str, ZSTR_KNOWN(ZEND_STR_VOID), /* is_intersection */ false);
@@ -6232,6 +6235,11 @@ static zend_type zend_compile_typename(
62326235
zend_error_noreturn(E_COMPILE_ERROR,
62336236
"Duplicate type %s is redundant", ZSTR_VAL(overlap_type_str));
62346237
}
6238+
if ( ((ZEND_TYPE_PURE_MASK(type) & MAY_BE_TRUE) && (single_type_mask == MAY_BE_FALSE))
6239+
|| ((ZEND_TYPE_PURE_MASK(type) & MAY_BE_FALSE) && (single_type_mask == MAY_BE_TRUE)) ) {
6240+
zend_error_noreturn(E_COMPILE_ERROR,
6241+
"Type contains both true and false, bool should be used instead");
6242+
}
62356243
ZEND_TYPE_FULL_MASK(type) |= ZEND_TYPE_PURE_MASK(single_type);
62366244
ZEND_TYPE_FULL_MASK(single_type) &= ~_ZEND_TYPE_MAY_BE_MASK;
62376245

Zend/zend_string.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,7 @@ EMPTY_SWITCH_DEFAULT_CASE()
578578
_(ZEND_STR_VOID, "void") \
579579
_(ZEND_STR_NEVER, "never") \
580580
_(ZEND_STR_FALSE, "false") \
581+
_(ZEND_STR_TRUE, "true") \
581582
_(ZEND_STR_NULL_LOWERCASE, "null") \
582583
_(ZEND_STR_MIXED, "mixed") \
583584
_(ZEND_STR_SLEEP, "__sleep") \

ext/reflection/php_reflection.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3123,6 +3123,8 @@ ZEND_METHOD(ReflectionUnionType, getTypes)
31233123
}
31243124
if ((type_mask & MAY_BE_BOOL) == MAY_BE_BOOL) {
31253125
append_type_mask(return_value, MAY_BE_BOOL);
3126+
} else if (type_mask & MAY_BE_TRUE) {
3127+
append_type_mask(return_value, MAY_BE_TRUE);
31263128
} else if (type_mask & MAY_BE_FALSE) {
31273129
append_type_mask(return_value, MAY_BE_FALSE);
31283130
}

ext/reflection/tests/ReflectionType_possible_types.phpt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ $functions = [
1414
function(): iterable {},
1515
function(): null {},
1616
function(): false {},
17+
function(): true {},
1718
function(): StdClass {}
1819
];
1920

@@ -34,4 +35,5 @@ string(8) "callable"
3435
string(8) "iterable"
3536
string(4) "null"
3637
string(5) "false"
38+
string(4) "true"
3739
string(8) "StdClass"

ext/reflection/tests/union_types.phpt

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ function test1(): X|Y|int|float|false|null { }
2424
function test2(): X|iterable|bool { }
2525
function test3(): null|false { }
2626
function test4(): ?false { }
27+
function test5(): X|iterable|true { }
28+
function test6(): null|true { }
29+
function test7(): ?true { }
2730

2831
class Test {
2932
public X|Y|int $prop;
@@ -33,6 +36,9 @@ dumpType((new ReflectionFunction('test1'))->getReturnType());
3336
dumpType((new ReflectionFunction('test2'))->getReturnType());
3437
dumpBCType((new ReflectionFunction('test3'))->getReturnType());
3538
dumpBCType((new ReflectionFunction('test4'))->getReturnType());
39+
dumpType((new ReflectionFunction('test5'))->getReturnType());
40+
dumpBCType((new ReflectionFunction('test6'))->getReturnType());
41+
dumpBCType((new ReflectionFunction('test7'))->getReturnType());
3642

3743
$rc = new ReflectionClass(Test::class);
3844
$rp = $rc->getProperty('prop');
@@ -94,6 +100,25 @@ Type ?false:
94100
Name: false
95101
String: ?false
96102
Allows Null: true
103+
Type X|iterable|true:
104+
Allows null: false
105+
Name: X
106+
String: X
107+
Allows Null: false
108+
Name: iterable
109+
String: iterable
110+
Allows Null: false
111+
Name: true
112+
String: true
113+
Allows Null: false
114+
Type ?true:
115+
Name: true
116+
String: ?true
117+
Allows Null: true
118+
Type ?true:
119+
Name: true
120+
String: ?true
121+
Allows Null: true
97122
Type X|Y|int:
98123
Allows null: false
99124
Name: X

0 commit comments

Comments
 (0)