@@ -29,6 +29,9 @@ ZEND_API zend_class_entry *zend_ce_arrayaccess;
2929ZEND_API zend_class_entry * zend_ce_serializable ;
3030ZEND_API zend_class_entry * zend_ce_countable ;
3131ZEND_API zend_class_entry * zend_ce_stringable ;
32+ ZEND_API zend_class_entry * zend_ce_internal_iterator ;
33+
34+ static zend_object_handlers zend_internal_iterator_handlers ;
3235
3336/* {{{ zend_call_method
3437 Only returns the returned zval if retval_ptr != NULL */
@@ -287,12 +290,9 @@ ZEND_API zend_object_iterator *zend_user_it_get_new_iterator(zend_class_entry *c
287290/* {{{ zend_implement_traversable */
288291static int zend_implement_traversable (zend_class_entry * interface , zend_class_entry * class_type )
289292{
290- /* check that class_type is traversable at c-level or implements at least one of 'aggregate' and 'Iterator' */
293+ /* Check that class_type implements at least one of 'IteratorAggregate' or 'Iterator' */
291294 uint32_t i ;
292295
293- if (class_type -> get_iterator || (class_type -> parent && class_type -> parent -> get_iterator )) {
294- return SUCCESS ;
295- }
296296 if (class_type -> num_interfaces ) {
297297 ZEND_ASSERT (class_type -> ce_flags & ZEND_ACC_RESOLVED_INTERFACES );
298298 for (i = 0 ; i < class_type -> num_interfaces ; i ++ ) {
@@ -479,6 +479,164 @@ static int zend_implement_serializable(zend_class_entry *interface, zend_class_e
479479}
480480/* }}}*/
481481
482+ typedef struct {
483+ zend_object std ;
484+ zend_object_iterator * iter ;
485+ zend_bool rewind_called ;
486+ } zend_internal_iterator ;
487+
488+ static zend_object * zend_internal_iterator_create (zend_class_entry * ce ) {
489+ zend_internal_iterator * intern = emalloc (sizeof (zend_internal_iterator ));
490+ zend_object_std_init (& intern -> std , ce );
491+ intern -> std .handlers = & zend_internal_iterator_handlers ;
492+ intern -> iter = NULL ;
493+ intern -> rewind_called = 0 ;
494+ return & intern -> std ;
495+ }
496+
497+ ZEND_API int zend_create_internal_iterator_zval (zval * return_value , zval * obj ) {
498+ zend_class_entry * scope = EG (current_execute_data )-> func -> common .scope ;
499+ ZEND_ASSERT (scope -> get_iterator != zend_user_it_get_new_iterator );
500+ zend_object_iterator * iter = scope -> get_iterator (Z_OBJCE_P (obj ), obj , /* by_ref */ 0 );
501+ if (!iter ) {
502+ return FAILURE ;
503+ }
504+
505+ zend_internal_iterator * intern =
506+ (zend_internal_iterator * ) zend_internal_iterator_create (zend_ce_internal_iterator );
507+ intern -> iter = iter ;
508+ ZVAL_OBJ (return_value , & intern -> std );
509+ return SUCCESS ;
510+ }
511+
512+ static void zend_internal_iterator_free (zend_object * obj ) {
513+ zend_internal_iterator * intern = (zend_internal_iterator * ) obj ;
514+ if (intern -> iter ) {
515+ zend_iterator_dtor (intern -> iter );
516+ }
517+ zend_object_std_dtor (& intern -> std );
518+ }
519+
520+ static zend_internal_iterator * zend_internal_iterator_fetch (zval * This ) {
521+ zend_internal_iterator * intern = (zend_internal_iterator * ) Z_OBJ_P (This );
522+ if (!intern -> iter ) {
523+ zend_throw_error (NULL , "The InternalIterator object has not been properly initialized" );
524+ return NULL ;
525+ }
526+ return intern ;
527+ }
528+
529+ /* Many iterators will now behave correctly if rewind() is not called, make sure it happens. */
530+ static int zend_internal_iterator_ensure_rewound (zend_internal_iterator * intern ) {
531+ if (!intern -> rewind_called ) {
532+ zend_object_iterator * iter = intern -> iter ;
533+ intern -> rewind_called = 1 ;
534+ if (iter -> funcs -> rewind ) {
535+ iter -> funcs -> rewind (iter );
536+ if (UNEXPECTED (EG (exception ))) {
537+ return FAILURE ;
538+ }
539+ }
540+ }
541+ return SUCCESS ;
542+ }
543+
544+
545+ ZEND_METHOD (InternalIterator , __construct ) {
546+ zend_throw_error (NULL , "Cannot manually construct InternalIterator" );
547+ }
548+
549+ ZEND_METHOD (InternalIterator , current ) {
550+ ZEND_PARSE_PARAMETERS_NONE ();
551+
552+ zend_internal_iterator * intern = zend_internal_iterator_fetch (ZEND_THIS );
553+ if (!intern ) {
554+ RETURN_THROWS ();
555+ }
556+
557+ if (zend_internal_iterator_ensure_rewound (intern ) == FAILURE ) {
558+ RETURN_THROWS ();
559+ }
560+
561+ zval * data = intern -> iter -> funcs -> get_current_data (intern -> iter );
562+ if (data ) {
563+ ZVAL_COPY_DEREF (return_value , data );
564+ }
565+ }
566+
567+ ZEND_METHOD (InternalIterator , key ) {
568+ ZEND_PARSE_PARAMETERS_NONE ();
569+
570+ zend_internal_iterator * intern = zend_internal_iterator_fetch (ZEND_THIS );
571+ if (!intern ) {
572+ RETURN_THROWS ();
573+ }
574+
575+ if (zend_internal_iterator_ensure_rewound (intern ) == FAILURE ) {
576+ RETURN_THROWS ();
577+ }
578+
579+ if (intern -> iter -> funcs -> get_current_key ) {
580+ intern -> iter -> funcs -> get_current_key (intern -> iter , return_value );
581+ } else {
582+ RETURN_LONG (intern -> iter -> index );
583+ }
584+ }
585+
586+ ZEND_METHOD (InternalIterator , next ) {
587+ ZEND_PARSE_PARAMETERS_NONE ();
588+
589+ zend_internal_iterator * intern = zend_internal_iterator_fetch (ZEND_THIS );
590+ if (!intern ) {
591+ RETURN_THROWS ();
592+ }
593+
594+ if (zend_internal_iterator_ensure_rewound (intern ) == FAILURE ) {
595+ RETURN_THROWS ();
596+ }
597+
598+ intern -> iter -> funcs -> move_forward (intern -> iter );
599+ intern -> iter -> index ++ ;
600+ }
601+
602+ ZEND_METHOD (InternalIterator , valid ) {
603+ ZEND_PARSE_PARAMETERS_NONE ();
604+
605+ zend_internal_iterator * intern = zend_internal_iterator_fetch (ZEND_THIS );
606+ if (!intern ) {
607+ RETURN_THROWS ();
608+ }
609+
610+ if (zend_internal_iterator_ensure_rewound (intern ) == FAILURE ) {
611+ RETURN_THROWS ();
612+ }
613+
614+ RETURN_BOOL (intern -> iter -> funcs -> valid (intern -> iter ) == SUCCESS );
615+ }
616+
617+ ZEND_METHOD (InternalIterator , rewind ) {
618+ ZEND_PARSE_PARAMETERS_NONE ();
619+
620+ zend_internal_iterator * intern = zend_internal_iterator_fetch (ZEND_THIS );
621+ if (!intern ) {
622+ RETURN_THROWS ();
623+ }
624+
625+ if (!intern -> iter -> funcs -> rewind ) {
626+ /* Allow calling rewind() if no iteration has happened yet,
627+ * even if the iterator does not support rewinding. */
628+ if (intern -> iter -> index != 0 ) {
629+ zend_throw_error (NULL , "Iterator does not support rewinding" );
630+ RETURN_THROWS ();
631+ }
632+ intern -> iter -> index = 0 ;
633+ return ;
634+ }
635+
636+ intern -> iter -> funcs -> rewind (intern -> iter );
637+ intern -> iter -> index = 0 ;
638+ }
639+
482640/* {{{ function tables */
483641static const zend_function_entry zend_funcs_aggregate [] = {
484642 ZEND_ABSTRACT_ME (iterator , getIterator , arginfo_class_IteratorAggregate_getIterator )
@@ -519,11 +677,23 @@ static const zend_function_entry zend_funcs_stringable[] = {
519677 ZEND_ABSTRACT_ME (Stringable , __toString , arginfo_class_Stringable___toString )
520678 ZEND_FE_END
521679};
680+
681+ static const zend_function_entry zend_funcs_internal_iterator [] = {
682+ ZEND_ME (InternalIterator , __construct , arginfo_class_InternalIterator___construct , ZEND_ACC_PRIVATE )
683+ ZEND_ME (InternalIterator , current , arginfo_class_InternalIterator_current , ZEND_ACC_PUBLIC )
684+ ZEND_ME (InternalIterator , next , arginfo_class_InternalIterator_next , ZEND_ACC_PUBLIC )
685+ ZEND_ME (InternalIterator , key , arginfo_class_InternalIterator_key , ZEND_ACC_PUBLIC )
686+ ZEND_ME (InternalIterator , valid , arginfo_class_InternalIterator_valid , ZEND_ACC_PUBLIC )
687+ ZEND_ME (InternalIterator , rewind , arginfo_class_InternalIterator_rewind , ZEND_ACC_PUBLIC )
688+ ZEND_FE_END
689+ };
522690/* }}} */
523691
524692/* {{{ zend_register_interfaces */
525693ZEND_API void zend_register_interfaces (void )
526694{
695+ zend_class_entry ce ;
696+
527697 REGISTER_MAGIC_INTERFACE (traversable , Traversable );
528698
529699 REGISTER_MAGIC_INTERFACE (aggregate , IteratorAggregate );
@@ -534,7 +704,6 @@ ZEND_API void zend_register_interfaces(void)
534704
535705 REGISTER_MAGIC_INTERFACE (serializable , Serializable );
536706
537- zend_class_entry ce ;
538707 INIT_CLASS_ENTRY (ce , "ArrayAccess" , zend_funcs_arrayaccess );
539708 zend_ce_arrayaccess = zend_register_internal_interface (& ce );
540709
@@ -543,5 +712,15 @@ ZEND_API void zend_register_interfaces(void)
543712
544713 INIT_CLASS_ENTRY (ce , "Stringable" , zend_funcs_stringable );
545714 zend_ce_stringable = zend_register_internal_interface (& ce );
715+
716+ INIT_CLASS_ENTRY (ce , "InternalIterator" , zend_funcs_internal_iterator );
717+ zend_ce_internal_iterator = zend_register_internal_class (& ce );
718+ zend_class_implements (zend_ce_internal_iterator , 1 , zend_ce_iterator );
719+ zend_ce_internal_iterator -> ce_flags |= ZEND_ACC_FINAL ;
720+ zend_ce_internal_iterator -> create_object = zend_internal_iterator_create ;
721+
722+ memcpy (& zend_internal_iterator_handlers , zend_get_std_object_handlers (),
723+ sizeof (zend_object_handlers ));
724+ zend_internal_iterator_handlers .free_obj = zend_internal_iterator_free ;
546725}
547726/* }}} */
0 commit comments