Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Implement nullsafe ?-> operator #5619

Closed
wants to merge 25 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
638bcfe
Implement nullsafe ?-> operator
iluuu1994 May 24, 2020
9757ee0
Disallow ?-> in write context
iluuu1994 Jul 15, 2020
88aa39a
Compile error when taking ref of ?-> chain
iluuu1994 Jul 15, 2020
6acd29c
Fix isset() directly on nullsafe
nikic Jul 16, 2020
77e60ce
Add test case for nullsafe in foreach by ref
nikic Jul 16, 2020
d850f13
Add nullsafe in foreach target test
iluuu1994 Jul 16, 2020
e7b2751
Don't convert foreach on nullsafe as ref
nikic Jul 16, 2020
b7fdb62
Fix SCCP on JMP_NULL
nikic Jul 16, 2020
3e25a35
Optimize JMP_NULL in dfa_pass
nikic Jul 16, 2020
2dba89d
Correct e-ssa pi node placement for jmp_null
nikic Jul 16, 2020
fb20008
Handle JMP_NULL in one more place
nikic Jul 16, 2020
a7d0505
Remvoe added phar.tar file
nikic Jul 16, 2020
83c56b2
Remove short-circuit handling for assignments
nikic Jul 16, 2020
7f9644d
Undo some more changes
nikic Jul 16, 2020
0f8fce5
Fix nullsafe chain in varvar
nikic Jul 16, 2020
efe2cc7
Correctly handle static property / method name
nikic Jul 16, 2020
207f7a3
Add test file
nikic Jul 16, 2020
4f2bf30
Alternative implementation for short-circuiting
nikic Jul 16, 2020
b12abdb
Make opcache test i386 compatible
nikic Jul 22, 2020
230eb9e
Use ZVAL_TRUE/FALSE macros
nikic Jul 22, 2020
9eae721
Remove redundant return
nikic Jul 22, 2020
44cbf31
Use more efficient argument passing opcode if possible
nikic Jul 22, 2020
7cbe31e
Remove unnecessary FREE_OP1(), reduce specialization
nikic Jul 22, 2020
9dd99c8
Improve nullsafe in binary op tests
iluuu1994 Jul 23, 2020
3af4df8
Add tests for ?-> in sub-chain by ref
iluuu1994 Jul 23, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 106 additions & 0 deletions Zend/tests/nullsafe_operator/001.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
--TEST--
Test basic nullsafe method calls
--FILE--
<?php

class Foo {
function null() {
var_dump('Foo::null()');
return null;
}

function self() {
var_dump('Foo::self()');
return $this;
}
}

var_dump(null?->bar());
var_dump(null?->bar(var_dump('Not executed')));
var_dump(null?->bar()->baz());
var_dump(null?->bar()->baz(var_dump('Not executed')));
var_dump(null?->bar()->baz);
var_dump(null?->bar()::$baz);
var_dump(null?->bar()::baz());

$foo = new Foo();
var_dump($foo->null()?->bar());
var_dump($foo->null()?->bar(var_dump('Not executed')));
var_dump($foo->null()?->bar()->baz());
var_dump($foo->null()?->bar()->baz(var_dump('Not executed')));
var_dump($foo->null()?->bar()->baz);
var_dump($foo->null()?->bar()::$baz);
var_dump($foo->null()?->bar()::baz());

$foo = new Foo();
var_dump($foo?->self(var_dump('Executed'))->null()?->bar());
var_dump($foo?->self(var_dump('Executed'))->null()?->bar(var_dump('Not executed')));
var_dump($foo?->self(var_dump('Executed'))->null()?->bar()->baz());
var_dump($foo?->self(var_dump('Executed'))->null()?->bar()->baz(var_dump('Not executed')));
var_dump($foo?->self(var_dump('Executed'))->null()?->bar()->baz);
var_dump($foo?->self(var_dump('Executed'))->null()?->bar()::$baz);
var_dump($foo?->self(var_dump('Executed'))->null()?->bar()::baz());

