]> granicus.if.org Git - php/commitdiff
Allow arbitrary expressions for empty()
authorNikita Popov <nikic@php.net>
Thu, 12 Apr 2012 09:54:52 +0000 (11:54 +0200)
committerNikita Popov <nikic@php.net>
Sun, 13 May 2012 12:56:51 +0000 (14:56 +0200)
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.

NEWS
UPGRADING
Zend/tests/empty_with_expr.phpt [new file with mode: 0644]
Zend/tests/isset_expr_error.phpt [new file with mode: 0644]
Zend/tests/isset_func_error.phpt [new file with mode: 0644]
Zend/zend_compile.c
Zend/zend_language_parser.y

diff --git a/NEWS b/NEWS
index 42c2f834ca29e206f883933442f5b83d1ad63a74..87ecf08bfc3b9f86c935b128daabae318bff70ce 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -7,6 +7,9 @@ PHP                                                                        NEWS
   . World domination
   . Improve set_exception_handler while doing reset.(Laruence)
   . Support constant array/string dereferencing. (Laruence)
+  . Add support for using empty() on the result of function calls and
+    other expressions (https://wiki.php.net/rfc/empty_isset_exprs).
+    (Nikita Popov)
 
 - Core:
   . Fixed bug #61681 (Malformed grammar). (Nikita Popov, Etienne, Laruence).
index 27d01cbe949308946313e87c9a6c7f81af2f1220..0fae6147db2c8af516cd5d98941d4af245daa4fb 100755 (executable)
--- a/UPGRADING
+++ b/UPGRADING
@@ -28,6 +28,9 @@ PHP X.Y UPGRADE NOTES
 
 - Support constant array/string dereferencing. (Laruence)
   (https://wiki.php.net/rfc/constdereference)
+- Add support for using empty() on the result of function calls and
+  other expressions. Thus it is now possible to write empty(getArray()),
+  for example. (https://wiki.php.net/rfc/empty_isset_exprs)
 
 ========================================
 2. Changes in SAPI modules
@@ -51,7 +54,7 @@ PHP X.Y UPGRADE NOTES
   - Implemented format character "Z": NUL-padded string
   - "a" now does not remove trailing NUL characters on unpack() anymore
   - "A" will now strip all trailing ASCII whitespace on unpack() (it used to
-    remove only trailing spaces.
+    remove only trailing spaces)
 
 ========================================
 5. New Functions
diff --git a/Zend/tests/empty_with_expr.phpt b/Zend/tests/empty_with_expr.phpt
new file mode 100644 (file)
index 0000000..582eb3d
--- /dev/null
@@ -0,0 +1,32 @@
+--TEST--
+empty() with arbitrary expressions
+--FILE--
+<?php
+
+function getEmptyArray() { return []; }
+function getNonEmptyArray() { return [1, 2, 3]; }
+
+var_dump(empty([]));
+var_dump(empty([1, 2, 3]));
+
+var_dump(empty(getEmptyArray()));
+var_dump(empty(getNonEmptyArray()));
+
+var_dump(empty([] + []));
+var_dump(empty([1, 2, 3] + []));
+
+var_dump(empty("string"));
+var_dump(empty(""));
+var_dump(empty(true));
+var_dump(empty(false));
+--EXPECT--
+bool(true)
+bool(false)
+bool(true)
+bool(false)
+bool(true)
+bool(false)
+bool(false)
+bool(true)
+bool(false)
+bool(true)
diff --git a/Zend/tests/isset_expr_error.phpt b/Zend/tests/isset_expr_error.phpt
new file mode 100644 (file)
index 0000000..27fc6cd
--- /dev/null
@@ -0,0 +1,8 @@
+--TEST--
+Error message for isset(func())
+--FILE--
+<?php
+isset(1 + 1);
+?>
+--EXPECTF--
+Fatal error: Cannot use isset() on the result of an expression (you can use "null !== expression" instead) in %s on line %d
diff --git a/Zend/tests/isset_func_error.phpt b/Zend/tests/isset_func_error.phpt
new file mode 100644 (file)
index 0000000..7d1036d
--- /dev/null
@@ -0,0 +1,8 @@
+--TEST--
+Error message for isset(func())
+--FILE--
+<?php
+isset(abc());
+?>
+--EXPECTF--
+Fatal error: Cannot use isset() on the result of a function call (you can use "null !== func()" instead) in %s on line %d
index 602b6004100b4505a4e3c4e630c84e57edf5c077..37f2dc2ef5833fd0f2fce835216e3203b27b556b 100644 (file)
@@ -6089,7 +6089,16 @@ void zend_do_isset_or_isempty(int type, znode *result, znode *variable TSRMLS_DC
 
        zend_do_end_variable_parse(variable, BP_VAR_IS, 0 TSRMLS_CC);
 
-       zend_check_writable_variable(variable);
+       if (zend_is_function_or_method_call(variable)) {
+               if (type == ZEND_ISEMPTY) {
+                       /* empty(func()) can be transformed to !func() */
+                       zend_do_unary_op(ZEND_BOOL_NOT, result, variable TSRMLS_CC);
+               } else {
+                       zend_error(E_COMPILE_ERROR, "Cannot use isset() on the result of a function call (you can use \"null !== func()\" instead)");
+               }
+
+               return;
+       }
 
        if (variable->op_type == IS_CV) {
                last_op = get_next_op(CG(active_op_array) TSRMLS_CC);
index 893e0133efe0f684681519479c1a6dc5d639dca4..5a5bb60cfb259438daaa987a27778dc557a684c2 100644 (file)
@@ -1158,6 +1158,7 @@ encaps_var_offset:
 internal_functions_in_yacc:
                T_ISSET '(' isset_variables ')' { $$ = $3; }
        |       T_EMPTY '(' variable ')'        { zend_do_isset_or_isempty(ZEND_ISEMPTY, &$$, &$3 TSRMLS_CC); }
+       |       T_EMPTY '(' expr_without_variable ')' { zend_do_unary_op(ZEND_BOOL_NOT, &$$, &$3 TSRMLS_CC); }
        |       T_INCLUDE expr                  { zend_do_include_or_eval(ZEND_INCLUDE, &$$, &$2 TSRMLS_CC); }
        |       T_INCLUDE_ONCE expr     { zend_do_include_or_eval(ZEND_INCLUDE_ONCE, &$$, &$2 TSRMLS_CC); }
        |       T_EVAL '(' expr ')'     { zend_do_include_or_eval(ZEND_EVAL, &$$, &$3 TSRMLS_CC); }
@@ -1166,8 +1167,13 @@ internal_functions_in_yacc:
 ;
 
 isset_variables:
-               variable                                { zend_do_isset_or_isempty(ZEND_ISSET, &$$, &$1 TSRMLS_CC); }
-       |       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); }
+               isset_variable                  { $$ = $1; }
+       |       isset_variables ',' { zend_do_boolean_and_begin(&$1, &$2 TSRMLS_CC); } isset_variable { zend_do_boolean_and_end(&$$, &$1, &$4, &$2 TSRMLS_CC); }
+;
+
+isset_variable:
+               variable                                { zend_do_isset_or_isempty(ZEND_ISSET, &$$, &$1 TSRMLS_CC); }
+       |       expr_without_variable   { zend_error(E_COMPILE_ERROR, "Cannot use isset() on the result of an expression (you can use \"null !== expression\" instead)"); }
 ;
 
 class_constant: