Hi Marcus!
>> 1. What should happen when the argument is an object?
>
> Seems like an error message is missing there. It allows to take an instance
> of another ArrayObject/Iterator and use the array from that.
>
> In case any other Object is passed it is ignored. What do you feel?
Sounds alright to me, but it's inconsistent with __construct(), which
in addition to using the storage array from ArrayObject/Iterator
instances, can use the properties from any other kind of object. Also,
just to be clear, I should point out that it is not the current
behaviour. Passing any object to exchangeArray(), even an instance of
ArrayObject/Iterator, eradicates the container:
<?php
$ao1 = new ArrayObject(array('one'));
$ao2 = new ArrayObject(array('two'));
$ao1->exchangeArray($ao2);
var_dump($ao1);
?>
Output on PHP 5.3.0-dev (cli) (built: Jul 28 2008 00:21:59):
object(ArrayObject)#1 (0) {
}
>> Here's a suggested patch against 5_3 that implements this (and
>> includes some tests): http://pastebin.ca/1091668
>> Note that I'm not an internals or SPL expert, so let me know if I
>> missed something.
>
> Seems about right. Can you attach the patch as a text file (I cannot open
> the link).
Attached. I just naively moved some logic from __construct() into a
separate function and called it from both __construct() and
exchangeArray().
Thanks!
Robin
Index: spl_array.c
===================================================================
RCS file: /repository/php-src/ext/spl/spl_array.c,v
retrieving revision 1.71.2.17.2.13.2.19
diff -u -w -p -r1.71.2.17.2.13.2.19 spl_array.c
--- spl_array.c 26 Jul 2008 12:34:10 -0000 1.71.2.17.2.13.2.19
+++ spl_array.c 3 Aug 2008 19:42:50 -0000
@@ -892,6 +892,51 @@ static void spl_array_it_rewind(zend_obj
}
/* }}} */
+/* {{{ spl_array_set_array */
+static void spl_array_set_array(zval *object, spl_array_object *intern, zval
**array, long ar_flags, int just_array TSRMLS_DC) {
+
+ if (Z_TYPE_PP(array) == IS_ARRAY) {
+ SEPARATE_ZVAL_IF_NOT_REF(array);
+ }
+
+ if (Z_TYPE_PP(array) == IS_OBJECT && (Z_OBJ_HT_PP(array) ==
&spl_handler_ArrayObject || Z_OBJ_HT_PP(array) == &spl_handler_ArrayIterator)) {
+ zval_ptr_dtor(&intern->array);
+ if (just_array) {
+ spl_array_object *other =
(spl_array_object*)zend_object_store_get_object(*array TSRMLS_CC);
+ ar_flags = other->ar_flags & ~SPL_ARRAY_INT_MASK;
+ }
+ ar_flags |= SPL_ARRAY_USE_OTHER;
+ intern->array = *array;
+ } else {
+ if (Z_TYPE_PP(array) != IS_OBJECT && Z_TYPE_PP(array) !=
IS_ARRAY) {
+ php_set_error_handling(EH_NORMAL, NULL TSRMLS_CC);
+ zend_throw_exception(spl_ce_InvalidArgumentException,
"Passed variable is not an array or object, using empty array instead", 0
TSRMLS_CC);
+ return;
+ }
+ zval_ptr_dtor(&intern->array);
+ intern->array = *array;
+ }
+ if (object == *array) {
+ intern->ar_flags |= SPL_ARRAY_IS_SELF;
+ intern->ar_flags &= ~SPL_ARRAY_USE_OTHER;
+ } else {
+ intern->ar_flags &= ~SPL_ARRAY_IS_SELF;
+ }
+ intern->ar_flags |= ar_flags;
+ Z_ADDREF_P(intern->array);
+ if (Z_TYPE_PP(array) == IS_OBJECT) {
+ zend_object_get_properties_t handler = Z_OBJ_HANDLER_PP(array,
get_properties);
+ if ((handler != std_object_handlers.get_properties && handler
!= spl_array_get_properties)
+ || !spl_array_get_hash_table(intern, 0 TSRMLS_CC)) {
+ php_set_error_handling(EH_NORMAL, NULL TSRMLS_CC);
+
zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0 TSRMLS_CC,
"Overloaded object of type %s is not compatible with %s",
Z_OBJCE_PP(array)->name, intern->std.ce->name);
+ }
+ }
+
+ spl_array_rewind(intern TSRMLS_CC);
+}
+/* }}} */
+
/* iterator handler table */
zend_object_iterator_funcs spl_array_it_funcs = {
spl_array_it_dtor,
@@ -949,10 +994,6 @@ SPL_METHOD(Array, __construct)
return;
}
- if (Z_TYPE_PP(array) == IS_ARRAY) {
- SEPARATE_ZVAL_IF_NOT_REF(array);
- }
-
if (ZEND_NUM_ARGS() > 2) {
if (zend_lookup_class(class_name, class_name_len,
&pce_get_iterator TSRMLS_CC) == FAILURE) {
zend_throw_exception(spl_ce_InvalidArgumentException,
"A class that implements Iterator must be specified", 0 TSRMLS_CC);
@@ -964,43 +1005,7 @@ SPL_METHOD(Array, __construct)
ar_flags &= ~SPL_ARRAY_INT_MASK;
- if (Z_TYPE_PP(array) == IS_OBJECT && (Z_OBJ_HT_PP(array) ==
&spl_handler_ArrayObject || Z_OBJ_HT_PP(array) == &spl_handler_ArrayIterator)) {
- zval_ptr_dtor(&intern->array);
- if (ZEND_NUM_ARGS() == 1)
- {
- spl_array_object *other =
(spl_array_object*)zend_object_store_get_object(*array TSRMLS_CC);
- ar_flags = other->ar_flags & ~SPL_ARRAY_INT_MASK;
- }
- ar_flags |= SPL_ARRAY_USE_OTHER;
- intern->array = *array;
- } else {
- if (Z_TYPE_PP(array) != IS_OBJECT && Z_TYPE_PP(array) !=
IS_ARRAY) {
- php_set_error_handling(EH_NORMAL, NULL TSRMLS_CC);
- zend_throw_exception(spl_ce_InvalidArgumentException,
"Passed variable is not an array or object, using empty array instead", 0
TSRMLS_CC);
- return;
- }
- zval_ptr_dtor(&intern->array);
- intern->array = *array;
- }
- if (object == *array) {
- intern->ar_flags |= SPL_ARRAY_IS_SELF;
- intern->ar_flags &= ~SPL_ARRAY_USE_OTHER;
- } else {
- intern->ar_flags &= ~SPL_ARRAY_IS_SELF;
- }
- intern->ar_flags |= ar_flags;
- Z_ADDREF_P(intern->array);
- if (Z_TYPE_PP(array) == IS_OBJECT) {
- zend_object_get_properties_t handler = Z_OBJ_HANDLER_PP(array,
get_properties);
- if ((handler != std_object_handlers.get_properties && handler
!= spl_array_get_properties)
- || !spl_array_get_hash_table(intern, 0 TSRMLS_CC)) {
- php_set_error_handling(EH_NORMAL, NULL TSRMLS_CC);
-
zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0 TSRMLS_CC,
"Overloaded object of type %s is not compatible with %s",
Z_OBJCE_PP(array)->name, intern->std.ce->name);
- return;
- }
- }
-
- spl_array_rewind(intern TSRMLS_CC);
+ spl_array_set_array(object, intern, array, ar_flags, ZEND_NUM_ARGS() ==
1 TSRMLS_CC);
php_set_error_handling(EH_NORMAL, NULL TSRMLS_CC);
}
@@ -1081,31 +1086,9 @@ SPL_METHOD(Array, exchangeArray)
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &array) ==
FAILURE) {
return;
}
- if (Z_TYPE_PP(array) == IS_OBJECT && intern ==
(spl_array_object*)zend_object_store_get_object(object TSRMLS_CC)) {
- zval_ptr_dtor(&intern->array);
- array = &object;
- intern->array = object;
- } else if (Z_TYPE_PP(array) == IS_OBJECT && (Z_OBJ_HT_PP(array) ==
&spl_handler_ArrayObject || Z_OBJ_HT_PP(array) == &spl_handler_ArrayIterator)) {
- spl_array_object *other =
(spl_array_object*)zend_object_store_get_object(*array TSRMLS_CC);
- zval_ptr_dtor(&intern->array);
- intern->array = other->array;
- } else {
- if (Z_TYPE_PP(array) != IS_OBJECT && !HASH_OF(*array)) {
- zend_throw_exception(spl_ce_InvalidArgumentException,
"Passed variable is not an array or object, using empty array instead", 0
TSRMLS_CC);
- return;
- }
- zval_ptr_dtor(&intern->array);
- intern->array = *array;
- intern->ar_flags &= ~SPL_ARRAY_USE_OTHER;
- }
- if (object == *array) {
- intern->ar_flags |= SPL_ARRAY_IS_SELF;
- } else {
- intern->ar_flags &= ~SPL_ARRAY_IS_SELF;
- }
- Z_ADDREF_P(intern->array);
- spl_array_rewind(intern TSRMLS_CC);
+ spl_array_set_array(object, intern, array, 0L, 1 TSRMLS_CC);
+
}
/* }}} */
Index: tests/arrayObject_exchange_basic1.phpt
===================================================================
RCS file: tests/arrayObject_exchange_basic1.phpt
diff -N tests/arrayObject_exchange_basic1.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ tests/arrayObject_exchange_basic1.phpt 3 Aug 2008 09:44:39 -0000
@@ -0,0 +1,40 @@
+--TEST--
+ArrayObject::exchange() and copy-on-write references
+--FILE--
+<?php
+$ao = new ArrayObject();
+$swapIn = array();
+$cowRef = $swapIn; // create a copy-on-write ref to $swapIn
+$ao->exchangeArray($swapIn);
+
+$ao['a'] = 'adding element to $ao';
+$swapIn['b'] = 'adding element to $swapIn';
+$ao['c'] = 'adding another element to $ao';
+
+echo "\n--> swapIn: ";
+var_dump($swapIn);
+
+echo "\n--> cowRef: ";
+var_dump($cowRef);
+
+echo "\n--> ao: ";
+var_dump($ao);
+?>
+--EXPECTF--
+--> swapIn: array(1) {
+ ["b"]=>
+ string(25) "adding element to $swapIn"
+}
+
+--> cowRef: array(0) {
+}
+
+--> ao: object(ArrayObject)#%d (1) {
+ ["storage":"ArrayObject":private]=>
+ array(2) {
+ ["a"]=>
+ string(21) "adding element to $ao"
+ ["c"]=>
+ string(29) "adding another element to $ao"
+ }
+}
Index: tests/arrayObject_exchange_basic2.phpt
===================================================================
RCS file: tests/arrayObject_exchange_basic2.phpt
diff -N tests/arrayObject_exchange_basic2.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ tests/arrayObject_exchange_basic2.phpt 3 Aug 2008 09:48:20 -0000
@@ -0,0 +1,33 @@
+--TEST--
+ArrayObject::exchangeArray(object)
+--FILE--
+<?php
+echo "--> exchangeArray(array):\n";
+$ao = new ArrayObject();
+$ao->exchangeArray(array('original'));
+var_dump($ao);
+
+echo "\n--> exchangeArray(object):\n";
+$obj = new stdClass;
+$obj->newProp = 'changed';
+$ao->exchangeArray($obj);
+var_dump($ao);
+?>
+--EXPECTF--
+--> exchangeArray(array):
+object(ArrayObject)#%d (1) {
+ ["storage":"ArrayObject":private]=>
+ array(1) {
+ [0]=>
+ string(8) "original"
+ }
+}
+
+--> exchangeArray(object):
+object(ArrayObject)#%d (1) {
+ ["storage":"ArrayObject":private]=>
+ object(stdClass)#%d (1) {
+ ["newProp"]=>
+ string(7) "changed"
+ }
+}
\ No newline at end of file
--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php