helly           Mon Feb  6 00:58:11 2006 UTC

  Added files:                 
    /php-src/ext/spl/tests      array_019.phpt array_020.phpt 

  Modified files:              
    /php-src/ext/spl    spl_array.c 
  Log:
  - Synch c-level iterator and iterator methods 
  - Fix iterator checks
  # A nice discussion with Andrei made me remember this issue
  
  
http://cvs.php.net/viewcvs.cgi/php-src/ext/spl/spl_array.c?r1=1.91&r2=1.92&diff_format=u
Index: php-src/ext/spl/spl_array.c
diff -u php-src/ext/spl/spl_array.c:1.91 php-src/ext/spl/spl_array.c:1.92
--- php-src/ext/spl/spl_array.c:1.91    Sun Feb  5 23:31:47 2006
+++ php-src/ext/spl/spl_array.c Mon Feb  6 00:58:11 2006
@@ -16,7 +16,7 @@
    +----------------------------------------------------------------------+
  */
 
-/* $Id: spl_array.c,v 1.91 2006/02/05 23:31:47 helly Exp $ */
+/* $Id: spl_array.c,v 1.92 2006/02/06 00:58:11 helly Exp $ */
 
 #ifdef HAVE_CONFIG_H
 # include "config.h"
@@ -46,12 +46,18 @@
 
 #define SPL_ARRAY_STD_PROP_LIST      0x00000001
 #define SPL_ARRAY_ARRAY_AS_PROPS     0x00000002
+#define SPL_ARRAY_OVERLOADED_REWIND  0x00010000
+#define SPL_ARRAY_OVERLOADED_VALID   0x00020000
+#define SPL_ARRAY_OVERLOADED_KEY     0x00040000
+#define SPL_ARRAY_OVERLOADED_CURRENT 0x00080000
+#define SPL_ARRAY_OVERLOADED_NEXT    0x00100000
 #define SPL_ARRAY_IS_REF             0x01000000
 #define SPL_ARRAY_IS_SELF            0x02000000
 #define SPL_ARRAY_USE_OTHER          0x04000000
-#define SPL_ARRAY_INT_MASK           0xFF000000
+#define SPL_ARRAY_INT_MASK           0xFFFF0000
 #define SPL_ARRAY_CLONE_MASK         0x03000007
 
