@@ -28,6 +28,9 @@ ZEND_API zend_class_entry *zend_ce_iterator;
28
28
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
+ ZEND_API zend_class_entry * zend_ce_internal_iterator ;
32
+
33
+ static zend_object_handlers zend_internal_iterator_handlers ;
31
34
32
35
/* {{{ zend_call_method
33
36
Only returns the returned zval if retval_ptr != NULL */
@@ -286,12 +289,9 @@ ZEND_API zend_object_iterator *zend_user_it_get_new_iterator(zend_class_entry *c
286
289
/* {{{ zend_implement_traversable */
287
290
static int zend_implement_traversable (zend_class_entry * interface , zend_class_entry * class_type )
288
291
{
289
- /* check that class_type is traversable at c-level or implements at least one of 'aggregate' and 'Iterator' */
292
+ /* Check that class_type implements at least one of 'IteratorAggregate' or 'Iterator' */
290
293
uint32_t i ;
291
294
292
- if (class_type -> get_iterator || (class_type -> parent && class_type -> parent -> get_iterator )) {
293
- return SUCCESS ;
294
- }
295
295
if (class_type -> num_interfaces ) {
296
296
ZEND_ASSERT (class_type -> ce_flags & ZEND_ACC_RESOLVED_INTERFACES );
297
297
for (i = 0 ; i < class_type -> num_interfaces ; i ++ ) {
@@ -312,59 +312,38 @@ static int zend_implement_traversable(zend_class_entry *interface, zend_class_en
312
312
/* {{{ zend_implement_aggregate */
313
313
static int zend_implement_aggregate (zend_class_entry * interface , zend_class_entry * class_type )
314
314
{
315
- uint32_t i ;
316
- int t = -1 ;
317
- zend_class_iterator_funcs * funcs_ptr ;
315
+ if (zend_class_implements_interface (class_type , zend_ce_iterator )) {
316
+ zend_error_noreturn (E_ERROR ,
317
+ "Class %s cannot implement both Iterator and IteratorAggregate at the same time" ,
318
+ ZSTR_VAL (class_type -> name ));
319
+ }
318
320
319
321
if (class_type -> get_iterator ) {
320
- if (class_type -> type == ZEND_INTERNAL_CLASS ) {
321
- /* inheritance ensures the class has necessary userland methods */
322
- return SUCCESS ;
323
- } else if (class_type -> get_iterator != zend_user_it_get_new_iterator ) {
324
- /* c-level get_iterator cannot be changed (exception being only Traversable is implemented) */
325
- if (class_type -> num_interfaces ) {
326
- ZEND_ASSERT (class_type -> ce_flags & ZEND_ACC_RESOLVED_INTERFACES );
327
- for (i = 0 ; i < class_type -> num_interfaces ; i ++ ) {
328
- if (class_type -> interfaces [i ] == zend_ce_iterator ) {
329
- zend_error_noreturn (E_ERROR , "Class %s cannot implement both %s and %s at the same time" ,
330
- ZSTR_VAL (class_type -> name ),
331
- ZSTR_VAL (interface -> name ),
332
- ZSTR_VAL (zend_ce_iterator -> name ));
333
- return FAILURE ;
334
- }
335
- if (class_type -> interfaces [i ] == zend_ce_traversable ) {
336
- t = i ;
337
- }
338
- }
339
- }
340
- if (t == -1 ) {
341
- return FAILURE ;
342
- }
343
- }
322
+ /* An internal class with a custom get_iterator implementation. */
323
+ ZEND_ASSERT (class_type -> type == ZEND_INTERNAL_CLASS );
324
+ return SUCCESS ;
344
325
}
345
- if (class_type -> parent
346
- && (class_type -> parent -> ce_flags & ZEND_ACC_REUSE_GET_ITERATOR )) {
326
+
327
+ zend_function * zf = zend_hash_str_find_ptr (
328
+ & class_type -> function_table , "getiterator" , sizeof ("getiterator" ) - 1 );
329
+ if (class_type -> parent && class_type -> parent -> get_iterator != zend_user_it_get_new_iterator
330
+ && zf -> common .scope != class_type ) {
331
+ /* We have a custom get_iterator on the parent class, and the getIterator() method
332
+ * has not been overridden in this class: Inherit the parent get_iterator.
333
+ * Otherwise fall back to a normal call of the overridden getIterator(). */
347
334
class_type -> get_iterator = class_type -> parent -> get_iterator ;
348
- class_type -> ce_flags |= ZEND_ACC_REUSE_GET_ITERATOR ;
349
- } else {
350
- class_type -> get_iterator = zend_user_it_get_new_iterator ;
351
- }
352
- funcs_ptr = class_type -> iterator_funcs_ptr ;
353
- if (class_type -> type == ZEND_INTERNAL_CLASS ) {
354
- if (!funcs_ptr ) {
355
- funcs_ptr = calloc (1 , sizeof (zend_class_iterator_funcs ));
356
- class_type -> iterator_funcs_ptr = funcs_ptr ;
357
- }
358
- funcs_ptr -> zf_new_iterator = zend_hash_str_find_ptr (& class_type -> function_table , "getiterator" , sizeof ("getiterator" ) - 1 );
359
- } else {
360
- if (!funcs_ptr ) {
361
- funcs_ptr = zend_arena_alloc (& CG (arena ), sizeof (zend_class_iterator_funcs ));
362
- class_type -> iterator_funcs_ptr = funcs_ptr ;
363
- memset (funcs_ptr , 0 , sizeof (zend_class_iterator_funcs ));
364
- } else {
365
- funcs_ptr -> zf_new_iterator = NULL ;
366
- }
335
+ return SUCCESS ;
367
336
}
337
+
338
+ ZEND_ASSERT (!class_type -> iterator_funcs_ptr && "Iterator funcs already set?" );
339
+ zend_class_iterator_funcs * funcs_ptr = class_type -> type == ZEND_INTERNAL_CLASS
340
+ ? pemalloc (sizeof (zend_class_iterator_funcs ), 1 )
341
+ : zend_arena_alloc (& CG (arena ), sizeof (zend_class_iterator_funcs ));
342
+ class_type -> get_iterator = zend_user_it_get_new_iterator ;
343
+ class_type -> iterator_funcs_ptr = funcs_ptr ;
344
+ memset (funcs_ptr , 0 , sizeof (zend_class_iterator_funcs ));
345
+ funcs_ptr -> zf_new_iterator = zf ;
346
+
368
347
return SUCCESS ;
369
348
}
370
349
/* }}} */
@@ -381,12 +360,9 @@ static int zend_implement_iterator(zend_class_entry *interface, zend_class_entry
381
360
} else {
382
361
/* c-level get_iterator cannot be changed */
383
362
if (class_type -> get_iterator == zend_user_it_get_new_iterator ) {
384
- zend_error_noreturn (E_ERROR , "Class %s cannot implement both %s and %s at the same time" ,
385
- ZSTR_VAL (class_type -> name ),
386
- ZSTR_VAL (interface -> name ),
387
- ZSTR_VAL (zend_ce_aggregate -> name ));
363
+ zend_error_noreturn (E_ERROR , "Class %s cannot implement both Iterator and IteratorAggregate at the same time" ,
364
+ ZSTR_VAL (class_type -> name ));
388
365
}
389
- return FAILURE ;
390
366
}
391
367
}
392
368
if (class_type -> parent
@@ -532,6 +508,157 @@ static int zend_implement_countable(zend_class_entry *interface, zend_class_entr
532
508
}
533
509
/* }}}*/
534
510
511
+ typedef struct {
512
+ zend_object std ;
513
+ zend_object_iterator * iter ;
514
+ zend_bool rewind_called ;
515
+ } zend_internal_iterator ;
516
+
517
+ static zend_object * zend_internal_iterator_create (zend_class_entry * ce ) {
518
+ zend_internal_iterator * intern = emalloc (sizeof (zend_internal_iterator ));
519
+ zend_object_std_init (& intern -> std , ce );
520
+ intern -> std .handlers = & zend_internal_iterator_handlers ;
521
+ intern -> iter = NULL ;
522
+ intern -> rewind_called = 0 ;
523
+ return & intern -> std ;
524
+ }
525
+
526
+ ZEND_API int zend_create_internal_iterator_zval (zval * return_value , zval * obj ) {
527
+ zend_class_entry * ce = Z_OBJCE_P (obj );
528
+ ZEND_ASSERT (ce -> get_iterator != zend_user_it_get_new_iterator );
529
+ zend_object_iterator * iter = ce -> get_iterator (ce , obj , /* by_ref */ 0 );
530
+ if (!iter ) {
531
+ return FAILURE ;
532
+ }
533
+
534
+ zend_internal_iterator * intern =
535
+ (zend_internal_iterator * ) zend_internal_iterator_create (zend_ce_internal_iterator );
536
+ intern -> iter = iter ;
537
+ ZVAL_OBJ (return_value , & intern -> std );
538
+ return SUCCESS ;
539
+ }
540
+
541
+ static void zend_internal_iterator_free (zend_object * obj ) {
542
+ zend_internal_iterator * intern = (zend_internal_iterator * ) obj ;
543
+ if (intern -> iter ) {
544
+ zend_iterator_dtor (intern -> iter );
545
+ }
546
+ zend_object_std_dtor (& intern -> std );
547
+ }
548
+
549
+ static zend_internal_iterator * zend_internal_iterator_fetch (zval * This ) {
550
+ zend_internal_iterator * intern = (zend_internal_iterator * ) Z_OBJ_P (This );
551
+ if (!intern -> iter ) {
552
+ zend_throw_error (NULL , "The InternalIterator object has not been properly initialized" );
553
+ return NULL ;
554
+ }
555
+ return intern ;
556
+ }
557
+
558
+ /* Many iterators will now behave correctly if rewind() is not called, make sure it happens. */
559
+ static int zend_internal_iterator_ensure_rewound (zend_internal_iterator * intern ) {
560
+ if (!intern -> rewind_called ) {
561
+ zend_object_iterator * iter = intern -> iter ;
562
+ intern -> rewind_called = 1 ;
563
+ if (iter -> funcs -> rewind ) {
564
+ iter -> funcs -> rewind (iter );
565
+ if (UNEXPECTED (EG (exception ))) {
566
+ return FAILURE ;
567
+ }
568
+ }
569
+ }
570
+ return SUCCESS ;
571
+ }
572
+
573
+
574
+ ZEND_METHOD (InternalIterator , __construct ) {
575
+ zend_throw_error (NULL , "Cannot manually construct InternalIterator" );
576
+ }
577
+
578
+ ZEND_METHOD (InternalIterator , current ) {
579
+ ZEND_PARSE_PARAMETERS_NONE ();
580
+
581
+ zend_internal_iterator * intern = zend_internal_iterator_fetch (ZEND_THIS );
582
+ if (!intern ) {
583
+ RETURN_THROWS ();
584
+ }
585
+
586
+ if (zend_internal_iterator_ensure_rewound (intern ) == FAILURE ) {
587
+ RETURN_THROWS ();
588
+ }
589
+
590
+ zval * data = intern -> iter -> funcs -> get_current_data (intern -> iter );
591
+ if (data ) {
592
+ ZVAL_COPY_DEREF (return_value , data );
593
+ }
594
+ }
595
+
596
+ ZEND_METHOD (InternalIterator , key ) {
597
+ ZEND_PARSE_PARAMETERS_NONE ();
598
+
599
+ zend_internal_iterator * intern = zend_internal_iterator_fetch (ZEND_THIS );
600
+ if (!intern ) {
601
+ RETURN_THROWS ();
602
+ }
603
+
604
+ if (zend_internal_iterator_ensure_rewound (intern ) == FAILURE ) {
605
+ RETURN_THROWS ();
606
+ }
607
+
608
+ if (intern -> iter -> funcs -> get_current_key ) {
609
+ intern -> iter -> funcs -> get_current_key (intern -> iter , return_value );
610
+ } else {
611
+ RETURN_LONG (intern -> iter -> index );
612
+ }
613
+ }
614
+
615
+ ZEND_METHOD (InternalIterator , next ) {
616
+ ZEND_PARSE_PARAMETERS_NONE ();
617
+
618
+ zend_internal_iterator * intern = zend_internal_iterator_fetch (ZEND_THIS );
619
+ if (!intern ) {
620
+ RETURN_THROWS ();
621
+ }
622
+
623
+ if (zend_internal_iterator_ensure_rewound (intern ) == FAILURE ) {
624
+ RETURN_THROWS ();
625
+ }
626
+
627
+ intern -> iter -> funcs -> move_forward (intern -> iter );
628
+ intern -> iter -> index ++ ;
629
+ }
630
+
631
+ ZEND_METHOD (InternalIterator , valid ) {
632
+ ZEND_PARSE_PARAMETERS_NONE ();
633
+
634
+ zend_internal_iterator * intern = zend_internal_iterator_fetch (ZEND_THIS );
635
+ if (!intern ) {
636
+ RETURN_THROWS ();
637
+ }
638
+
639
+ if (zend_internal_iterator_ensure_rewound (intern ) == FAILURE ) {
640
+ RETURN_THROWS ();
641
+ }
642
+
643
+ RETURN_BOOL (intern -> iter -> funcs -> valid (intern -> iter ) == SUCCESS );
644
+ }
645
+
646
+ ZEND_METHOD (InternalIterator , rewind ) {
647
+ ZEND_PARSE_PARAMETERS_NONE ();
648
+
649
+ zend_internal_iterator * intern = zend_internal_iterator_fetch (ZEND_THIS );
650
+ if (!intern ) {
651
+ RETURN_THROWS ();
652
+ }
653
+
654
+ if (!intern -> iter -> funcs -> rewind ) {
655
+ zend_throw_error (NULL , "Iterator does not support rewinding" );
656
+ RETURN_THROWS ();
657
+ }
658
+
659
+ intern -> iter -> funcs -> rewind (intern -> iter );
660
+ }
661
+
535
662
/* {{{ function tables */
536
663
static const zend_function_entry zend_funcs_aggregate [] = {
537
664
ZEND_ABSTRACT_ME (iterator , getIterator , arginfo_class_IteratorAggregate_getIterator )
@@ -567,11 +694,23 @@ static const zend_function_entry zend_funcs_countable[] = {
567
694
ZEND_ABSTRACT_ME (Countable , count , arginfo_class_Countable_count )
568
695
ZEND_FE_END
569
696
};
697
+
698
+ static const zend_function_entry zend_funcs_internal_iterator [] = {
699
+ ZEND_ME (InternalIterator , __construct , arginfo_class_InternalIterator___construct , ZEND_ACC_PRIVATE )
700
+ ZEND_ME (InternalIterator , current , arginfo_class_InternalIterator_current , ZEND_ACC_PUBLIC )
701
+ ZEND_ME (InternalIterator , next , arginfo_class_InternalIterator_next , ZEND_ACC_PUBLIC )
702
+ ZEND_ME (InternalIterator , key , arginfo_class_InternalIterator_key , ZEND_ACC_PUBLIC )
703
+ ZEND_ME (InternalIterator , valid , arginfo_class_InternalIterator_valid , ZEND_ACC_PUBLIC )
704
+ ZEND_ME (InternalIterator , rewind , arginfo_class_InternalIterator_rewind , ZEND_ACC_PUBLIC )
705
+ ZEND_FE_END
706
+ };
570
707
/* }}} */
571
708
572
709
/* {{{ zend_register_interfaces */
573
710
ZEND_API void zend_register_interfaces (void )
574
711
{
712
+ zend_class_entry ce ;
713
+
575
714
REGISTER_MAGIC_INTERFACE (traversable , Traversable );
576
715
577
716
REGISTER_MAGIC_INTERFACE (aggregate , IteratorAggregate );
@@ -585,5 +724,15 @@ ZEND_API void zend_register_interfaces(void)
585
724
REGISTER_MAGIC_INTERFACE (serializable , Serializable );
586
725
587
726
REGISTER_MAGIC_INTERFACE (countable , Countable );
727
+
728
+ INIT_CLASS_ENTRY (ce , "InternalIterator" , zend_funcs_internal_iterator );
729
+ zend_ce_internal_iterator = zend_register_internal_class (& ce );
730
+ zend_class_implements (zend_ce_internal_iterator , 1 , zend_ce_iterator );
731
+ zend_ce_internal_iterator -> ce_flags |= ZEND_ACC_FINAL ;
732
+ zend_ce_internal_iterator -> create_object = zend_internal_iterator_create ;
733
+
734
+ memcpy (& zend_internal_iterator_handlers , zend_get_std_object_handlers (),
735
+ sizeof (zend_object_handlers ));
736
+ zend_internal_iterator_handlers .free_obj = zend_internal_iterator_free ;
588
737
}
589
738
/* }}} */
0 commit comments