Commit 4d528531be4 for php.net

commit 4d528531be4d944f888954099ac5b037a645cadf
Author: Gina Peter Banyard <girgias@php.net>
Date:   Sat Mar 21 17:05:33 2026 +0000

    ext/spl: extract AppendIterator constructor (#21367)

    The implementation of it is simple whereas spl_dual_it_construct() is extremely convoluted

diff --git a/ext/spl/spl_iterators.c b/ext/spl/spl_iterators.c
index a73e69519db..4f1a2a407be 100644
--- a/ext/spl/spl_iterators.c
+++ b/ext/spl/spl_iterators.c
@@ -1341,15 +1341,6 @@ static spl_dual_it_object* spl_dual_it_construct(INTERNAL_FUNCTION_PARAMETERS, z
 			}
 			break;
 		}
-		case DIT_AppendIterator:
-			if (zend_parse_parameters_none() == FAILURE) {
-				return NULL;
-			}
-			intern->dit_type = DIT_AppendIterator;
-			object_init_ex(&intern->u.append.zarrayit, spl_ce_ArrayIterator);
-			zend_call_method_with_0_params(Z_OBJ(intern->u.append.zarrayit), spl_ce_ArrayIterator, &spl_ce_ArrayIterator->constructor, "__construct", NULL);
-			intern->u.append.iterator = spl_ce_ArrayIterator->get_iterator(spl_ce_ArrayIterator, &intern->u.append.zarrayit, 0);
-			return intern;
 		case DIT_RegexIterator:
 		case DIT_RecursiveRegexIterator: {
 			zend_string *regex;
@@ -2814,7 +2805,21 @@ static void spl_append_it_next(spl_dual_it_object *intern) /* {{{ */
 /* {{{ Create an AppendIterator */
 PHP_METHOD(AppendIterator, __construct)
 {
-	spl_dual_it_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, spl_ce_AppendIterator, zend_ce_iterator, DIT_AppendIterator);
+	ZEND_PARSE_PARAMETERS_NONE();
+
+	spl_dual_it_object *intern = Z_SPLDUAL_IT_P(ZEND_THIS);
+
+	/* TODO: This should be converted to a normal Error as this is triggered when calling the constructor twice */
+	if (intern->dit_type != DIT_Unknown) {
+		zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "%s::getIterator() must be called exactly once per instance", ZSTR_VAL(spl_ce_AppendIterator->name));
+		RETURN_THROWS();
+	}
+
+	intern->dit_type = DIT_AppendIterator;
+	object_init_ex(&intern->u.append.zarrayit, spl_ce_ArrayIterator);
+	zend_call_method_with_0_params(Z_OBJ(intern->u.append.zarrayit), spl_ce_ArrayIterator, &spl_ce_ArrayIterator->constructor, "__construct", NULL);
+	intern->u.append.iterator = spl_ce_ArrayIterator->get_iterator(spl_ce_ArrayIterator, &intern->u.append.zarrayit, 0);
+
 } /* }}} */

 /* {{{ Append an iterator */
diff --git a/ext/spl/tests/iterator_031.phpt b/ext/spl/tests/iterator_031.phpt
index 610e056f538..c2b5885dd23 100644
--- a/ext/spl/tests/iterator_031.phpt
+++ b/ext/spl/tests/iterator_031.phpt
@@ -3,6 +3,13 @@
 --FILE--
 <?php

+$ap = new AppendIterator();
+try {
+	$ap->__construct();
+} catch (\Throwable $e) {
+	echo $e::class, ': ', $e->getMessage(), "\n";
+}
+
 class MyArrayIterator extends ArrayIterator
 {
     function rewind(): void
@@ -14,8 +21,7 @@ function rewind(): void

 $it = new MyArrayIterator(array(1,2));

-foreach($it as $k=>$v)
-{
+foreach($it as $k=>$v) {
     echo "$k=>$v\n";
 }

@@ -44,31 +50,24 @@ function append(Iterator $what): void
         parent::append($what);
     }

-    function parent__construct()
-    {
+    function parent__construct() {
         parent::__construct();
     }
 }

 $ap = new MyAppendIterator;

-try
-{
+try {
     $ap->append($it);
-}
-catch(\Error $e)
-{
+} catch(\Error $e) {
     echo $e->getMessage() . "\n";
 }

 $ap->parent__construct();

-try
-{
+try {
     $ap->parent__construct($it);
-}
-catch(BadMethodCallException $e)
-{
+} catch(BadMethodCallException $e) {
     echo $e->getMessage() . "\n";
 }

@@ -76,13 +75,13 @@ function parent__construct()
 $ap->append($it);
 $ap->append($it);

-foreach($ap as $k=>$v)
-{
+foreach($ap as $k=>$v) {
     echo "$k=>$v\n";
 }

 ?>
 --EXPECT--
+BadMethodCallException: AppendIterator::getIterator() must be called exactly once per instance
 MyArrayIterator::rewind
 0=>1
 1=>2