diff --git a/ext/standard/array.c b/ext/standard/array.c index aa89e3755b123..4f45855ee9a45 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -103,6 +103,7 @@ PHP_MINIT_FUNCTION(array) /* {{{ */ REGISTER_LONG_CONSTANT("SORT_REGULAR", PHP_SORT_REGULAR, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("SORT_NUMERIC", PHP_SORT_NUMERIC, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SORT_STRICT", PHP_SORT_STRICT, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("SORT_STRING", PHP_SORT_STRING, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("SORT_LOCALE_STRING", PHP_SORT_LOCALE_STRING, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("SORT_NATURAL", PHP_SORT_NATURAL, CONST_CS | CONST_PERSISTENT); @@ -349,6 +350,104 @@ static zend_always_inline int php_array_data_compare_unstable_i(Bucket *f, Bucke } /* }}} */ +/* return int to be compatible with compare_func_t */ +static int hash_zval_strict_function(zval *z1, zval *z2) /* {{{ */ +{ + ZVAL_DEREF(z1); + ZVAL_DEREF(z2); + // If the types are different, compare based on type. + // (Values of different types can't be identical.) + int t1 = Z_TYPE_P(z1); + int t2 = Z_TYPE_P(z2); + if ( t1 != t2 ) { + return (t1 > t2 ) ? 1 : -1; + } + // The most important thing about this comparison mode is that the result + // is 0 when zend_is_identical, and non-zero otherwise. This test is + // done first to make it easier to verify this property by inspection. + // (Arrays are excluded as an optimization, to avoid a redudant + // deep inspection.) + if (t1 != IS_ARRAY && zend_is_identical(z1, z2)) { + return 0; + } + // Both types are the same *but the values are not identical* + // Below this point, the return value for non-array values + // should always be non-zero. + switch (t1) { + case IS_LONG: + return Z_LVAL_P(z1) > Z_LVAL_P(z2) ? 1 : -1; + + case IS_DOUBLE: + return Z_DVAL_P(z1) > Z_DVAL_P(z2) ? 1 : -1; + + case IS_STRING: + return zend_binary_strcmp( + Z_STRVAL_P(z1), Z_STRLEN_P(z1), + Z_STRVAL_P(z2), Z_STRLEN_P(z2) + ); + + case IS_ARRAY: + // Do a recursive comparison. This is consistent with the test + // for arrays in zend_is_identical, but unlike that method it + // provides a meaningful ordering in the case of non-identity + // as well. + return zend_hash_compare( + Z_ARRVAL_P(z1), Z_ARRVAL_P(z2), + (compare_func_t) hash_zval_strict_function, 1 /* ordered */ + ); + + case IS_OBJECT: + { + // Start with a recursive comparison like for arrays, for consistency. + // (This is deliberately not using the user-defined `compare` handler, + // nor is it using zend_std_compare_objects() because that uses + // zend_compare when examining properties, not a strict comparison.) + zend_object *zobj1 = Z_OBJ_P(z1); + zend_object *zobj2 = Z_OBJ_P(z2); + rebuild_object_properties(zobj1); + rebuild_object_properties(zobj2); + // zend_std_compare_objects() uses unordered comparison, but that + // leads to a unpredictable sort: with unordered the properties are + // compared in the order they appear in the *first* object so + // `compare(a,b)` is not guaranteed to be the same as `-compare(b,a)`. + int c = zend_hash_compare( + zobj1->properties, zobj2->properties, + (compare_func_t) hash_zval_strict_function, 1 /* ordered */ + ); + if (c != 0) { + return (c > 0) ? 1 : -1; + } + // Fall back on spl_object_id() value, which will probably vary + // non-deterministically between runs (alas). + ZEND_ASSERT(zobj1->handle != zobj2->handle); + return (zobj1->handle > zobj2->handle) ? 1 : -1; + } + + case IS_RESOURCE: + // This will also likely vary non-deterministically between runs. + return Z_RES_HANDLE_P(z1) > Z_RES_HANDLE_P(z2) ? 1 : -1; + + case IS_REFERENCE: + ZEND_ASSERT(0 && "Should have been dereferenced above"); + + case IS_UNDEF: + case IS_NULL: + case IS_FALSE: + case IS_TRUE: + default: + ZEND_ASSERT(0 && "Values w/ same type should be identical"); + return 0; + } +} +/* }}} */ + + +static zend_always_inline int php_array_data_compare_strict_unstable_i(Bucket *f, Bucket *s) /* {{{ */ +{ + return hash_zval_strict_function(&f->val, &s->val); +} +/* }}} */ + static zend_always_inline int php_array_data_compare_numeric_unstable_i(Bucket *f, Bucket *s) /* {{{ */ { return numeric_compare_function(&f->val, &s->val); @@ -405,6 +504,7 @@ DEFINE_SORT_VARIANTS(key_compare_string_case); DEFINE_SORT_VARIANTS(key_compare_string); DEFINE_SORT_VARIANTS(key_compare_string_locale); DEFINE_SORT_VARIANTS(data_compare); +DEFINE_SORT_VARIANTS(data_compare_strict); DEFINE_SORT_VARIANTS(data_compare_numeric); DEFINE_SORT_VARIANTS(data_compare_string_case); DEFINE_SORT_VARIANTS(data_compare_string); @@ -527,6 +627,14 @@ static bucket_compare_func_t php_get_data_compare_func(zend_long sort_type, int } break; + case PHP_SORT_STRICT: + if (reverse) { + return php_array_reverse_data_compare_strict; + } else { + return php_array_data_compare_strict; + } + break; + case PHP_SORT_REGULAR: default: if (reverse) { @@ -591,6 +699,14 @@ static bucket_compare_func_t php_get_data_compare_func_unstable(zend_long sort_t } break; + case PHP_SORT_STRICT: + if (reverse) { + return php_array_reverse_data_compare_strict_unstable; + } else { + return php_array_data_compare_strict_unstable; + } + break; + case PHP_SORT_REGULAR: default: if (reverse) { diff --git a/ext/standard/php_array.h b/ext/standard/php_array.h index c11f769205994..72e81413505f8 100644 --- a/ext/standard/php_array.h +++ b/ext/standard/php_array.h @@ -36,6 +36,7 @@ PHPAPI zend_long php_count_recursive(HashTable *ht); #define PHP_SORT_ASC 4 #define PHP_SORT_LOCALE_STRING 5 #define PHP_SORT_NATURAL 6 +#define PHP_SORT_STRICT 7 #define PHP_SORT_FLAG_CASE 8 #define COUNT_NORMAL 0 diff --git a/ext/standard/tests/array/array_unique_strict.phpt b/ext/standard/tests/array/array_unique_strict.phpt new file mode 100644 index 0000000000000..c63c112d727f1 --- /dev/null +++ b/ext/standard/tests/array/array_unique_strict.phpt @@ -0,0 +1,67 @@ +--TEST-- +Test array_unique() function : SORT_STRICT functionality +--FILE-- + $a ]; +$b3 = (object)[ 'foo' => $b ]; +var_dump( $a == $b && $a2 == $b2 && $a3 == $b3 ); +var_dump( $a === $b || $a2 === $b2 || $a3 === $b3 ); + +var_dump( count( array_unique( [ $a, $b, $a2, $b2, $a3, $b3 ], SORT_STRICT ) ) ); + +?> +--EXPECT-- +*** Testing array_unique() : SORT_STRICT functionality *** +array(1) { + [0]=> + string(4) "1234" +} +array(1) { + [0]=> + string(4) "1234" +} +array(2) { + [0]=> + string(4) "1234" + [1]=> + int(1234) +} +array(3) { + [0]=> + int(0) + [3]=> + string(3) "0.0" + [4]=> + string(0) "" +} +array(6) { + [0]=> + int(0) + [1]=> + string(1) "0" + [2]=> + float(0) + [3]=> + string(3) "0.0" + [4]=> + string(0) "" + [5]=> + NULL +} +bool(true) +bool(false) +int(6) diff --git a/ext/standard/tests/array/arsort_basic.phpt b/ext/standard/tests/array/arsort_basic.phpt index c636a6278457c..965920fabcd25 100644 --- a/ext/standard/tests/array/arsort_basic.phpt +++ b/ext/standard/tests/array/arsort_basic.phpt @@ -9,6 +9,7 @@ Test arsort() function : basic functionality * SORT_REGULAR - compare items normally * SORT_NUMERIC - compare items numerically * SORT_STRING - compare items as strings + * SORT_STRICT - compare items using strict equality (===) */ echo "*** Testing arsort() : basic functionality ***\n"; @@ -67,6 +68,16 @@ $temp_array = $unsorted_numerics; var_dump( arsort($temp_array, SORT_NUMERIC) ); // expecting : bool(true) var_dump( $temp_array); +echo "\n-- Testing arsort() by supplying string array, 'flag' = SORT_STRICT --\n"; +$temp_array = $unsorted_strings; +var_dump( arsort($temp_array, SORT_STRICT) ); // expecting : bool(true) +var_dump( $temp_array); + +echo "\n-- Testing arsort() by supplying numeric array, 'flag' = SORT_STRICT --\n"; +$temp_array = $unsorted_numerics; +var_dump( arsort($temp_array, SORT_STRICT) ); // expecting : bool(true) +var_dump( $temp_array); + echo "Done\n"; ?> --EXPECT-- @@ -236,4 +247,38 @@ array(4) { [4]=> int(22) } + +-- Testing arsort() by supplying string array, 'flag' = SORT_STRICT -- +bool(true) +array(8) { + ["o20"]=> + string(8) "orange20" + ["o2"]=> + string(7) "orange2" + ["o"]=> + string(6) "orange" + ["l"]=> + string(5) "lemon" + ["b"]=> + string(6) "banana" + ["O3"]=> + string(7) "Orange3" + ["O1"]=> + string(7) "Orange1" + ["O"]=> + string(6) "Orange" +} + +-- Testing arsort() by supplying numeric array, 'flag' = SORT_STRICT -- +bool(true) +array(4) { + [3]=> + int(555) + [1]=> + int(100) + [2]=> + int(33) + [4]=> + int(22) +} Done diff --git a/ext/standard/tests/array/arsort_strict.phpt b/ext/standard/tests/array/arsort_strict.phpt new file mode 100644 index 0000000000000..1023ed90f8eaf --- /dev/null +++ b/ext/standard/tests/array/arsort_strict.phpt @@ -0,0 +1,241 @@ +--TEST-- +Test arsort() function : SORT_STRICT flag +--FILE-- + 'bar' ]; +$arr2 = [ 'foo' => 'bar' ]; // Same contents as $arr1 +$arr3 = [ 'foo' => 'bat' ]; // Different value +$arr4 = [ 'bar' => 'bar' ]; // Different key + +echo "\n"; +echo "\$arr1 and \$arr2 are value objects which are indistiguishable in PHP\n"; +var_dump($arr1 === $arr2); // expect : bool(true) + +// Object-type equivalents of the array cases +$obj1 = (object)$arr1; +$obj2 = (object)$arr2; # Unlike $arr1/$arr2, $obj1 !== $obj2 +$obj3 = (object)$arr3; +$obj4 = (object)$arr4; + +echo "Unlike \$arr1/\$arr2, object values are distinguishable.\n"; +var_dump($obj1 === $obj2); // expect : bool(false) + +// The object IDs of $obj1 and $obj2 may vary non-deterministically from +// run to run. But in order to ensure deterministic results from this test, +// we're going to check spl_object_id() and swap $obj1 and $obj2 so that +// $obj1 is guaranteed to have the lower spl_object_id(). +if (spl_object_id($obj1) > spl_object_id($obj2)) { + $tmp = $obj1; + $obj1 = $obj2; + $obj2 = $tmp; +} + +// Arrays containing values which are == but not === +$arr5 = [ $arr1 ]; +$arr6 = [ $arr2 ]; +$arr7 = [ $obj1 ]; +$arr8 = [ $obj2 ]; + +echo "Arrays with indistinguishable objects are themselves indistinguishable.\n"; +var_dump($arr5 === $arr6); // expect : bool(true) +echo "But arrays with distinguishable arrays are distinguishable.\n"; +var_dump($arr7 === $arr8); // expect : bool(false) + +// Object-type equivalents. +$obj5 = (object)[ 'p' => $arr1 ]; +$obj6 = (object)[ 'p' => $arr2 ]; +$obj7 = (object)[ 'p' => $obj1 ]; +$obj8 = (object)[ 'p' => $obj2 ]; + +echo "These are all distinguishable:\n"; +var_dump($obj5 === $obj6); // expect : bool(false) +var_dump($obj7 === $obj8); // expect : bool(false) + +echo "\n"; +echo "Now, let's sort these!\n"; +$pi = 3.141592654; +$temp_array = [ + // We expect these non-array values to be sorted to the front. + 'bool(true)' => true, + 'null' => null, + 'bool(false)' => false, + 'int(0)' => 0, + 'string(a)' => "a", + 'int(1)' => 1, + 'string(b)' => "b", + 'float' => &$pi, + 'callable' => fn($x) => $x + 1, + // We expect all object type values to be first in the sorted result + 'obj1' => $obj1, 'obj2' => $obj2, 'obj3' => $obj3, 'obj4' => $obj4, + 'obj5' => $obj5, 'obj6' => $obj6, 'obj7' => $obj7, 'obj8' => $obj8, + // We expect all array type values to follow them + 'arr1' => $arr1, 'arr2' => $arr2, 'arr3' => $arr3, 'arr4' => $arr4, + 'arr5' => $arr5, 'arr6' => $arr6, 'arr7' => $arr7, 'arr8' => $arr8, +]; +var_dump( arsort($temp_array, SORT_STRICT) ); // expect : bool(true) +// Usually, we'd expect the order of $obj1/$obj2, $arr7/$arr8, $obj5/$obj6 +// and $obj7/$obj8 to vary because they depend on object_id. But we've +// arranged for $arr1/$obj1 to have the lower ID so we can test positions +// precisely. +// (Every other element, except for these six pairs, would always have +// a deterministic position; when PHP8 made the sort stable this +// ensured $arr1/$arr2 and $arr5/$arr6 would have a deterministic order.) +var_dump( $temp_array ); + +echo "Done\n"; +?> +--EXPECTF-- +*** Testing arsort() : SORT_STRICT flag *** + +$arr1 and $arr2 are value objects which are indistiguishable in PHP +bool(true) +Unlike $arr1/$arr2, object values are distinguishable. +bool(false) +Arrays with indistinguishable objects are themselves indistinguishable. +bool(true) +But arrays with distinguishable arrays are distinguishable. +bool(false) +These are all distinguishable: +bool(false) +bool(false) + +Now, let's sort these! +bool(true) +array(25) { + ["obj3"]=> + object(stdClass)#%d (1) { + ["foo"]=> + string(3) "bat" + } + ["obj2"]=> + object(stdClass)#%d (1) { + ["foo"]=> + string(3) "bar" + } + ["obj1"]=> + object(stdClass)#%d (1) { + ["foo"]=> + string(3) "bar" + } + ["obj4"]=> + object(stdClass)#%d (1) { + ["bar"]=> + string(3) "bar" + } + ["obj8"]=> + object(stdClass)#%d (1) { + ["p"]=> + object(stdClass)#%d (1) { + ["foo"]=> + string(3) "bar" + } + } + ["obj7"]=> + object(stdClass)#%d (1) { + ["p"]=> + object(stdClass)#%d (1) { + ["foo"]=> + string(3) "bar" + } + } + ["obj6"]=> + object(stdClass)#%d (1) { + ["p"]=> + array(1) { + ["foo"]=> + string(3) "bar" + } + } + ["obj5"]=> + object(stdClass)#%d (1) { + ["p"]=> + array(1) { + ["foo"]=> + string(3) "bar" + } + } + ["callable"]=> + object(Closure)#%d (1) { + ["parameter"]=> + array(1) { + ["$x"]=> + string(10) "" + } + } + ["arr3"]=> + array(1) { + ["foo"]=> + string(3) "bat" + } + ["arr1"]=> + array(1) { + ["foo"]=> + string(3) "bar" + } + ["arr2"]=> + array(1) { + ["foo"]=> + string(3) "bar" + } + ["arr4"]=> + array(1) { + ["bar"]=> + string(3) "bar" + } + ["arr8"]=> + array(1) { + [0]=> + object(stdClass)#%d (1) { + ["foo"]=> + string(3) "bar" + } + } + ["arr7"]=> + array(1) { + [0]=> + object(stdClass)#%d (1) { + ["foo"]=> + string(3) "bar" + } + } + ["arr5"]=> + array(1) { + [0]=> + array(1) { + ["foo"]=> + string(3) "bar" + } + } + ["arr6"]=> + array(1) { + [0]=> + array(1) { + ["foo"]=> + string(3) "bar" + } + } + ["string(b)"]=> + string(1) "b" + ["string(a)"]=> + string(1) "a" + ["float"]=> + &float(3.141592654) + ["int(1)"]=> + int(1) + ["int(0)"]=> + int(0) + ["bool(true)"]=> + bool(true) + ["bool(false)"]=> + bool(false) + ["null"]=> + NULL +} +Done diff --git a/ext/standard/tests/array/asort_basic.phpt b/ext/standard/tests/array/asort_basic.phpt index 13f03a92ef6e0..7ad444fb5a482 100644 --- a/ext/standard/tests/array/asort_basic.phpt +++ b/ext/standard/tests/array/asort_basic.phpt @@ -9,6 +9,7 @@ Test asort() function : basic functionality * SORT_REGULAR - compare items normally * SORT_NUMERIC - compare items numerically * SORT_STRING - compare items as strings + * SORT_STRICT - compare items using strict equality (===) */ echo "*** Testing asort() : basic functionality ***\n"; @@ -67,6 +68,16 @@ $temp_array = $unsorted_numerics; var_dump( asort($temp_array, SORT_NUMERIC) ); // expecting : bool(true) var_dump( $temp_array); +echo "\n-- Testing asort() by supplying string array, 'flag' = SORT_STRICT --\n"; +$temp_array = $unsorted_strings; +var_dump( asort($temp_array, SORT_STRICT) ); // expecting : bool(true) +var_dump( $temp_array); + +echo "\n-- Testing asort() by supplying numeric array, 'flag' = SORT_STRICT --\n"; +$temp_array = $unsorted_numerics; +var_dump( asort($temp_array, SORT_STRICT) ); // expecting : bool(true) +var_dump( $temp_array); + echo "Done\n"; ?> --EXPECT-- @@ -236,4 +247,38 @@ array(4) { [3]=> int(555) } + +-- Testing asort() by supplying string array, 'flag' = SORT_STRICT -- +bool(true) +array(8) { + ["O"]=> + string(6) "Orange" + ["O1"]=> + string(7) "Orange1" + ["O3"]=> + string(7) "Orange3" + ["b"]=> + string(6) "banana" + ["l"]=> + string(5) "lemon" + ["o"]=> + string(6) "orange" + ["o2"]=> + string(7) "orange2" + ["o20"]=> + string(8) "orange20" +} + +-- Testing asort() by supplying numeric array, 'flag' = SORT_STRICT -- +bool(true) +array(4) { + [4]=> + int(22) + [2]=> + int(33) + [1]=> + int(100) + [3]=> + int(555) +} Done diff --git a/ext/standard/tests/array/asort_strict.phpt b/ext/standard/tests/array/asort_strict.phpt new file mode 100644 index 0000000000000..a959be63ca5c2 --- /dev/null +++ b/ext/standard/tests/array/asort_strict.phpt @@ -0,0 +1,241 @@ +--TEST-- +Test asort() function : SORT_STRICT flag +--FILE-- + 'bar' ]; +$arr2 = [ 'foo' => 'bar' ]; // Same contents as $arr1 +$arr3 = [ 'foo' => 'bat' ]; // Different value +$arr4 = [ 'bar' => 'bar' ]; // Different key + +echo "\n"; +echo "\$arr1 and \$arr2 are value objects which are indistiguishable in PHP\n"; +var_dump($arr1 === $arr2); // expect : bool(true) + +// Object-type equivalents of the array cases +$obj1 = (object)$arr1; +$obj2 = (object)$arr2; # Unlike $arr1/$arr2, $obj1 !== $obj2 +$obj3 = (object)$arr3; +$obj4 = (object)$arr4; + +echo "Unlike \$arr1/\$arr2, object values are distinguishable.\n"; +var_dump($obj1 === $obj2); // expect : bool(false) + +// The object IDs of $obj1 and $obj2 may vary non-deterministically from +// run to run. But in order to ensure deterministic results from this test, +// we're going to check spl_object_id() and swap $obj1 and $obj2 so that +// $obj1 is guaranteed to have the lower spl_object_id(). +if (spl_object_id($obj1) > spl_object_id($obj2)) { + $tmp = $obj1; + $obj1 = $obj2; + $obj2 = $tmp; +} + +// Arrays containing values which are == but not === +$arr5 = [ $arr1 ]; +$arr6 = [ $arr2 ]; +$arr7 = [ $obj1 ]; +$arr8 = [ $obj2 ]; + +echo "Arrays with indistinguishable objects are themselves indistinguishable.\n"; +var_dump($arr5 === $arr6); // expect : bool(true) +echo "But arrays with distinguishable arrays are distinguishable.\n"; +var_dump($arr7 === $arr8); // expect : bool(false) + +// Object-type equivalents. +$obj5 = (object)[ 'p' => $arr1 ]; +$obj6 = (object)[ 'p' => $arr2 ]; +$obj7 = (object)[ 'p' => $obj1 ]; +$obj8 = (object)[ 'p' => $obj2 ]; + +echo "These are all distinguishable:\n"; +var_dump($obj5 === $obj6); // expect : bool(false) +var_dump($obj7 === $obj8); // expect : bool(false) + +echo "\n"; +echo "Now, let's sort these!\n"; +$pi = 3.141592654; +$temp_array = [ + // We expect all object type values to be last in the sorted result + 'obj1' => $obj1, 'obj2' => $obj2, 'obj3' => $obj3, 'obj4' => $obj4, + 'obj5' => $obj5, 'obj6' => $obj6, 'obj7' => $obj7, 'obj8' => $obj8, + // We expect all array type values to precede them + 'arr1' => $arr1, 'arr2' => $arr2, 'arr3' => $arr3, 'arr4' => $arr4, + 'arr5' => $arr5, 'arr6' => $arr6, 'arr7' => $arr7, 'arr8' => $arr8, + // We expect these non-array values to be sorted to the front. + 'bool(true)' => true, + 'null' => null, + 'bool(false)' => false, + 'int(0)' => 0, + 'string(a)' => "a", + 'int(1)' => 1, + 'string(b)' => "b", + 'float' => &$pi, + 'callable' => fn($x) => $x + 1, +]; +var_dump( asort($temp_array, SORT_STRICT) ); // expect : bool(true) +// Usually, we'd expect the order of $obj1/$obj2, $arr7/$arr8, $obj5/$obj6 +// and $obj7/$obj8 to vary because they depend on object_id. But we've +// arranged for $arr1/$obj1 to have the lower ID so we can test positions +// precisely. +// (Every other element, except for these six pairs, would always have +// a deterministic position; when PHP8 made the sort stable this +// ensured $arr1/$arr2 and $arr5/$arr6 would have a deterministic order.) +var_dump( $temp_array ); + +echo "Done\n"; +?> +--EXPECTF-- +*** Testing asort() : SORT_STRICT flag *** + +$arr1 and $arr2 are value objects which are indistiguishable in PHP +bool(true) +Unlike $arr1/$arr2, object values are distinguishable. +bool(false) +Arrays with indistinguishable objects are themselves indistinguishable. +bool(true) +But arrays with distinguishable arrays are distinguishable. +bool(false) +These are all distinguishable: +bool(false) +bool(false) + +Now, let's sort these! +bool(true) +array(25) { + ["null"]=> + NULL + ["bool(false)"]=> + bool(false) + ["bool(true)"]=> + bool(true) + ["int(0)"]=> + int(0) + ["int(1)"]=> + int(1) + ["float"]=> + &float(3.141592654) + ["string(a)"]=> + string(1) "a" + ["string(b)"]=> + string(1) "b" + ["arr5"]=> + array(1) { + [0]=> + array(1) { + ["foo"]=> + string(3) "bar" + } + } + ["arr6"]=> + array(1) { + [0]=> + array(1) { + ["foo"]=> + string(3) "bar" + } + } + ["arr7"]=> + array(1) { + [0]=> + object(stdClass)#%d (1) { + ["foo"]=> + string(3) "bar" + } + } + ["arr8"]=> + array(1) { + [0]=> + object(stdClass)#%d (1) { + ["foo"]=> + string(3) "bar" + } + } + ["arr4"]=> + array(1) { + ["bar"]=> + string(3) "bar" + } + ["arr1"]=> + array(1) { + ["foo"]=> + string(3) "bar" + } + ["arr2"]=> + array(1) { + ["foo"]=> + string(3) "bar" + } + ["arr3"]=> + array(1) { + ["foo"]=> + string(3) "bat" + } + ["callable"]=> + object(Closure)#%d (1) { + ["parameter"]=> + array(1) { + ["$x"]=> + string(10) "" + } + } + ["obj5"]=> + object(stdClass)#%d (1) { + ["p"]=> + array(1) { + ["foo"]=> + string(3) "bar" + } + } + ["obj6"]=> + object(stdClass)#%d (1) { + ["p"]=> + array(1) { + ["foo"]=> + string(3) "bar" + } + } + ["obj7"]=> + object(stdClass)#%d (1) { + ["p"]=> + object(stdClass)#%d (1) { + ["foo"]=> + string(3) "bar" + } + } + ["obj8"]=> + object(stdClass)#%d (1) { + ["p"]=> + object(stdClass)#%d (1) { + ["foo"]=> + string(3) "bar" + } + } + ["obj4"]=> + object(stdClass)#%d (1) { + ["bar"]=> + string(3) "bar" + } + ["obj1"]=> + object(stdClass)#%d (1) { + ["foo"]=> + string(3) "bar" + } + ["obj2"]=> + object(stdClass)#%d (1) { + ["foo"]=> + string(3) "bar" + } + ["obj3"]=> + object(stdClass)#%d (1) { + ["foo"]=> + string(3) "bat" + } +} +Done