]> granicus.if.org Git - php/commitdiff
Fixed bug #71731
authorNikita Popov <nikic@php.net>
Sun, 20 Mar 2016 12:33:00 +0000 (13:33 +0100)
committerNikita Popov <nikic@php.net>
Sun, 20 Mar 2016 12:35:00 +0000 (13:35 +0100)
The read_dimension() handler in BP_VAR_IS mode will now call
offsetExists() before caling offsetGet(). This has always been a
problem, however recently the issue has been exacerbated, because
the null-coalesce operator ?? makes it visible for non-nested
array accesses.

Also fixes #69659.

NEWS
Zend/tests/bug71731.phpt [new file with mode: 0644]
Zend/zend_object_handlers.c

diff --git a/NEWS b/NEWS
index d97e7c5a80a307f61f831267ac91539419626ac1..0cccc6abe7b3141c2a26fc891383f7b751894232 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -8,6 +8,9 @@ PHP                                                                        NEWS
   . Fixed Bug #71859 (zend_objects_store_call_destructors operates on realloced
     memory, crashing). (Laruence)
   . Fixed bug #71841 (EG(error_zval) is not handled well). (Laruence)
+  . Fixed bug #71731 (Null coalescing operator and ArrayAccess). (Nikita)
+  . Fixed bug #69659 (ArrayAccess, isset() and the offsetExists method).
+    (Nikita)
 
 - ODBC:
   . Fixed bug #63171 (Script hangs after max_execution_time). (Remi)
diff --git a/Zend/tests/bug71731.phpt b/Zend/tests/bug71731.phpt
new file mode 100644 (file)
index 0000000..46a79f1
--- /dev/null
@@ -0,0 +1,64 @@
+--TEST--
+Bug #71731: Null coalescing operator and ArrayAccess
+--FILE--
+<?php
+
+class AA implements ArrayAccess {
+    private $data = [];
+    public function offsetExists($name) {
+        echo "offsetExists($name)\n";
+        return array_key_exists($name, $this->data);
+    }
+    public function &offsetGet($name) {
+        echo "offsetGet($name)\n";
+        if (!array_key_exists($name, $this->data)) {
+            throw new Exception('Unknown offset');
+        }
+        return $this->data[$name];
+    }
+    public function offsetSet($name, $value) {
+        echo "offsetSet($name)\n";
+        $this->data[$name] = $value;
+    }
+    public function offsetUnset($name) {
+        echo "offsetUnset($name)\n";
+        unset($this->data[$name]);
+    }
+}
+
+$aa = new AA;
+var_dump(isset($aa[0][1][2]));
+var_dump(isset($aa[0]->foo));
+var_dump($aa[0] ?? 42);
+var_dump($aa[0][1][2] ?? 42);
+
+$aa[0] = new AA;
+$aa[0][1] = new AA;
+var_dump(isset($aa[0][1][2]));
+var_dump($aa[0][1][2] ?? 42);
+
+?>
+--EXPECT--
+offsetExists(0)
+bool(false)
+offsetExists(0)
+bool(false)
+offsetExists(0)
+int(42)
+offsetExists(0)
+int(42)
+offsetSet(0)
+offsetGet(0)
+offsetSet(1)
+offsetExists(0)
+offsetGet(0)
+offsetExists(1)
+offsetGet(1)
+offsetExists(2)
+bool(false)
+offsetExists(0)
+offsetGet(0)
+offsetExists(1)
+offsetGet(1)
+offsetExists(2)
+int(42)
index 091a0a0d5382b9aa471070d323ab781263dfada9..7b31fdc52442daf6dc699c0e68ed0c75bd02728c 100644 (file)
@@ -710,13 +710,26 @@ zval *zend_std_read_dimension(zval *object, zval *offset, int type, zval *rv) /*
        zval tmp;
 
        if (EXPECTED(instanceof_function_ex(ce, zend_ce_arrayaccess, 1) != 0)) {
-               if(offset == NULL) {
+               if (offset == NULL) {
                        /* [] construct */
                        ZVAL_NULL(&tmp);
                        offset = &tmp;
                } else {
                        SEPARATE_ARG_IF_REF(offset);
                }
+
+               if (type == BP_VAR_IS) {
+                       zend_call_method_with_1_params(object, ce, NULL, "offsetexists", rv, offset);
+                       if (UNEXPECTED(Z_ISUNDEF_P(rv))) {
+                               return NULL;
+                       }
+                       if (!i_zend_is_true(rv)) {
+                               zval_ptr_dtor(rv);
+                               return &EG(uninitialized_zval);
+                       }
+                       zval_ptr_dtor(rv);
+               }
+
                zend_call_method_with_1_params(object, ce, NULL, "offsetget", rv, offset);
 
                zval_ptr_dtor(offset);