Skip to content

Commit ec061a9

Browse files
committed
Allow arbitrary expressions for empty()
This change is as per RFC https://wiki.php.net/rfc/empty_isset_exprs. The change allows passing the result of function calls and other expressions to the empty() language construct. This is accomplished by simply rewriting empty(expr) to !expr. The change does not affect the suppression of errors when using empty() on variables. empty($undefinedVar) will continue not to throw errors. When an expression is used inside empty() on the other hand, errors will not be suppressed. Thus empty($undefinedVar + $somethingElse) *will* throw a notice. The change also does not make empty() into a real function, so using 'empty' as a callback is still not possible. In addition to the empty() changes the commit adds nicer error messages when isset() is used on function call results or other expressions.
1 parent 5852e5f commit ec061a9

File tree

7 files changed

+73
-4
lines changed

7 files changed

+73
-4
lines changed

NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ PHP NEWS
77
. World domination
88
. Improve set_exception_handler while doing reset.(Laruence)
99
. Support constant array/string dereferencing. (Laruence)
10+
. Add support for using empty() on the result of function calls and
11+
other expressions (https://wiki.php.net/rfc/empty_isset_exprs).
12+
(Nikita Popov)
1013

1114
- Core:
1215
. Fixed bug #61681 (Malformed grammar). (Nikita Popov, Etienne, Laruence).

UPGRADING

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ PHP X.Y UPGRADE NOTES
2828

2929
- Support constant array/string dereferencing. (Laruence)
3030
(https://wiki.php.net/rfc/constdereference)
31+
- Add support for using empty() on the result of function calls and
32+
other expressions. Thus it is now possible to write empty(getArray()),
33+
for example. (https://wiki.php.net/rfc/empty_isset_exprs)
3134

3235
========================================
3336
2. Changes in SAPI modules
@@ -51,7 +54,7 @@ PHP X.Y UPGRADE NOTES
5154
- Implemented format character "Z": NUL-padded string
5255
- "a" now does not remove trailing NUL characters on unpack() anymore
5356
- "A" will now strip all trailing ASCII whitespace on unpack() (it used to
54-
remove only trailing spaces.
57+
remove only trailing spaces)
5558

5659
========================================
5760
5. New Functions

Zend/tests/empty_with_expr.phpt

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
--TEST--
2+
empty() with arbitrary expressions
3+
--FILE--
4+
<?php
5+
6+
function getEmptyArray() { return []; }
7+
function getNonEmptyArray() { return [1, 2, 3]; }
8+
9+
var_dump(empty([]));
10+
var_dump(empty([1, 2, 3]));
11+
12+
var_dump(empty(getEmptyArray()));
13+
var_dump(empty(getNonEmptyArray()));
14+
15+
var_dump(empty([] + []));
16+
var_dump(empty([1, 2, 3] + []));
17+
18+
var_dump(empty("string"));
19+
var_dump(empty(""));
20+
var_dump(empty(true));
21+
var_dump(empty(false));
22+
--EXPECT--
23+
bool(true)
24+
bool(false)
25+
bool(true)
26+
bool(false)
27+
bool(true)
28+
bool(false)
29+
bool(false)
30+
bool(true)
31+
bool(false)
32+
bool(true)

Zend/tests/isset_expr_error.phpt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
--TEST--
2+
Error message for isset(func())
3+
--FILE--
4+
<?php
5+
isset(1 + 1);
6+
?>
7+
--EXPECTF--
8+
Fatal error: Cannot use isset() on the result of an expression (you can use "null !== expression" instead) in %s on line %d

Zend/tests/isset_func_error.phpt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
--TEST--
2+
Error message for isset(func())
3+
--FILE--
4+
<?php
5+
isset(abc());
6+
?>
7+
--EXPECTF--
8+
Fatal error: Cannot use isset() on the result of a function call (you can use "null !== func()" instead) in %s on line %d

Zend/zend_compile.c

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6089,7 +6089,16 @@ void zend_do_isset_or_isempty(int type, znode *result, znode *variable TSRMLS_DC
60896089

60906090
zend_do_end_variable_parse(variable, BP_VAR_IS, 0 TSRMLS_CC);
60916091

6092-
zend_check_writable_variable(variable);
6092+
if (zend_is_function_or_method_call(variable)) {
6093+
if (type == ZEND_ISEMPTY) {
6094+
/* empty(func()) can be transformed to !func() */
6095+
zend_do_unary_op(ZEND_BOOL_NOT, result, variable TSRMLS_CC);
6096+
} else {
6097+
zend_error(E_COMPILE_ERROR, "Cannot use isset() on the result of a function call (you can use \"null !== func()\" instead)");
6098+
}
6099+
6100+
return;
6101+
}
60936102

60946103
if (variable->op_type == IS_CV) {
60956104
last_op = get_next_op(CG(active_op_array) TSRMLS_CC);

Zend/zend_language_parser.y

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1158,6 +1158,7 @@ encaps_var_offset:
11581158
internal_functions_in_yacc:
11591159
T_ISSET '(' isset_variables ')' { $$ = $3; }
11601160
| T_EMPTY '(' variable ')' { zend_do_isset_or_isempty(ZEND_ISEMPTY, &$$, &$3 TSRMLS_CC); }
1161+
| T_EMPTY '(' expr_without_variable ')' { zend_do_unary_op(ZEND_BOOL_NOT, &$$, &$3 TSRMLS_CC); }
11611162
| T_INCLUDE expr { zend_do_include_or_eval(ZEND_INCLUDE, &$$, &$2 TSRMLS_CC); }
11621163
| T_INCLUDE_ONCE expr { zend_do_include_or_eval(ZEND_INCLUDE_ONCE, &$$, &$2 TSRMLS_CC); }
11631164
| T_EVAL '(' expr ')' { zend_do_include_or_eval(ZEND_EVAL, &$$, &$3 TSRMLS_CC); }
@@ -1166,8 +1167,13 @@ internal_functions_in_yacc:
11661167
;
11671168

11681169
isset_variables:
1169-
variable { zend_do_isset_or_isempty(ZEND_ISSET, &$$, &$1 TSRMLS_CC); }
1170-
| isset_variables ',' { zend_do_boolean_and_begin(&$1, &$2 TSRMLS_CC); } variable { znode tmp; zend_do_isset_or_isempty(ZEND_ISSET, &tmp, &$4 TSRMLS_CC); zend_do_boolean_and_end(&$$, &$1, &tmp, &$2 TSRMLS_CC); }
1170+
isset_variable { $$ = $1; }
1171+
| isset_variables ',' { zend_do_boolean_and_begin(&$1, &$2 TSRMLS_CC); } isset_variable { zend_do_boolean_and_end(&$$, &$1, &$4, &$2 TSRMLS_CC); }
1172+
;
1173+
1174+
isset_variable:
1175+
variable { zend_do_isset_or_isempty(ZEND_ISSET, &$$, &$1 TSRMLS_CC); }
1176+
| expr_without_variable { zend_error(E_COMPILE_ERROR, "Cannot use isset() on the result of an expression (you can use \"null !== expression\" instead)"); }
11711177
;
11721178

11731179
class_constant:

0 commit comments

Comments
 (0)