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