Commit 44b11b75f2b for php.net

commit 44b11b75f2b91f66ba8a69d762d1e05d8f38d89e
Author: Weilin Du <weilindu@php.net>
Date:   Sun Jun 28 17:36:38 2026 +0800

    ext/Intl: Fix IntlIterator::current() on invalid iterators (#22495)

    IntlIterator::current() could expose an undefined zval when called before
    theiterator is positioned, or after the iterator becomes invalid.

    In these cases we directly returns the undefined zval and cause to a UNKNOWN:0
    output in userland. Generally, a NULL output would be more proper.

diff --git a/NEWS b/NEWS
index 9df461452c1..a4e0e3ba4f0 100644
--- a/NEWS
+++ b/NEWS
@@ -107,6 +107,8 @@ PHP                                                                        NEWS
     as float rather than int on 64-bit platforms. (Weilin Du)
   . Fixed UConverter::transcode() silently truncating from_subst and to_subst
     option lengths greater than 127 bytes. (Weilin Du)
+  . Fixed IntlIterator::current() to return NULL instead of an undefined value
+    when the iterator is not positioned on a valid element. (Weilin Du)

 - IO:
   . Added new polling API. (Jakub Zelenka)
diff --git a/docs/source/core/data-structures/zend_string.rst b/docs/source/core/data-structures/zend_string.rst
index 5b1d3ac0ccf..292028e4349 100644
--- a/docs/source/core/data-structures/zend_string.rst
+++ b/docs/source/core/data-structures/zend_string.rst
@@ -29,9 +29,9 @@ in bytes, and the ``val`` field contains the actual string data.

 You may wonder why the ``val`` field is declared as ``char val[1]``. This is called the `struct
 hack`_ in C. It is used to create structs with a flexible size, namely by allowing the last element
-to be expanded arbitrarily. In this case, the size of ``zend_string`` depends on the string's length,
-which is determined at runtime (see ``_ZSTR_STRUCT_SIZE``). When allocating the string, we append
-enough bytes to the allocation to hold the strings content.
+to be expanded arbitrarily. In this case, the size of ``zend_string`` depends on the string's
+length, which is determined at runtime (see ``_ZSTR_STRUCT_SIZE``). When allocating the string, we
+append enough bytes to the allocation to hold the strings content.

 .. _struct hack: https://www.geeksforgeeks.org/struct-hack/

diff --git a/ext/intl/common/common_enum.cpp b/ext/intl/common/common_enum.cpp
index 37956186694..4260e3cce57 100644
--- a/ext/intl/common/common_enum.cpp
+++ b/ext/intl/common/common_enum.cpp
@@ -226,7 +226,7 @@ PHP_METHOD(IntlIterator, current)

 	INTLITERATOR_METHOD_FETCH_OBJECT;
 	data = ii->iterator->funcs->get_current_data(ii->iterator);
-	if (data) {
+	if (data && !Z_ISUNDEF_P(data)) {
 		RETURN_COPY_DEREF(data);
 	}
 }
diff --git a/ext/intl/tests/intl_iterator_current_invalid.phpt b/ext/intl/tests/intl_iterator_current_invalid.phpt
new file mode 100644
index 00000000000..5276bee5bae
--- /dev/null
+++ b/ext/intl/tests/intl_iterator_current_invalid.phpt
@@ -0,0 +1,22 @@
+--TEST--
+IntlIterator::current() returns null when the iterator is not valid
+--EXTENSIONS--
+intl
+--FILE--
+<?php
+$iterator = IntlBreakIterator::createWordInstance('en_US')->getPartsIterator();
+var_dump($iterator->current());
+
+$breakIterator = IntlBreakIterator::createWordInstance('en_US');
+$breakIterator->setText('foo');
+$iterator = $breakIterator->getPartsIterator();
+$iterator->rewind();
+$iterator->next();
+$iterator->next();
+var_dump($iterator->valid());
+var_dump($iterator->current());
+?>
+--EXPECT--
+NULL
+bool(false)
+NULL