]> granicus.if.org Git - php/commitdiff
Fix bug #67369 ArrayObject serializatino drops the iterator class
authorAlex Dowad <alexinbeijing@gmail.com>
Wed, 15 Apr 2020 13:25:14 +0000 (15:25 +0200)
committerNikita Popov <nikita.ppv@gmail.com>
Mon, 20 Apr 2020 09:55:18 +0000 (11:55 +0200)
When ArrayObject is round-tripped through serialize() and unserialize(),
it forgets any iterator class name which was set using ::setIteratorClass().
Fix that.

NEWS
ext/spl/spl_array.c
ext/spl/tests/ArrayObject__serialize_saves_iterator_class.phpt [new file with mode: 0644]
ext/spl/tests/array_025.phpt
ext/spl/tests/bug45826.phpt
ext/spl/tests/bug74669.phpt
ext/spl/tests/unserialize_errors.phpt
ext/standard/tests/serialize/bug45706.phpt

diff --git a/NEWS b/NEWS
index 034da7c676eabf5a6523e803b48448889e800f7c..60781fc2eba68bf04c9b6dad58f0434e7849edd8 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -28,6 +28,8 @@ PHP                                                                        NEWS
 
 - SPL:
   . Fixed bug #69264 (__debugInfo() ignored while extending SPL classes). (cmb)
+  . Fixed bug #67369 (ArrayObject serialization drops the iterator class).
+    (Alex Dowad)
 
 - Standard:
   . Fixed bug #79468 (SIGSEGV when closing stream handle with a stream filter
index 3803055d3023ccb36168f90347c20bc4eee71a08..5f70bfe7a03b760dd8fe33e2e3df7e85d2a70e17 100644 (file)
@@ -1840,6 +1840,14 @@ SPL_METHOD(Array, __serialize)
        ZVAL_ARR(&tmp, zend_std_get_properties(ZEND_THIS));
        Z_TRY_ADDREF(tmp);
        zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &tmp);
+
+       /* iterator class */
+       if (intern->ce_get_iterator == spl_ce_ArrayIterator) {
+               ZVAL_NULL(&tmp);
+       } else {
+               ZVAL_STR_COPY(&tmp, intern->ce_get_iterator->name);
+       }
+       zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &tmp);
 }
 /* }}} */
 
@@ -1849,18 +1857,22 @@ SPL_METHOD(Array, __unserialize)
 {
        spl_array_object *intern = Z_SPLARRAY_P(ZEND_THIS);
        HashTable *data;
-       zval *flags_zv, *storage_zv, *members_zv;
+       zval *flags_zv, *storage_zv, *members_zv, *iterator_class_zv;
        zend_long flags;
 
        if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "h", &data) == FAILURE) {
                return;
        }
 
-       flags_zv = zend_hash_index_find(data, 0);
-       storage_zv = zend_hash_index_find(data, 1);
-       members_zv = zend_hash_index_find(data, 2);
+       flags_zv          = zend_hash_index_find(data, 0);
+       storage_zv        = zend_hash_index_find(data, 1);
+       members_zv        = zend_hash_index_find(data, 2);
+       iterator_class_zv = zend_hash_index_find(data, 3);
+
        if (!flags_zv || !storage_zv || !members_zv ||
-                       Z_TYPE_P(flags_zv) != IS_LONG || Z_TYPE_P(members_zv) != IS_ARRAY) {
+                       Z_TYPE_P(flags_zv) != IS_LONG || Z_TYPE_P(members_zv) != IS_ARRAY ||
+                       (iterator_class_zv && (Z_TYPE_P(iterator_class_zv) != IS_NULL &&
+                               Z_TYPE_P(iterator_class_zv) != IS_STRING))) {
                zend_throw_exception(spl_ce_UnexpectedValueException,
                        "Incomplete or ill-typed serialization data", 0);
                return;
@@ -1878,6 +1890,24 @@ SPL_METHOD(Array, __unserialize)
        }
 
        object_properties_load(&intern->std, Z_ARRVAL_P(members_zv));
