@@ -29,6 +29,9 @@ ZEND_API zend_class_entry *zend_ce_arrayaccess;
29
29
ZEND_API zend_class_entry * zend_ce_serializable ;
30
30
ZEND_API zend_class_entry * zend_ce_countable ;
31
31
ZEND_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 ;
32
35
33
36
/* {{{ zend_call_method
34
37
Only returns the returned zval if retval_ptr != NULL */
@@ -287,20 +290,16 @@ ZEND_API zend_object_iterator *zend_user_it_get_new_iterator(zend_class_entry *c
287
290
/* {{{ zend_implement_traversable */
288
291
static int zend_implement_traversable (zend_class_entry * interface , zend_class_entry * class_type )
289
292
{
290
- /* check that class_type is traversable at c-level or implements at least one of 'aggregate' and 'Iterator' */
291
- uint32_t i ;
292
-
293
- if (class_type -> get_iterator || (class_type -> parent && class_type -> parent -> get_iterator )) {
294
- return SUCCESS ;
295
- }
296
293
/* Abstract class can implement Traversable only, in which case the extending class must
297
294
* implement Iterator or IteratorAggregate. */
298
295
if (class_type -> ce_flags & ZEND_ACC_EXPLICIT_ABSTRACT_CLASS ) {
299
296
return SUCCESS ;
300
297
}
298
+
299
+ /* Check that class_type implements at least one of 'IteratorAggregate' or 'Iterator' */
301
300
if (class_type -> num_interfaces ) {
302
301
ZEND_ASSERT (class_type -> ce_flags & ZEND_ACC_RESOLVED_INTERFACES );
303
- for (i = 0 ; i < class_type -> num_interfaces ; i ++ ) {
302
+ for (uint32_t i = 0 ; i < class_type -> num_interfaces ; i ++ ) {
304
303
if (class_type -> interfaces [i ] == zend_ce_aggregate || class_type -> interfaces [i ] == zend_ce_iterator ) {
305
304
return SUCCESS ;
306
305
}
@@ -484,9 +483,169 @@ static int zend_implement_serializable(zend_class_entry *interface, zend_class_e
484
483
}
485
484
/* }}}*/
486
485
486
+ typedef struct {
487
+ zend_object std ;
488
+ zend_object_iterator * iter ;
489
+ zend_bool rewind_called ;
490
+ } zend_internal_iterator ;
491
+
492
+ static zend_object * zend_internal_iterator_create (zend_class_entry * ce ) {
493
+ zend_internal_iterator * intern = emalloc (sizeof (zend_internal_iterator ));
494
+ zend_object_std_init (& intern -> std , ce );
495
+ intern -> std .handlers = & zend_internal_iterator_handlers ;
496
+ intern -> iter = NULL ;
497
+ intern -> rewind_called = 0 ;
498
+ return & intern -> std ;
499
+ }
500
+
501
+ ZEND_API int zend_create_internal_iterator_zval (zval * return_value , zval * obj ) {
502
+ zend_class_entry * scope = EG (current_execute_data )-> func -> common .scope ;
503
+ ZEND_ASSERT (scope -> get_iterator != zend_user_it_get_new_iterator );
504
+ zend_object_iterator * iter = scope -> get_iterator (Z_OBJCE_P (obj ), obj , /* by_ref */ 0 );
505
+ if (!iter ) {
506
+ return FAILURE ;
507
+ }
508
+
509
+ zend_internal_iterator * intern =
510
+ (zend_internal_iterator * ) zend_internal_iterator_create (zend_ce_internal_iterator );
511
+ intern -> iter = iter ;
512
+ ZVAL_OBJ (return_value , & intern -> std );
513
+ return SUCCESS ;
514
+ }
515
+
516
+ static void zend_internal_iterator_free (zend_object * obj ) {
517
+ zend_internal_iterator * intern = (zend_internal_iterator * ) obj ;
518
+ if (intern -> iter ) {
519
+ zend_iterator_dtor (intern -> iter );
520
+ }
521
+ zend_object_std_dtor (& intern -> std );
522
+ }
523
+
524
+ static zend_internal_iterator * zend_internal_iterator_fetch (zval * This ) {
525
+ zend_internal_iterator * intern = (zend_internal_iterator * ) Z_OBJ_P (This );
526
+ if (!intern -> iter ) {
527
+ zend_throw_error (NULL , "The InternalIterator object has not been properly initialized" );
528
+ return NULL ;
529
+ }
530
+ return intern ;
531
+ }
532
+
533
+ /* Many iterators will not behave correctly if rewind() is not called, make sure it happens. */
534
+ static int zend_internal_iterator_ensure_rewound (zend_internal_iterator * intern ) {
535
+ if (!intern -> rewind_called ) {
536
+ zend_object_iterator * iter = intern -> iter ;
537
+ intern -> rewind_called = 1 ;
538
+ if (iter -> funcs -> rewind ) {
539
+ iter -> funcs -> rewind (iter );
540
+ if (UNEXPECTED (EG (exception ))) {
541
+ return FAILURE ;
542
+ }
543
+ }
544
+ }
545
+ return SUCCESS ;
546
+ }
547
+
548
+
549
+ ZEND_METHOD (InternalIterator , __construct ) {
550
+ zend_throw_error (NULL , "Cannot manually construct InternalIterator" );
551
+ }
552
+
553
+ ZEND_METHOD (InternalIterator , current ) {
554
+ ZEND_PARSE_PARAMETERS_NONE ();
555
+
556
+ zend_internal_iterator * intern = zend_internal_iterator_fetch (ZEND_THIS );
557
+ if (!intern ) {
558
+ RETURN_THROWS ();
559
+ }
560
+
561
+ if (zend_internal_iterator_ensure_rewound (intern ) == FAILURE ) {
562
+ RETURN_THROWS ();
563
+ }
564
+
565
+ zval * data = intern -> iter -> funcs -> get_current_data (intern -> iter );
566
+ if (data ) {
567
+ ZVAL_COPY_DEREF (return_value , data );
568
+ }
569
+ }
570
+
571
+ ZEND_METHOD (InternalIterator , key ) {
572
+ ZEND_PARSE_PARAMETERS_NONE ();
573
+
574
+ zend_internal_iterator * intern = zend_internal_iterator_fetch (ZEND_THIS );
575
+ if (!intern ) {
576
+ RETURN_THROWS ();
577
+ }
578
+
579
+ if (zend_internal_iterator_ensure_rewound (intern ) == FAILURE ) {
580
+ RETURN_THROWS ();
581
+ }
582
+
583
+ if (intern -> iter -> funcs -> get_current_key ) {
584
+ intern -> iter -> funcs -> get_current_key (intern -> iter , return_value );
585
+ } else {
586
+ RETURN_LONG (intern -> iter -> index );
587
+ }
588
+ }
589
+
590
+ ZEND_METHOD (InternalIterator , next ) {
591
+ ZEND_PARSE_PARAMETERS_NONE ();
592
+
593
+ zend_internal_iterator * intern = zend_internal_iterator_fetch (ZEND_THIS );
594
+ if (!intern ) {
595
+ RETURN_THROWS ();
596
+ }
597
+
598
+ if (zend_internal_iterator_ensure_rewound (intern ) == FAILURE ) {
599
+ RETURN_THROWS ();
600
+ }
601
+
602
+ intern -> iter -> funcs -> move_forward (intern -> iter );
603
+ intern -> iter -> index ++ ;
604
+ }
605
+
606
+ ZEND_METHOD (InternalIterator , valid ) {
607
+ ZEND_PARSE_PARAMETERS_NONE ();
608
+
609
+ zend_internal_iterator * intern = zend_internal_iterator_fetch (ZEND_THIS );
610
+ if (!intern ) {
611
+ RETURN_THROWS ();
612
+ }
613
+
614
+ if (zend_internal_iterator_ensure_rewound (intern ) == FAILURE ) {
615
+ RETURN_THROWS ();
616
+ }
617
+
618
+ RETURN_BOOL (intern -> iter -> funcs -> valid (intern -> iter ) == SUCCESS );
619
+ }
620
+
621
+ ZEND_METHOD (InternalIterator , rewind ) {
622
+ ZEND_PARSE_PARAMETERS_NONE ();
623
+
624
+ zend_internal_iterator * intern = zend_internal_iterator_fetch (ZEND_THIS );
625
+ if (!intern ) {
626
+ RETURN_THROWS ();
627
+ }
628
+
629
+ if (!intern -> iter -> funcs -> rewind ) {
630
+ /* Allow calling rewind() if no iteration has happened yet,
631
+ * even if the iterator does not support rewinding. */
632
+ if (intern -> iter -> index != 0 ) {
633
+ zend_throw_error (NULL , "Iterator does not support rewinding" );
634
+ RETURN_THROWS ();
635
+ }
636
+ intern -> iter -> index = 0 ;
637
+ return ;
638
+ }
639
+
640
+ intern -> iter -> funcs -> rewind (intern -> iter );
641
+ intern -> iter -> index = 0 ;
642
+ }
643
+
487
644
/* {{{ zend_register_interfaces */
488
645
ZEND_API void zend_register_interfaces (void )
489
646
{
647
+ zend_class_entry ce ;
648
+
490
649
REGISTER_MAGIC_INTERFACE (traversable , Traversable );
491
650
492
651
REGISTER_MAGIC_INTERFACE (aggregate , IteratorAggregate );
@@ -497,7 +656,6 @@ ZEND_API void zend_register_interfaces(void)
497
656
498
657
REGISTER_MAGIC_INTERFACE (serializable , Serializable );
499
658
500
- zend_class_entry ce ;
501
659
INIT_CLASS_ENTRY (ce , "ArrayAccess" , class_ArrayAccess_methods );
502
660
zend_ce_arrayaccess = zend_register_internal_interface (& ce );
503
661
@@ -506,5 +664,17 @@ ZEND_API void zend_register_interfaces(void)
506
664
507
665
INIT_CLASS_ENTRY (ce , "Stringable" , class_Stringable_methods );
508
666
zend_ce_stringable = zend_register_internal_interface (& ce );
667
+
668
+ INIT_CLASS_ENTRY (ce , "InternalIterator" , class_InternalIterator_methods );
669
+ zend_ce_internal_iterator = zend_register_internal_class (& ce );
670
+ zend_class_implements (zend_ce_internal_iterator , 1 , zend_ce_iterator );
671
+ zend_ce_internal_iterator -> ce_flags |= ZEND_ACC_FINAL ;
672
+ zend_ce_internal_iterator -> create_object = zend_internal_iterator_create ;
673
+ zend_ce_internal_iterator -> serialize = zend_class_serialize_deny ;
674
+ zend_ce_internal_iterator -> unserialize = zend_class_unserialize_deny ;
675
+
676
+ memcpy (& zend_internal_iterator_handlers , zend_get_std_object_handlers (),
677
+ sizeof (zend_object_handlers ));
678
+ zend_internal_iterator_handlers .free_obj = zend_internal_iterator_free ;
509
679
}
510
680
/* }}} */
0 commit comments