Skip to content

Commit 942da68

Browse files
committed
Implement nullsafe ?-> operator
1 parent 08858e7 commit 942da68

30 files changed

+1361
-523
lines changed
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
--TEST--
2+
Test basic nullsafe method calls
3+
--FILE--
4+
<?php
5+
6+
class Foo {
7+
function null() {
8+
var_dump('Foo::null()');
9+
return null;
10+
}
11+
12+
function self() {
13+
var_dump('Foo::self()');
14+
return $this;
15+
}
16+
}
17+
18+
var_dump(null?->bar());
19+
var_dump(null?->bar(var_dump('Not executed')));
20+
var_dump(null?->bar()->baz());
21+
var_dump(null?->bar()->baz(var_dump('Not executed')));
22+
var_dump(null?->bar()->baz);
23+
var_dump(null?->bar()::$baz);
24+
var_dump(null?->bar()::baz());
25+
26+
$foo = new Foo();
27+
var_dump($foo->null()?->bar());
28+
var_dump($foo->null()?->bar(var_dump('Not executed')));
29+
var_dump($foo->null()?->bar()->baz());
30+
var_dump($foo->null()?->bar()->baz(var_dump('Not executed')));
31+
var_dump($foo->null()?->bar()->baz);
32+
var_dump($foo->null()?->bar()::$baz);
33+
var_dump($foo->null()?->bar()::baz());
34+
35+
$foo = new Foo();
36+
var_dump($foo?->self()->null()?->bar());
37+
var_dump($foo?->self()->null()?->bar(var_dump('Not executed')));
38+
var_dump($foo?->self()->null()?->bar()->baz());
39+
var_dump($foo?->self()->null()?->bar()->baz(var_dump('Not executed')));
40+
var_dump($foo?->self()->null()?->bar()->baz);
41+
var_dump($foo?->self()->null()?->bar()::$baz);
42+
var_dump($foo?->self()->null()?->bar()::baz());
43+
44+
var_dump($foo->self(null?->bar())->null());
45+
try {
46+
var_dump($foo?->self()[null?->bar()]);
47+
} catch (Throwable $e) {
48+
var_dump($e->getMessage());
49+
}
50+
51+
--EXPECT--
52+
NULL
53+
NULL
54+
NULL
55+
NULL
56+
NULL
57+
NULL
58+
NULL
59+
string(11) "Foo::null()"
60+
NULL
61+
string(11) "Foo::null()"
62+
NULL
63+
string(11) "Foo::null()"
64+
NULL
65+
string(11) "Foo::null()"
66+
NULL
67+
string(11) "Foo::null()"
68+
NULL
69+
string(11) "Foo::null()"
70+
NULL
71+
string(11) "Foo::null()"
72+
NULL
73+
string(11) "Foo::self()"
74+
string(11) "Foo::null()"
75+
NULL
76+
string(11) "Foo::self()"
77+
string(11) "Foo::null()"
78+
NULL
79+
string(11) "Foo::self()"
80+
string(11) "Foo::null()"
81+
NULL
82+
string(11) "Foo::self()"
83+
string(11) "Foo::null()"
84+
NULL
85+
string(11) "Foo::self()"
86+
string(11) "Foo::null()"
87+
NULL
88+
string(11) "Foo::self()"
89+
string(11) "Foo::null()"
90+
NULL
91+
string(11) "Foo::self()"
92+
string(11) "Foo::null()"
93+
NULL
94+
string(11) "Foo::self()"
95+
string(11) "Foo::null()"
96+
NULL
97+
string(11) "Foo::self()"
98+
string(38) "Cannot use object of type Foo as array"
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
--TEST--
2+
Test nullsafe strict type check
3+
--FILE--
4+
<?php
5+
6+
try {
7+
false?->bar();
8+
} catch (Throwable $e) {
9+
var_dump($e->getMessage());
10+
}
11+
12+
try {
13+
[]?->bar();
14+
} catch (Throwable $e) {
15+
var_dump($e->getMessage());
16+
}
17+
18+
try {
19+
(0)?->bar();
20+
} catch (Throwable $e) {
21+
var_dump($e->getMessage());
22+
}
23+
24+
try {
25+
(0.0)?->bar();
26+
} catch (Throwable $e) {
27+
var_dump($e->getMessage());
28+
}
29+
30+
try {
31+
''?->bar();
32+
} catch (Throwable $e) {
33+
var_dump($e->getMessage());
34+
}
35+
36+
--EXPECT--
37+
string(39) "Call to a member function bar() on bool"
38+
string(40) "Call to a member function bar() on array"
39+
string(38) "Call to a member function bar() on int"
40+
string(40) "Call to a member function bar() on float"
41+
string(41) "Call to a member function bar() on string"
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
--TEST--
2+
Test basic nullsafe property fetching
3+
--FILE--
4+
<?php
5+
6+
class Foo {
7+
public $bar = 'bar';
8+
}
9+
10+
$null = null;
11+
$foo = new Foo();
12+
13+
var_dump(null?->foo);
14+
var_dump((new Foo)?->bar);
15+
var_dump((new Foo)?->baz);
16+
var_dump("{$null?->foo}");
17+
var_dump("{$foo?->bar}");
18+
var_dump("{$foo?->baz}");
19+
20+
--EXPECTF--
21+
NULL
22+
string(3) "bar"
23+
24+
Warning: Undefined property: Foo::$baz in %s003.php on line 12
25+
NULL
26+
string(0) ""
27+
string(3) "bar"
28+
29+
Warning: Undefined property: Foo::$baz in %s003.php on line 15
30+
string(0) ""
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
--TEST--
2+
Test nullsafe property assignment error
3+
--FILE--
4+
<?php
5+
6+
class Foo {
7+
public $bar;
8+
}
9+
10+
function test(?Foo $foo) {
11+
var_dump($foo?->bar = 'bar');
12+
var_dump($foo?->bar);
13+
}
14+
15+
test(null);
16+
test(new Foo());
17+
18+
--EXPECT--
19+
NULL
20+
NULL
21+
string(3) "bar"
22+
string(3) "bar"
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
--TEST--
2+
Test nullsafe property assignment op error
3+
--FILE--
4+
<?php
5+
6+
class Foo {
7+
public $bar = 0;
8+
}
9+
10+
function test(?Foo $foo) {
11+
var_dump($foo?->bar = 1);
12+
var_dump($foo?->bar);
13+
}
14+
15+
test(null);
16+
test(new Foo());
17+
18+
--EXPECT--
19+
NULL
20+
NULL
21+
int(1)
22+
int(1)
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
--TEST--
2+
Test nullsafe property post increment/decrement error
3+
--FILE--
4+
<?php
5+
6+
class Foo {
7+
public $bar = 0;
8+
}
9+
10+
function test(?Foo $foo) {
11+
var_dump(++$foo?->bar);
12+
var_dump($foo?->bar);
13+
var_dump(--$foo?->bar);
14+
var_dump($foo?->bar);
15+
}
16+
17+
test(null);
18+
test(new Foo());
19+
20+
--EXPECT--
21+
NULL
22+
NULL
23+
NULL
24+
NULL
25+
int(1)
26+
int(1)
27+
int(0)
28+
int(0)
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
--TEST--
2+
Test nullsafe property pre increment/decrement error
3+
--FILE--
4+
<?php
5+
6+
class Foo {
7+
public $bar = 0;
8+
}
9+
10+
function test(?Foo $foo) {
11+
var_dump($foo?->bar++);
12+
var_dump($foo?->bar);
13+
var_dump($foo?->bar--);
14+
var_dump($foo?->bar);
15+
}
16+
17+
test(null);
18+
test(new Foo());
19+
20+
--EXPECT--
21+
NULL
22+
NULL
23+
NULL
24+
NULL
25+
int(0)
26+
int(1)
27+
int(1)
28+
int(0)
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
--TEST--
2+
Test nullsafe property coalesce assignment error
3+
--SKIPIF--
4+
<?php if(extension_loaded("Zend OPcache")) die("skip with opcache because SSA incorrectly optimized"); ?>
5+
--FILE--
6+
<?php
7+
8+
class Foo {
9+
public $bar;
10+
}
11+
12+
function test(?Foo $foo) {
13+
var_dump($foo?->bar ??= 'bar');
14+
var_dump($foo?->bar);
15+
}
16+
17+
test(null);
18+
test(new Foo());
19+
20+
--EXPECT--
21+
NULL
22+
NULL
23+
string(3) "bar"
24+
string(3) "bar"
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
--TEST--
2+
Test fetch nullsafe property by ref error
3+
--FILE--
4+
<?php
5+
6+
class Foo {
7+
public $bar;
8+
}
9+
10+
function test(?Foo $foo) {
11+
var_dump($ref = &$foo?->bar);
12+
$ref = 'bar';
13+
var_dump($foo?->bar);
14+
}
15+
16+
test(null);
17+
test(new Foo());
18+
19+
--EXPECT--
20+
NULL
21+
NULL
22+
NULL
23+
string(3) "bar"
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
--TEST--
2+
Test fetch nested nullsafe property by ref error
3+
--FILE--
4+
<?php
5+
6+
class Foo {
7+
public $bar;
8+
}
9+
10+
class Bar {
11+
public $baz;
12+
}
13+
14+
function test(?Foo $foo) {
15+
var_dump($ref = &$foo?->bar->baz);
16+
$ref = 'baz';
17+
var_dump($foo?->bar->baz);
18+
}
19+
20+
test(null);
21+
22+
$foo = new Foo();
23+
$foo->bar = new Bar();
24+
test($foo);
25+
26+
--EXPECT--
27+
NULL
28+
NULL
29+
NULL
30+
string(3) "baz"
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
--TEST--
2+
Test isset and empty on nullsafe property
3+
--FILE--
4+
<?php
5+
6+
class Foo {
7+
public $bar;
8+
}
9+
10+
var_dump(isset($foo?->bar));
11+
var_dump(empty($foo?->bar));
12+
13+
$foo = null;
14+
var_dump(isset($foo?->bar));
15+
var_dump(empty($foo?->bar));
16+
17+
$foo = new Foo();
18+
var_dump(isset($foo?->bar));
19+
var_dump(empty($foo?->bar));
20+
21+
$foo->bar = 'bar';
22+
var_dump(isset($foo?->bar));
23+
var_dump(empty($foo?->bar));
24+
25+
--EXPECT--
26+
bool(false)
27+
bool(true)
28+
bool(false)
29+
bool(true)
30+
bool(false)
31+
bool(true)
32+
bool(true)
33+
bool(false)

0 commit comments

Comments
 (0)