+
+       if (iterator_class_zv && Z_TYPE_P(iterator_class_zv) == IS_STRING) {
+               zend_class_entry *ce = zend_lookup_class(Z_STR_P(iterator_class_zv));
+
+               if (!ce) {
+                       zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
+                               "Cannot deserialize ArrayObject with iterator class '%s'; no such class exists",
+                               ZSTR_VAL(Z_STR_P(iterator_class_zv)));
+                       return;
+               } else if (!instanceof_function(ce, spl_ce_Iterator)) {
+                       zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
+                               "Cannot deserialize ArrayObject with iterator class '%s'; this class does not implement the Iterator interface",
+                               ZSTR_VAL(Z_STR_P(iterator_class_zv)));
+                       return;
+               } else {
+                       intern->ce_get_iterator = ce;
+               }
+       }
 }
 /* }}} */
 
diff --git a/ext/spl/tests/ArrayObject__serialize_saves_iterator_class.phpt b/ext/spl/tests/ArrayObject__serialize_saves_iterator_class.phpt
new file mode 100644 (file)
index 0000000..9947311
--- /dev/null
@@ -0,0 +1,15 @@
+--TEST--
+ArrayObject::__serialize saves any iterator class set by ::setIteratorClass
+--FILE--
+<?php
+
+class MyArrayIterator extends ArrayIterator {}
+$arrayObject = new ArrayObject(array(1, 2, 3));
+$arrayObject->setIteratorClass("MyArrayIterator");
+$serialized = serialize($arrayObject);
+$backAgain  = unserialize($serialized);
+echo $backAgain->getIteratorClass(), "\n";
+
+?>
+--EXPECT--
+MyArrayIterator
index 9a95de60eb48d48cd21d85bec8fad85ad1779434..d64f8f04c1e14e99023955bc42dd455e0aca988d 100644 (file)
@@ -24,7 +24,7 @@ ArrayObject Object
         )
 
 )
