Skip to content

Commit 9bf1198

Browse files
iluuu1994nikic
andcommitted
Implement nullsafe ?-> operator
RFC: https://wiki.php.net/rfc/nullsafe_operator Closes GH-5619. Co-authored-by: Nikita Popov <[email protected]>
1 parent 293d2f9 commit 9bf1198

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+1810
-517
lines changed

UPGRADING

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,8 @@ PHP 8.0 UPGRADE NOTES
607607
inheritance rules on the methods of a child class. (with the exception of
608608
final private constructors)
609609
RFC: https://wiki.php.net/rfc/inheritance_private_methods
610+
. Added support for nullsafe operator (`?->`).
611+
RFC: https://wiki.php.net/rfc/nullsafe_operator
610612

611613
- Date:
612614
. Added DateTime::createFromInterface() and

Zend/tests/nullsafe_operator/001.phpt

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
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(var_dump('Executed'))->null()?->bar());
37+
var_dump($foo?->self(var_dump('Executed'))->null()?->bar(var_dump('Not executed')));
38+
var_dump($foo?->self(var_dump('Executed'))->null()?->bar()->baz());
39+
var_dump($foo?->self(var_dump('Executed'))->null()?->bar()->baz(var_dump('Not executed')));
40+
var_dump($foo?->self(var_dump('Executed'))->null()?->bar()->baz);
41+
var_dump($foo?->self(var_dump('Executed'))->null()?->bar()::$baz);
42+
var_dump($foo?->self(var_dump('Executed'))->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+
?>
52+
--EXPECT--
53+
NULL
54+
NULL
55+
NULL
56+
NULL
57+
NULL
58+
NULL
59+
NULL
60+
string(11) "Foo::null()"
61+
NULL
62+
string(11) "Foo::null()"
63+
NULL
64+
string(11) "Foo::null()"
65+
NULL
66+
string(11) "Foo::null()"
67+
NULL
68+
string(11) "Foo::null()"
69+
NULL
70+
string(11) "Foo::null()"
71+
NULL
72+
string(11) "Foo::null()"
73+
NULL
74+
string(8) "Executed"
75+
string(11) "Foo::self()"
76+
string(11) "Foo::null()"
77+
NULL
78+
string(8) "Executed"
79+
string(11) "Foo::self()"
80+
string(11) "Foo::null()"
81+
NULL
82+
string(8) "Executed"
83+
string(11) "Foo::self()"
84+
string(11) "Foo::null()"
85+
NULL
86+
string(8) "Executed"
87+
string(11) "Foo::self()"
88+
string(11) "Foo::null()"
89+
NULL
90+
string(8) "Executed"
91+
string(11) "Foo::self()"
92+
string(11) "Foo::null()"
93+
NULL
94+
string(8) "Executed"
95+
string(11) "Foo::self()"
96+
string(11) "Foo::null()"
97+
NULL
98+
string(8) "Executed"
99+
string(11) "Foo::self()"
100+
string(11) "Foo::null()"
101+
NULL
102+
string(11) "Foo::self()"
103+
string(11) "Foo::null()"
104+
NULL
105+
string(11) "Foo::self()"
106+
string(38) "Cannot use object of type Foo as array"

Zend/tests/nullsafe_operator/002.phpt

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
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+
?>
37+
--EXPECT--
38+
string(39) "Call to a member function bar() on bool"
39+
string(40) "Call to a member function bar() on array"
40+
string(38) "Call to a member function bar() on int"
41+
string(40) "Call to a member function bar() on float"
42+
string(41) "Call to a member function bar() on string"

Zend/tests/nullsafe_operator/003.phpt

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
--TEST--
2+
Test basic nullsafe property fetching
3+
--FILE--
4+
<?php
5+
6+
class Foo {
7+
public $bar = 'bar';
8+
9+
function qux() {
10+
return 'qux';
11+
}
12+
}
13+
14+
$null = null;
15+
$foo = new Foo();
16+
17+
var_dump(null?->bar);
18+
var_dump(null?->baz);
19+
var_dump(null?->qux());
20+
var_dump(null?->quux());
21+
22+
var_dump((new Foo)?->bar);
23+
var_dump((new Foo)?->baz);
24+
var_dump((new Foo)?->qux());
25+
try {
26+
var_dump((new Foo)?->quux());
27+
} catch (Throwable $e) {
28+
var_dump($e->getMessage());
29+
}
30+
31+
var_dump("{$null?->foo}");
32+
var_dump("{$null?->bar}");
33+
var_dump("{$null?->qux()}");
34+
var_dump("{$null?->quux()}");
35+
36+
var_dump("{$foo?->bar}");
37+
var_dump("{$foo?->baz}");
38+
var_dump("{$foo?->qux()}");
39+
try {
40+
var_dump("{$foo?->quux()}");
41+
} catch (Throwable $e) {
42+
var_dump($e->getMessage());
43+
}
44+
45+
?>
46+
--EXPECTF--
47+
NULL
48+
NULL
49+
NULL
50+
NULL
51+
string(3) "bar"
52+
53+
Warning: Undefined property: Foo::$baz in %s.php on line 20
54+
NULL
55+
string(3) "qux"
56+
string(36) "Call to undefined method Foo::quux()"
57+
string(0) ""
58+
string(0) ""
59+
string(0) ""
60+
string(0) ""
61+
string(3) "bar"
62+
63+
Warning: Undefined property: Foo::$baz in %s.php on line 34
64+
string(0) ""
65+
string(3) "qux"
66+
string(36) "Call to undefined method Foo::quux()"

Zend/tests/nullsafe_operator/004.phpt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
--TEST--
2+
Test nullsafe property assignment
3+
--FILE--
4+
<?php
5+
6+
$foo = null;
7+
var_dump($foo?->bar = 'bar');
8+
9+
?>
10+
--EXPECTF--
11+
Fatal error: Can't use nullsafe operator in write context in %s.php on line 4

Zend/tests/nullsafe_operator/005.phpt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
--TEST--
2+
Test nullsafe property assignment op
3+
--FILE--
4+
<?php
5+
6+
$foo = null;
7+
$foo?->bar += 1;
8+
9+
?>
10+
--EXPECTF--
11+
Fatal error: Can't use nullsafe operator in write context in %s.php on line 4

Zend/tests/nullsafe_operator/006.phpt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
--TEST--
2+
Test nullsafe property post increment
3+
--FILE--
4+
<?php
5+
6+
$foo = null;
7+
++$foo?->bar;
8+
9+
?>
10+
--EXPECTF--
11+
Fatal error: Can't use nullsafe operator in write context in %s.php on line 4

Zend/tests/nullsafe_operator/007.phpt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
--TEST--
2+
Test nullsafe property pre increment
3+
--FILE--
4+
<?php
5+
6+
$foo = null;
7+
var_dump($foo?->bar++);
8+
9+
?>
10+
--EXPECTF--
11+
Fatal error: Can't use nullsafe operator in write context in %s.php on line 4

Zend/tests/nullsafe_operator/008.phpt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
--TEST--
2+
Test nullsafe property coalesce assignment
3+
--FILE--
4+
<?php
5+
6+
$foo = null;
7+
var_dump($foo?->bar ??= 'bar');
8+
9+
?>
10+
--EXPECTF--
11+
Fatal error: Can't use nullsafe operator in write context in %s.php on line 4

Zend/tests/nullsafe_operator/009.phpt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
--TEST--
2+
Test fetch nullsafe property by ref
3+
--FILE--
4+
<?php
5+
6+
$foo = null;
7+
$ref = &$foo?->bar
8+
9+
?>
10+
--EXPECTF--
11+
Fatal error: Cannot take reference of a nullsafe chain in %s.php on line 4

Zend/tests/nullsafe_operator/010.phpt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
--TEST--
2+
Test fetch nested nullsafe property by ref
3+
--FILE--
4+
<?php
5+
6+
$foo = null;
7+
$ref = &$foo?->bar->baz;
8+
9+
?>
10+
--EXPECTF--
11+
Fatal error: Cannot take reference of a nullsafe chain in %s.php on line 4

Zend/tests/nullsafe_operator/011.phpt

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
--TEST--
2+
Test isset and empty on nullsafe property
3+
--FILE--
4+
<?php
5+
6+
class Foo {
7+
public $bar;
8+
}
9+
10+
class Bar {
11+
public $baz;
12+
}
13+
$bar = new Bar();
14+
$bar->baz = 'baz';
15+
16+
var_dump(isset($foo?->bar));
17+
var_dump(empty($foo?->bar));
18+
19+
var_dump(isset($foo?->bar->baz));
20+
var_dump(empty($foo?->bar->baz));
21+
echo "\n";
22+
23+
$foo = null;
24+
var_dump(isset($foo?->bar));
25+
var_dump(empty($foo?->bar));
26+
27+
var_dump(isset($foo?->bar->baz));
28+
var_dump(empty($foo?->bar->baz));
29+
echo "\n";
30+
31+
$foo = new Foo();
32+
var_dump(isset($foo?->bar->baz));
33+
var_dump(empty($foo?->bar->baz));
34+
35+
$foo->bar = $bar;
36+
var_dump(isset($foo?->bar->baz));
37+
var_dump(empty($foo?->bar->baz));
38+
39+
?>
40+
--EXPECT--
41+
bool(false)
42+
bool(true)
43+
bool(false)
44+
bool(true)
45+
46+
bool(false)
47+
bool(true)
48+
bool(false)
49+
bool(true)
50+
51+
bool(false)
52+
bool(true)
53+
bool(true)
54+
bool(false)

Zend/tests/nullsafe_operator/012.phpt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
--TEST--
2+
Test nullsafe property on reference
3+
--FILE--
4+
<?php
5+
6+
class Foo {
7+
public $bar;
8+
}
9+
10+
$foo = new Foo();
11+
$foo->bar = 'bar';
12+
13+
$fooRef = &$foo;
14+
var_dump($fooRef?->bar);
15+
16+
?>
17+
--EXPECT--
18+
string(3) "bar"

0 commit comments

Comments
 (0)