var_dump($foo->self(null?->bar())->null());
try {
var_dump($foo?->self()[null?->bar()]);
} catch (Throwable $e) {
var_dump($e->getMessage());
}

?>
--EXPECT--
NULL
NULL
NULL
NULL
NULL
NULL
NULL
string(11) "Foo::null()"
NULL
string(11) "Foo::null()"
NULL
string(11) "Foo::null()"
NULL
string(11) "Foo::null()"
NULL
string(11) "Foo::null()"
NULL
string(11) "Foo::null()"
NULL
string(11) "Foo::null()"
NULL
string(8) "Executed"
string(11) "Foo::self()"
string(11) "Foo::null()"
NULL
string(8) "Executed"
string(11) "Foo::self()"
string(11) "Foo::null()"
NULL
string(8) "Executed"
string(11) "Foo::self()"
string(11) "Foo::null()"
NULL
string(8) "Executed"
string(11) "Foo::self()"
string(11) "Foo::null()"
NULL
string(8) "Executed"
string(11) "Foo::self()"
string(11) "Foo::null()"
NULL
string(8) "Executed"
string(11) "Foo::self()"
string(11) "Foo::null()"
NULL
string(8) "Executed"
string(11) "Foo::self()"
string(11) "Foo::null()"
NULL
string(11) "Foo::self()"
string(11) "Foo::null()"
NULL
string(11) "Foo::self()"
string(38) "Cannot use object of type Foo as array"
42 changes: 42 additions & 0 deletions Zend/tests/nullsafe_operator/002.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
--TEST--
Test nullsafe strict type check
--FILE--
<?php

try {
false?->bar();
} catch (Throwable $e) {
var_dump($e->getMessage());
}

try {
[]?->bar();
} catch (Throwable $e) {
var_dump($e->getMessage());
}

try {
(0)?->bar();
} catch (Throwable $e) {
var_dump($e->getMessage());
}

try {
(0.0)?->bar();
} catch (Throwable $e) {
var_dump($e->getMessage());
}

try {
''?->bar();
} catch (Throwable $e) {
var_dump($e->getMessage());
}

?>
--EXPECT--
string(39) "Call to a member function bar() on bool"
string(40) "Call to a member function bar() on array"
string(38) "Call to a member function bar() on int"
string(40) "Call to a member function bar() on float"
string(41) "Call to a member function bar() on string"
66 changes: 66 additions & 0 deletions Zend/tests/nullsafe_operator/003.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
--TEST--
Test basic nullsafe property fetching
--FILE--
<?php

class Foo {
public $bar = 'bar';

function qux() {
return 'qux';
}
}

$null = null;
$foo = new Foo();

var_dump(null?->bar);
var_dump(null?->baz);
var_dump(null?->qux());
var_dump(null?->quux());

var_dump((new Foo)?->bar);
var_dump((new Foo)?->baz);
var_dump((new Foo)?->qux());
try {
var_dump((new Foo)?->quux());
} catch (Throwable $e) {
var_dump($e->getMessage());
}

var_dump("{$null?->foo}");
var_dump("{$null?->bar}");
var_dump("{$null?->qux()}");
var_dump("{$null?->quux()}");

var_dump("{$foo?->bar}");
var_dump("{$foo?->baz}");
var_dump("{$foo?->qux()}");
try {
var_dump("{$foo?->quux()}");
} catch (Throwable $e) {
var_dump($e->getMessage());
}

?>
--EXPECTF--
NULL
NULL
NULL
NULL
string(3) "bar"

Warning: Undefined property: Foo::$baz in %s.php on line 20
NULL
string(3) "qux"
string(36) "Call to undefined method Foo::quux()"
string(0) ""
string(0) ""
string(0) ""
string(0) ""
string(3) "bar"