-O:11:"ArrayObject":3:{i:0;i:0;i:1;O:11:"ArrayObject":3:{i:0;i:0;i:1;a:2:{i:0;i:1;i:1;i:2;}i:2;a:0:{}}i:2;a:0:{}}
+O:11:"ArrayObject":4:{i:0;i:0;i:1;O:11:"ArrayObject":4:{i:0;i:0;i:1;a:2:{i:0;i:1;i:1;i:2;}i:2;a:0:{}i:3;N;}i:2;a:0:{}i:3;N;}
 ArrayObject Object
 (
     [storage:ArrayObject:private] => ArrayObject Object
index 8187b3a3203aa3f0a967a67fc490aaf4264bb9fa..d4e6653306ffbe06b403d067725088c0aefe1eff 100644 (file)
@@ -16,7 +16,7 @@ $s2 = $o->serialize();
 var_dump($s1);
 var_dump($s2);
 
-$o1 =unserialize($s1);
+$o1 = unserialize($s1);
 
 var_dump($o1[0] === $o1[1]);
 var_dump($o1[2] === $o1);
@@ -69,8 +69,8 @@ var_dump($o2[2][2] === $o2[2]);
 --EXPECT--
 bool(true)
 bool(true)
-string(90) "O:11:"ArrayObject":3:{i:0;i:0;i:1;a:3:{i:0;O:8:"stdClass":0:{}i:1;r:4;i:2;r:1;}i:2;a:0:{}}"
-string(131) "x:i:0;a:3:{i:0;O:8:"stdClass":0:{}i:1;r:3;i:2;O:11:"ArrayObject":3:{i:0;i:0;i:1;a:3:{i:0;r:3;i:1;r:3;i:2;r:5;}i:2;a:0:{}}};m:a:0:{}"
+string(96) "O:11:"ArrayObject":4:{i:0;i:0;i:1;a:3:{i:0;O:8:"stdClass":0:{}i:1;r:4;i:2;r:1;}i:2;a:0:{}i:3;N;}"
+string(137) "x:i:0;a:3:{i:0;O:8:"stdClass":0:{}i:1;r:3;i:2;O:11:"ArrayObject":4:{i:0;i:0;i:1;a:3:{i:0;r:3;i:1;r:3;i:2;r:5;}i:2;a:0:{}i:3;N;}};m:a:0:{}"
 bool(true)
 bool(true)
 bool(true)
@@ -79,8 +79,8 @@ bool(true)
 #### Extending ArrayObject
 bool(true)
 bool(true)
-string(91) "O:12:"ArrayObject2":3:{i:0;i:0;i:1;a:3:{i:0;O:8:"stdClass":0:{}i:1;r:4;i:2;r:1;}i:2;a:0:{}}"
-array(3) {
+string(97) "O:12:"ArrayObject2":4:{i:0;i:0;i:1;a:3:{i:0;O:8:"stdClass":0:{}i:1;r:4;i:2;r:1;}i:2;a:0:{}i:3;N;}"
+array(4) {
   [0]=>
   int(0)
   [1]=>
@@ -100,6 +100,8 @@ array(3) {
   [2]=>
   array(0) {
   }
+  [3]=>
+  NULL
 }
 bool(true)
 bool(true)
index 264cd3b97a3589c78743404f6941fdcbac81e637..6b4ceec65cda0dc4eea7c0dfa1948394c8ea6a6e 100644 (file)
@@ -104,7 +104,7 @@ object(SelfArray)#9 (1) {
   ["foo"]=>
   string(3) "bar"
 }
-string(71) "O:9:"SelfArray":3:{i:0;i:16777216;i:1;N;i:2;a:1:{s:3:"foo";s:3:"bar";}}"
+string(77) "O:9:"SelfArray":4:{i:0;i:16777216;i:1;N;i:2;a:1:{s:3:"foo";s:3:"bar";}i:3;N;}"
 object(SelfArray)#9 (1) {
   ["foo"]=>
   string(3) "bar"
index 237d0673c46be6ab86bac7a9907f5614409dd102..1138b5c8cd542120ad4712393ad2e134657822b5 100644 (file)
@@ -6,6 +6,7 @@ Errors from __unserialize() with invalid data
 echo "ArrayObject:\n";
 
 try {
+    // empty array
     unserialize('O:11:"ArrayObject":0:{}');
 } catch (Exception $e) {
     echo $e->getMessage(), "\n";
@@ -29,6 +30,27 @@ try {
     echo $e->getMessage(), "\n";
 }
 
+try {
+    // iterator class name is not a string
+    unserialize('O:11:"ArrayObject":4:{i:0;i:0;i:1;i:0;i:2;a:0:{}i:3;i:0;}');
+} catch (Exception $e) {
+    echo $e->getMessage(), "\n";
+}
+
+try {
+    unserialize('O:11:"ArrayObject":4:{i:0;i:0;i:1;a:2:{i:0;i:1;i:1;i:2;}i:2;a:0:{}i:3;s:11:"NonExistent";}');
+} catch (Exception $e) {
+    echo $e->getMessage(), "\n";
+}
+
+class Existent {}
+
+try {
+    unserialize('O:11:"ArrayObject":4:{i:0;i:0;i:1;a:2:{i:0;i:1;i:1;i:2;}i:2;a:0:{}i:3;s:8:"Existent";}');
+} catch (Exception $e) {
+    echo $e->getMessage(), "\n";
+}
+
 echo "ArrayIterator:\n";
 
 try {
@@ -114,12 +136,15 @@ try {
 }
 
 ?>
---EXPECTF--
+--EXPECT--
 ArrayObject:
 Incomplete or ill-typed serialization data
 Incomplete or ill-typed serialization data
 Incomplete or ill-typed serialization data
 Passed variable is not an array or object
+Incomplete or ill-typed serialization data
+Cannot deserialize ArrayObject with iterator class 'NonExistent'; no such class exists
+Cannot deserialize ArrayObject with iterator class 'Existent'; this class does not implement the Iterator interface
 ArrayIterator:
 Incomplete or ill-typed serialization data
 Incomplete or ill-typed serialization data
index 12cadfe0fa87fbf519b22515f04bf9362d2b5536..cc71dec4e634d72b89d6fdd6c4691bda158dc383 100644 (file)
@@ -15,7 +15,7 @@ var_dump($y);
 --EXPECTF--
 array(2) {
   [0]=>
-  object(__PHP_Incomplete_Class)#3 (4) {
+  object(__PHP_Incomplete_Class)#3 (5) {
     ["__PHP_Incomplete_Class_Name"]=>
     string(4) "Bar1"
     ["0"]=>
@@ -26,6 +26,8 @@ array(2) {
     ["2"]=>
     array(0) {
     }
+    ["3"]=>
+    NULL
   }
   [1]=>
   object(__PHP_Incomplete_Class)#4 (1) {