@@ -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 */
@@ -246,20 +249,16 @@ ZEND_API zend_object_iterator *zend_user_it_get_new_iterator(zend_class_entry *c
246
249
/* {{{ zend_implement_traversable */
247
250
static int zend_implement_traversable (zend_class_entry * interface , zend_class_entry * class_type )
248
251
{
249
- /* check that class_type is traversable at c-level or implements at least one of 'aggregate' and 'Iterator' */
250
- uint32_t i ;
251
-
252
- if (class_type -> get_iterator || (class_type -> parent && class_type -> parent -> get_iterator )) {
253
- return SUCCESS ;
254
- }
255
252
/* Abstract class can implement Traversable only, in which case the extending class must
256
253
* implement Iterator or IteratorAggregate. */
257
254
if (class_type -> ce_flags & ZEND_ACC_EXPLICIT_ABSTRACT_CLASS ) {
258
255
return SUCCESS ;
259
256
}
257
+
258
+ /* Check that class_type implements at least one of 'IteratorAggregate' or 'Iterator' */
260
259
if (class_type -> num_interfaces ) {
261
260
ZEND_ASSERT (class_type -> ce_flags & ZEND_ACC_RESOLVED_INTERFACES );
262
- for (i = 0 ; i < class_type -> num_interfaces ; i ++ ) {
261
+ for (uint32_t i = 0 ; i < class_type -> num_interfaces ; i ++ ) {
263
262
if (class_type -> interfaces [i ] == zend_ce_aggregate || class_type -> interfaces [i ] == zend_ce_iterator ) {
264
263
return SUCCESS ;
265
264
}
@@ -441,9 +440,169 @@ static int zend_implement_serializable(zend_class_entry *interface, zend_class_e
441
440
}
442
441
/* }}}*/
443
442
443
+ typedef struct {
444
+ zend_object std ;
445
+ zend_object_iterator * iter ;
446
+ zend_bool rewind_called ;
447
+ } zend_internal_iterator ;
448
+
449
+ static zend_object * zend_internal_iterator_create (zend_class_entry * ce ) {
450
+ zend_internal_iterator * intern = emalloc (sizeof (zend_internal_iterator ));
451
+ zend_object_std_init (& intern -> std , ce );
452
+ intern -> std .handlers = & zend_internal_iterator_handlers ;
453
+ intern -> iter = NULL ;
454
+ intern -> rewind_called = 0 ;
455
+ return & intern -> std ;
456
+ }
457
+
458
+ ZEND_API int zend_create_internal_iterator_zval (zval * return_value , zval * obj ) {
459
+ zend_class_entry * scope = EG (current_execute_data )-> func -> common .scope ;
460
+ ZEND_ASSERT (scope -> get_iterator != zend_user_it_get_new_iterator );
461
+ zend_object_iterator * iter = scope -> get_iterator (Z_OBJCE_P (obj ), obj , /* by_ref */ 0 );
462
+ if (!iter ) {
463
+ return FAILURE ;
464
+ }
465
+
466
+ zend_internal_iterator * intern =
467
+ (zend_internal_iterator * ) zend_internal_iterator_create (zend_ce_internal_iterator );
468
+ intern -> iter = iter ;
469
+ ZVAL_OBJ (return_value , & intern -> std );
470
+ return SUCCESS ;
471
+ }
472
+
473
+ static void zend_internal_iterator_free (zend_object * obj ) {
474
+ zend_internal_iterator * intern = (zend_internal_iterator * ) obj ;
475
+ if (intern -> iter ) {
476
+ zend_iterator_dtor (intern -> iter );
477
+ }
478
+ zend_object_std_dtor (& intern -> std );
479
+ }
480
+
481
+ static zend_internal_iterator * zend_internal_iterator_fetch (zval * This ) {
482
+ zend_internal_iterator * intern = (zend_internal_iterator * ) Z_OBJ_P (This );
483
+ if (!intern -> iter ) {
484
+ zend_throw_error (NULL , "The InternalIterator object has not been properly initialized" );
485
+ return NULL ;
486
+ }
487
+ return intern ;
488
+ }
489
+
490
+ /* Many iterators will not behave correctly if rewind() is not called, make sure it happens. */
491
+ static int zend_internal_iterator_ensure_rewound (zend_internal_iterator * intern ) {
492
+ if (!intern -> rewind_called ) {
493
+ zend_object_iterator * iter = intern -> iter ;
494
+ intern -> rewind_called = 1 ;
495
+ if (iter -> funcs -> rewind ) {
496
+ iter -> funcs -> rewind (iter );
497
+ if (UNEXPECTED (EG (exception ))) {
498
+ return FAILURE ;
499
+ }
500
+ }
501
+ }
502
+ return SUCCESS ;
503
+ }
504
+
505
+
506
+ ZEND_METHOD (InternalIterator , __construct ) {
507
+ zend_throw_error (NULL , "Cannot manually construct InternalIterator" );
508
+ }
509
+
510
+ ZEND_METHOD (InternalIterator , current ) {
511
+ ZEND_PARSE_PARAMETERS_NONE ();
512
+
513
+ zend_internal_iterator * intern = zend_internal_iterator_fetch (ZEND_THIS );
514
+ if (!intern ) {
515
+ RETURN_THROWS ();
516
+ }
517
+
518
+ if (zend_internal_iterator_ensure_rewound (intern ) == FAILURE ) {
519
+ RETURN_THROWS ();
520
+ }
521
+
522
+ zval * data = intern -> iter -> funcs -> get_current_data (intern -> iter );
523
+ if (data ) {
524
+ ZVAL_COPY_DEREF (return_value , data );
525
+ }
526
+ }
527
+
528
+ ZEND_METHOD (InternalIterator , key ) {
529
+ ZEND_PARSE_PARAMETERS_NONE ();
530
+
531
+ zend_internal_iterator * intern = zend_internal_iterator_fetch (ZEND_THIS );
532
+ if (!intern ) {
533
+ RETURN_THROWS ();
534
+ }
535
+
536
+ if (zend_internal_iterator_ensure_rewound (intern ) == FAILURE ) {
537
+ RETURN_THROWS ();
538
+ }
539
+
540
+ if (intern -> iter -> funcs -> get_current_key ) {
541
+ intern -> iter -> funcs -> get_current_key (intern -> iter , return_value );
542
+ } else {
543
+ RETURN_LONG (intern -> iter -> index );
544
+ }
545
+ }
546
+
547
+ ZEND_METHOD (InternalIterator , next ) {
548
+ ZEND_PARSE_PARAMETERS_NONE ();
549
+
550
+ zend_internal_iterator * intern = zend_internal_iterator_fetch (ZEND_THIS );
551
+ if (!intern ) {
552
+ RETURN_THROWS ();
553
+ }
554
+
555
+ if (zend_internal_iterator_ensure_rewound (intern ) == FAILURE ) {
556
+ RETURN_THROWS ();
557
+ }
558
+
559
+ intern -> iter -> funcs -> move_forward (intern -> iter );
560
+ intern -> iter -> index ++ ;
561
+ }
562
+
563
+ ZEND_METHOD (InternalIterator , valid ) {
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
+ RETURN_BOOL (intern -> iter -> funcs -> valid (intern -> iter ) == SUCCESS );
576
+ }
577
+
578
+ ZEND_METHOD (InternalIterator , rewind ) {
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 (!intern -> iter -> funcs -> rewind ) {
587
+ /* Allow calling rewind() if no iteration has happened yet,
588
+ * even if the iterator does not support rewinding. */
589
+ if (intern -> iter -> index != 0 ) {
590
+ zend_throw_error (NULL , "Iterator does not support rewinding" );
591
+ RETURN_THROWS ();
592
+ }
593
+ intern -> iter -> index = 0 ;
594
+ return ;
595
+ }
596
+
597
+ intern -> iter -> funcs -> rewind (intern -> iter );
598
+ intern -> iter -> index = 0 ;
599
+ }
600
+
444
601
/* {{{ zend_register_interfaces */
445
602
ZEND_API void zend_register_interfaces (void )
446
603
{
604
+ zend_class_entry ce ;
605
+
447
606
REGISTER_MAGIC_INTERFACE (traversable , Traversable );
448
607
449
608
REGISTER_MAGIC_INTERFACE (aggregate , IteratorAggregate );
@@ -454,7 +613,6 @@ ZEND_API void zend_register_interfaces(void)
454
613
455
614
REGISTER_MAGIC_INTERFACE (serializable , Serializable );
456
615
457
- zend_class_entry ce ;
458
616
INIT_CLASS_ENTRY (ce , "ArrayAccess" , class_ArrayAccess_methods );
459
617
zend_ce_arrayaccess = zend_register_internal_interface (& ce );
460
618
@@ -463,5 +621,17 @@ ZEND_API void zend_register_interfaces(void)
463
621
464
622
INIT_CLASS_ENTRY (ce , "Stringable" , class_Stringable_methods );
465
623
zend_ce_stringable = zend_register_internal_interface (& ce );
624
+
625
+ INIT_CLASS_ENTRY (ce , "InternalIterator" , class_InternalIterator_methods );
626
+ zend_ce_internal_iterator = zend_register_internal_class (& ce );
627
+ zend_class_implements (zend_ce_internal_iterator , 1 , zend_ce_iterator );
628
+ zend_ce_internal_iterator -> ce_flags |= ZEND_ACC_FINAL ;
629
+ zend_ce_internal_iterator -> create_object = zend_internal_iterator_create ;
630
+ zend_ce_internal_iterator -> serialize = zend_class_serialize_deny ;
631
+ zend_ce_internal_iterator -> unserialize = zend_class_unserialize_deny ;
632
+
633
+ memcpy (& zend_internal_iterator_handlers , zend_get_std_object_handlers (),
634
+ sizeof (zend_object_handlers ));
635
+ zend_internal_iterator_handlers .free_obj = zend_internal_iterator_free ;
466
636
}
467
637
/* }}} */
0 commit comments