Warning: Undefined property: Foo::$baz in %s.php on line 34
string(0) ""
string(3) "qux"
string(36) "Call to undefined method Foo::quux()"
11 changes: 11 additions & 0 deletions Zend/tests/nullsafe_operator/004.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
--TEST--
Test nullsafe property assignment
--FILE--
<?php

$foo = null;
var_dump($foo?->bar = 'bar');

?>
--EXPECTF--
Fatal error: Can't use nullsafe operator in write context in %s.php on line 4
11 changes: 11 additions & 0 deletions Zend/tests/nullsafe_operator/005.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
--TEST--
Test nullsafe property assignment op
--FILE--
<?php

$foo = null;
$foo?->bar += 1;

?>
--EXPECTF--
Fatal error: Can't use nullsafe operator in write context in %s.php on line 4
11 changes: 11 additions & 0 deletions Zend/tests/nullsafe_operator/006.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
--TEST--
Test nullsafe property post increment
--FILE--
<?php

$foo = null;
++$foo?->bar;

?>
--EXPECTF--
Fatal error: Can't use nullsafe operator in write context in %s.php on line 4
11 changes: 11 additions & 0 deletions Zend/tests/nullsafe_operator/007.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
--TEST--
Test nullsafe property pre increment
--FILE--
<?php

$foo = null;
var_dump($foo?->bar++);

?>
--EXPECTF--
Fatal error: Can't use nullsafe operator in write context in %s.php on line 4
11 changes: 11 additions & 0 deletions Zend/tests/nullsafe_operator/008.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
--TEST--
Test nullsafe property coalesce assignment
--FILE--
<?php

$foo = null;
var_dump($foo?->bar ??= 'bar');

?>
--EXPECTF--
Fatal error: Can't use nullsafe operator in write context in %s.php on line 4
11 changes: 11 additions & 0 deletions Zend/tests/nullsafe_operator/009.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
--TEST--
Test fetch nullsafe property by ref
--FILE--
<?php

$foo = null;
$ref = &$foo?->bar

?>
--EXPECTF--
Fatal error: Cannot take reference of a nullsafe chain in %s.php on line 4
11 changes: 11 additions & 0 deletions Zend/tests/nullsafe_operator/010.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
--TEST--
Test fetch nested nullsafe property by ref
--FILE--
<?php

$foo = null;
$ref = &$foo?->bar->baz;

?>
--EXPECTF--
Fatal error: Cannot take reference of a nullsafe chain in %s.php on line 4
54 changes: 54 additions & 0 deletions Zend/tests/nullsafe_operator/011.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
--TEST--
Test isset and empty on nullsafe property
--FILE--
<?php

class Foo {
public $bar;
}

class Bar {
public $baz;
}
$bar = new Bar();
$bar->baz = 'baz';

var_dump(isset($foo?->bar));
var_dump(empty($foo?->bar));

var_dump(isset($foo?->bar->baz));
var_dump(empty($foo?->bar->baz));
echo "\n";

$foo = null;
var_dump(isset($foo?->bar));
var_dump(empty($foo?->bar));

var_dump(isset($foo?->bar->baz));
var_dump(empty($foo?->bar->baz));
echo "\n";

$foo = new Foo();
var_dump(isset($foo?->bar->baz));
var_dump(empty($foo?->bar->baz));

$foo->bar = $bar;
var_dump(isset($foo?->bar->baz));
var_dump(empty($foo?->bar->baz));

?>
--EXPECT--
bool(false)
bool(true)
bool(false)
bool(true)

bool(false)
bool(true)
bool(false)
bool(true)

bool(false)
bool(true)
bool(true)
bool(false)
18 changes: 18 additions & 0 deletions Zend/tests/nullsafe_operator/012.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
--TEST--
Test nullsafe property on reference
--FILE--
<?php

class Foo {
public $bar;
}

$foo = new Foo();
$foo->bar = 'bar';

$fooRef = &$foo;
var_dump($fooRef?->bar);

?>
--EXPECT--
string(3) "bar"
Loading