+
 typedef struct _spl_array_object {
        zend_object       std;
        zval              *array;
@@ -148,7 +154,7 @@
 
        retval.handle = zend_objects_store_put(intern, 
(zend_objects_store_dtor_t)zend_objects_destroy_object, 
(zend_objects_free_object_storage_t) spl_array_object_free_storage, NULL 
TSRMLS_CC);
        while (parent) {
-               if (parent == U_CLASS_ENTRY(spl_ce_ArrayIterator)) {
+               if (parent == U_CLASS_ENTRY(spl_ce_ArrayIterator) || parent == 
U_CLASS_ENTRY(spl_ce_RecursiveArrayIterator)) {
                        retval.handlers = &spl_handler_ArrayIterator;
                        break;
                } else if (parent == U_CLASS_ENTRY(spl_ce_ArrayObject)) {
@@ -159,7 +165,7 @@
                inherited = 1;
        }
        if (!parent) { /* this must never happen */
-               php_error_docref(NULL TSRMLS_CC, E_COMPILE_ERROR, "Internal 
compiler error, Class is not child of ArrayObject or arrayIterator");
+               php_error_docref(NULL TSRMLS_CC, E_COMPILE_ERROR, "Internal 
compiler error, Class is not child of ArrayObject or ArrayIterator");
        }
        if (inherited) {
                zend_hash_find(&class_type->function_table, "offsetget",    
sizeof("offsetget"),    (void **) &intern->fptr_offset_get);
@@ -179,6 +185,25 @@
                        intern->fptr_offset_del = NULL;
                }
        }
+       /* Cache iterator functions if ArrayIterator or derived. Check 
current's */
+       /* cache since only current is always required */
+       if (retval.handlers == &spl_handler_ArrayIterator) {
+               if (!class_type->iterator_funcs.zf_current) {
+                       zend_hash_find(&class_type->function_table, "rewind",  
sizeof("rewind"),  (void **) &class_type->iterator_funcs.zf_rewind);
+                       zend_hash_find(&class_type->function_table, "valid",   
sizeof("valid"),   (void **) &class_type->iterator_funcs.zf_valid);
+                       zend_hash_find(&class_type->function_table, "key",     
sizeof("key"),     (void **) &class_type->iterator_funcs.zf_key);
+                       zend_hash_find(&class_type->function_table, "current", 
sizeof("current"), (void **) &class_type->iterator_funcs.zf_current);
+                       zend_hash_find(&class_type->function_table, "next",    
sizeof("next"),    (void **) &class_type->iterator_funcs.zf_next);
+               }
+               if (inherited) {
+                       if (class_type->iterator_funcs.zf_rewind->common.scope  
!= parent) intern->ar_flags |= SPL_ARRAY_OVERLOADED_REWIND;
+                       if (class_type->iterator_funcs.zf_valid->common.scope   
!= parent) intern->ar_flags |= SPL_ARRAY_OVERLOADED_CURRENT;
+                       if (class_type->iterator_funcs.zf_key->common.scope     
!= parent) intern->ar_flags |= SPL_ARRAY_OVERLOADED_KEY;
+                       if (class_type->iterator_funcs.zf_current->common.scope 
!= parent) intern->ar_flags |= SPL_ARRAY_OVERLOADED_CURRENT;
+                       if (class_type->iterator_funcs.zf_next->common.scope    
!= parent) intern->ar_flags |= SPL_ARRAY_OVERLOADED_NEXT;
+               }
+       }
+
        intern->ce_get_iterator = U_CLASS_ENTRY(spl_ce_ArrayIterator);
        zend_hash_internal_pointer_reset_ex(spl_array_get_hash_table(intern, 0 
TSRMLS_CC), &intern->pos);
        return retval;
@@ -658,16 +683,20 @@
        spl_array_object   *object   = iterator->object;
        HashTable          *aht      = spl_array_get_hash_table(object, 0 
TSRMLS_CC);
 
-       if (!aht) {
-               php_error_docref(NULL TSRMLS_CC, E_NOTICE, 
"ArrayIterator::valid(): Array was modified outside object and is no longer an 
array");
-               return FAILURE;
-       }
-
-       if (object->pos && (object->ar_flags & SPL_ARRAY_IS_REF) && 
spl_hash_verify_pos(object TSRMLS_CC) == FAILURE) {
-               php_error_docref(NULL TSRMLS_CC, E_NOTICE, 
"ArrayIterator::valid(): Array was modified outside object and internal 
position is no longer valid");
-               return FAILURE;
+       if (object->ar_flags & SPL_ARRAY_OVERLOADED_VALID) {
+               return zend_user_it_valid(iter TSRMLS_CC);
        } else {
-               return zend_hash_has_more_elements_ex(aht, &object->pos);
+               if (!aht) {
+                       php_error_docref(NULL TSRMLS_CC, E_NOTICE, 
"ArrayIterator::valid(): Array was modified outside object and is no longer an 
array");
+                       return FAILURE;
+               }
+       
+               if (object->pos && (object->ar_flags & SPL_ARRAY_IS_REF) && 
spl_hash_verify_pos(object TSRMLS_CC) == FAILURE) {
+                       php_error_docref(NULL TSRMLS_CC, E_NOTICE, 
"ArrayIterator::valid(): Array was modified outside object and internal 
position is no longer valid");
+                       return FAILURE;
+               } else {
+                       return zend_hash_has_more_elements_ex(aht, 
&object->pos);
+               }
        }
 }
 /* }}} */
@@ -677,9 +706,13 @@
        spl_array_it       *iterator = (spl_array_it *)iter;
        spl_array_object   *object   = iterator->object;
        HashTable          *aht      = spl_array_get_hash_table(object, 0 
TSRMLS_CC);
-       
-       if (zend_hash_get_current_data_ex(aht, (void**)data, &object->pos) == 
FAILURE) {
-               *data = NULL;
+
+       if (object->ar_flags & SPL_ARRAY_OVERLOADED_CURRENT)    {
+               return zend_user_it_get_current_data(iter, data TSRMLS_CC);
+       } else {
+               if (zend_hash_get_current_data_ex(aht, (void**)data, 
&object->pos) == FAILURE) {
+                       *data = NULL;
+               }
        }
 }
 /* }}} */
@@ -690,17 +723,21 @@
        spl_array_object   *object   = iterator->object;
        HashTable          *aht      = spl_array_get_hash_table(object, 0 
TSRMLS_CC);
 
-       if (!aht) {
-               php_error_docref(NULL TSRMLS_CC, E_NOTICE, 
"ArrayIterator::current(): Array was modified outside object and is no longer 
an array");
-               return HASH_KEY_NON_EXISTANT;
-       }
-
-       if ((object->ar_flags & SPL_ARRAY_IS_REF) && spl_hash_verify_pos(object 
TSRMLS_CC) == FAILURE) {
-               php_error_docref(NULL TSRMLS_CC, E_NOTICE, 
"ArrayIterator::current(): Array was modified outside object and internal 
position is no longer valid");
-               return HASH_KEY_NON_EXISTANT;
+       if (object->ar_flags & SPL_ARRAY_OVERLOADED_KEY)        {
+               return zend_user_it_get_current_key(iter, str_key, str_key_len, 
int_key TSRMLS_CC);
+       } else {
+               if (!aht) {
+                       php_error_docref(NULL TSRMLS_CC, E_NOTICE, 
"ArrayIterator::current(): Array was modified outside object and is no longer 
an array");
+                       return HASH_KEY_NON_EXISTANT;
+               }
+       
+               if ((object->ar_flags & SPL_ARRAY_IS_REF) && 
spl_hash_verify_pos(object TSRMLS_CC) == FAILURE) {
+                       php_error_docref(NULL TSRMLS_CC, E_NOTICE, 
"ArrayIterator::current(): Array was modified outside object and internal 
position is no longer valid");
+                       return HASH_KEY_NON_EXISTANT;
+               }
+       
+               return zend_hash_get_current_key_ex(aht, str_key, str_key_len, 
int_key, 1, &object->pos);
        }
-
-       return zend_hash_get_current_key_ex(aht, str_key, str_key_len, int_key, 
1, &object->pos);
 }
 /* }}} */
 
@@ -708,18 +745,21 @@
 {
        spl_array_it       *iterator = (spl_array_it *)iter;
        spl_array_object   *object   = iterator->object;
-
        HashTable          *aht      = spl_array_get_hash_table(object, 0 
TSRMLS_CC);
 
-       if (!aht) {
-               php_error_docref(NULL TSRMLS_CC, E_NOTICE, 
"ArrayIterator::current(): Array was modified outside object and is no longer 
an array");
-               return;
-       }
-
-       if ((object->ar_flags & SPL_ARRAY_IS_REF) && spl_hash_verify_pos(object 
TSRMLS_CC) == FAILURE) {
-               php_error_docref(NULL TSRMLS_CC, E_NOTICE, 
"ArrayIterator::next(): Array was modified outside object and internal position 
is no longer valid");
+       if (object->ar_flags & SPL_ARRAY_OVERLOADED_NEXT)       {
+               zend_user_it_move_forward(iter TSRMLS_CC);
        } else {
-               spl_array_next(object TSRMLS_CC);
+               if (!aht) {
+                       php_error_docref(NULL TSRMLS_CC, E_NOTICE, 
"ArrayIterator::current(): Array was modified outside object and is no longer 
an array");
+                       return;
+               }
+       
+               if ((object->ar_flags & SPL_ARRAY_IS_REF) && 
spl_hash_verify_pos(object TSRMLS_CC) == FAILURE) {
+                       php_error_docref(NULL TSRMLS_CC, E_NOTICE, 
"ArrayIterator::next(): Array was modified outside object and internal position 
is no longer valid");
+               } else {
+                       spl_array_next(object TSRMLS_CC);
+               }
        }
 }
 /* }}} */
@@ -743,7 +783,11 @@
        spl_array_it       *iterator = (spl_array_it *)iter;
        spl_array_object   *object   = iterator->object;
 
-       spl_array_rewind(object TSRMLS_CC);
+       if (object->ar_flags & SPL_ARRAY_OVERLOADED_REWIND)     {
+               zend_user_it_rewind(iter TSRMLS_CC);
+       } else {
+               spl_array_rewind(object TSRMLS_CC);
+       }
 }
 /* }}} */
 
@@ -759,25 +803,57 @@
 
 zend_object_iterator *spl_array_obj_get_iterator(zend_class_entry *ce, zval 
*object, int by_ref TSRMLS_DC) /* {{{ */
 {
-       /* disable by_ref check */
+       zval *iterator = zend_user_it_new_iterator(ce, object TSRMLS_CC);
+       zend_object_iterator *new_iterator;
+
+       zend_class_entry *ce_it = iterator && Z_TYPE_P(iterator) == IS_OBJECT ? 
Z_OBJCE_P(iterator) : NULL;
+
+       if (!ce || !ce_it || !ce_it->get_iterator || (ce_it->get_iterator == 
zend_user_it_get_new_iterator && iterator == object)) {
+               if (!EG(exception))
+               {
+                       zend_throw_exception_ex(NULL, 0 TSRMLS_CC, "Objects 
returned by %v::getIterator() must be traversable or implement interface 
Iterator", ce->name);
+               }
+               if (iterator)
+               {
+                       zval_ptr_dtor(&iterator);
+               }
+               return NULL;
+       }
+#if MBO_0
        /* We enable by ref if the returned thing does. If it is an 
ArrayIterator */
        /* or derived then it does if it's current() method is not overloaded. 
*/
-       return zend_user_it_get_new_iterator(ce, object, 0 TSRMLS_CC);
+       if (by_ref && ce_it && instanceof_function(ce_it, zend_ce_iterator 
TSRMLS_CC)) {
+               if (!ce_it->iterator_funcs.zf_current) {
+                       zend_hash_find(&ce->function_table, "current", 
sizeof("current"), (void **) &ce_it->iterator_funcs.zf_current);
+               }
+               if (ce_it->iterator_funcs.zf_current->common.scope != 
spl_ce_ArrayObject) {
+                       zend_error(E_ERROR, "An iterator cannot be used with 
foreach by reference");
+               }
+       }
+#endif
+
+       new_iterator = ce_it->get_iterator(ce_it, iterator, by_ref TSRMLS_CC);
+       zval_ptr_dtor(&iterator);
+       return new_iterator;
 }
 /* }}} */
 
 zend_object_iterator *spl_array_get_iterator(zend_class_entry *ce, zval 
*object, int by_ref TSRMLS_DC) /* {{{ */
 {
        spl_array_it       *iterator;
-       spl_array_object   *array_object;
+       spl_array_object   *array_object = 
(spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
 
-#if MBO_0
-       if (by_ref && /* check current() is overloaded, else it works */) {
-               zend_error(E_ERROR, "An iterator cannot be used with foreach by 
reference");
+       if (by_ref) {
+               if (!ce->iterator_funcs.zf_current) {
+                       zend_hash_find(&ce->function_table, "current", 
sizeof("current"), (void **) &ce->iterator_funcs.zf_current);
+               }
+               if (array_object->ar_flags & SPL_ARRAY_OVERLOADED_CURRENT) {
+                       zend_error(E_ERROR, "An iterator cannot be used with 
foreach by reference");
+               }
        }
-#endif
+
+
        iterator     = emalloc(sizeof(spl_array_it));
-       array_object = (spl_array_object*)zend_object_store_get_object(object 
TSRMLS_CC);
 
        object->refcount++;
        iterator->intern.data = (void*)object;
@@ -1434,6 +1510,7 @@
        
        REGISTER_SPL_SUB_CLASS_EX(RecursiveArrayIterator, ArrayIterator, 
spl_array_object_new, spl_funcs_RecursiveArrayIterator);
        REGISTER_SPL_IMPLEMENTS(RecursiveArrayIterator, RecursiveIterator);
+       spl_ce_RecursiveArrayIterator->get_iterator = spl_array_get_iterator;
 
        REGISTER_SPL_INTERFACE(Countable);
        

http://cvs.php.net/viewcvs.cgi/php-src/ext/spl/tests/array_019.phpt?view=markup&rev=1.1
Index: php-src/ext/spl/tests/array_019.phpt
+++ php-src/ext/spl/tests/array_019.phpt
--TEST--
SPL: ArrayIterator and foreach by reference
--SKIPIF--
<?php if (!extension_loaded("spl")) print "skip"; ?>
--FILE--
<?php

$ar = new ArrayObject(array(1));            foreach($ar as &$v) var_dump($v);
$ar = new ArrayIterator(array(2));          foreach($ar as &$v) var_dump($v);
$ar = new RecursiveArrayIterator(array(3)); foreach($ar as &$v) var_dump($v);

class ArrayIteratorEx extends ArrayIterator
{
        function current()
        {
                return ArrayIterator::current();
        }
}

$ar = new ArrayIteratorEx(array(4)); foreach($ar as $v) var_dump($v);
$ar = new ArrayIteratorEx(array(5)); foreach($ar as &$v) var_dump($v);

?>
===DONE===
<?php exit(0); ?>
--EXPECTF--
int(1)
int(2)
int(3)
int(4)

Fatal error: An iterator cannot be used with foreach by reference in 
%sarray_019.php on line %d

http://cvs.php.net/viewcvs.cgi/php-src/ext/spl/tests/array_020.phpt?view=markup&rev=1.1
Index: php-src/ext/spl/tests/array_020.phpt
+++ php-src/ext/spl/tests/array_020.phpt
--TEST--
SPL: ArrayIterator overloading
--SKIPIF--
<?php if (!extension_loaded("spl")) print "skip"; ?>
--FILE--
<?php

class ArrayIteratorEx extends ArrayIterator
{
        function rewind()
        {
                echo __METHOD__ . "\n";
                ArrayIterator::rewind();
        }

        function valid()
        {
                echo __METHOD__ . "\n";
                return ArrayIterator::valid();
        }

        function key()
        {
                echo __METHOD__ . "\n";
                return ArrayIterator::key();
        }

        function current()
        {
                echo __METHOD__ . "\n";
                return ArrayIterator::current();
        }

        function next()
        {
                echo __METHOD__ . "\n";
                return ArrayIterator::next();
        }
}

$ar = new ArrayIteratorEx(array(1,2));
foreach($ar as $k => $v)
{
        var_dump($k);
        var_dump($v);
}

?>
===DONE===
<?php exit(0); ?>
--EXPECTF--
ArrayIteratorEx::rewind
ArrayIteratorEx::valid
ArrayIteratorEx::current
ArrayIteratorEx::key
int(0)
int(1)
ArrayIteratorEx::next
ArrayIteratorEx::valid
ArrayIteratorEx::current
ArrayIteratorEx::key
int(1)
int(2)
ArrayIteratorEx::next
ArrayIteratorEx::valid
===DONE===

-- 
PHP CVS Mailing List (http://www.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to