Commit e1a82e151da for php.net

commit e1a82e151daf114c30c693384e234fe7ad6b7c9d
Author: Weilin Du <weilindu@php.net>
Date:   Fri Aug 7 15:03:05 2026 +0800

    ext/intl: Add IntlDatePatternGenerator skeleton methods (#23078)

    This add missing Skeleton functions similar to #23049 and #23017 for
    IntlDatePatternGenerator. This adds IntlDatePatternGenerator::getSkeleton()
    and IntlDatePatternGenerator::getBaseSkeleton()

    Co-authored-by: David CARLIER <devnexen@gmail.com>

diff --git a/NEWS b/NEWS
index 7bad8e58adc..cc4ff914844 100644
--- a/NEWS
+++ b/NEWS
@@ -23,6 +23,8 @@ PHP                                                                        NEWS
   . Added gmp_powm_sec(). (Weilin Du)

 - Intl:
+  . Added static methods IntlDatePatternGenerator::getSkeleton() and
+    IntlDatePatternGenerator::getBaseSkeleton(). (Weilin Du)
   . Fixed Collator::sort(), collator_sort(), Collator::asort(), and
     collator_asort() to report UTF-8/UTF-16 conversion errors through the intl
     error handler instead of emitting a warning and continuing with an empty
diff --git a/UPGRADING b/UPGRADING
index d6ed37ef49b..25a405e1521 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -327,6 +327,9 @@ PHP 8.6 UPGRADE NOTES
     on official Windows builds using MPIR.

 - Intl:
+  . Added the static methods IntlDatePatternGenerator::getSkeleton() and
+    IntlDatePatternGenerator::getBaseSkeleton() to generate the unique skeleton
+    and base skeleton for a date/time pattern.
   . Added Locale::getDisplayKeyword() and Locale::getDisplayKeywordValue(),
     with the alias of locale_get_display_keyword() and
     locale_get_display_keyword_value() respectively.
@@ -506,6 +509,8 @@ PHP 8.6 UPGRADE NOTES
 - Intl:
   . grapheme_strrev()
     RFC: https://wiki.php.net/rfc/grapheme_strrev
+  . IntlDatePatternGenerator::getSkeleton()
+  . IntlDatePatternGenerator::getBaseSkeleton()
   . Locale::getDisplayKeyword() and Locale::getDisplayKeywordValue()
     RFC: https://wiki.php.net/rfc/getdisplaykeyword_and_getdisplaykeywordvalue
   . SpoofChecker::areBidiConfusable()
diff --git a/ext/intl/dateformat/datepatterngenerator.stub.php b/ext/intl/dateformat/datepatterngenerator.stub.php
index a9ce7c14631..b97e8d9a86a 100644
--- a/ext/intl/dateformat/datepatterngenerator.stub.php
+++ b/ext/intl/dateformat/datepatterngenerator.stub.php
@@ -10,4 +10,8 @@ public function __construct(?string $locale = null) {}
     public static function create(?string $locale = null): ?IntlDatePatternGenerator {}

     public function getBestPattern(string $skeleton): string|false {}
+
+    public static function getSkeleton(string $pattern): string|false {}
+
+    public static function getBaseSkeleton(string $pattern): string|false {}
 }
diff --git a/ext/intl/dateformat/datepatterngenerator_arginfo.h b/ext/intl/dateformat/datepatterngenerator_arginfo.h
index 01debdbe5fa..acf6a469592 100644
Binary files a/ext/intl/dateformat/datepatterngenerator_arginfo.h and b/ext/intl/dateformat/datepatterngenerator_arginfo.h differ
diff --git a/ext/intl/dateformat/datepatterngenerator_methods.cpp b/ext/intl/dateformat/datepatterngenerator_methods.cpp
index f22be1c21fc..24943d4ec9b 100644
--- a/ext/intl/dateformat/datepatterngenerator_methods.cpp
+++ b/ext/intl/dateformat/datepatterngenerator_methods.cpp
@@ -118,7 +118,8 @@ U_CFUNC PHP_METHOD( IntlDatePatternGenerator, getBestPattern )

 	INTL_METHOD_CHECK_STATUS(dtpgo, "Skeleton is not a valid UTF-8 string");

-	UnicodeString skeleton = dtpgo->dtpg->getSkeleton(skeleton_uncleaned, DTPATTERNGEN_ERROR_CODE(dtpgo));
+	UnicodeString skeleton = DateTimePatternGenerator::staticGetSkeleton(
+		skeleton_uncleaned, DTPATTERNGEN_ERROR_CODE(dtpgo));

 	INTL_METHOD_CHECK_STATUS(dtpgo, "Error getting cleaned skeleton");

@@ -132,3 +133,49 @@ U_CFUNC PHP_METHOD( IntlDatePatternGenerator, getBestPattern )

 	RETVAL_STR(u8str);
 }
+
+template <typename F>
+static void dtpg_get_skeleton(INTERNAL_FUNCTION_PARAMETERS, F&& skeletonfn, const char *errmsg)
+{
+	zend_string *pattern_str;
+	UnicodeString pattern;
+	UErrorCode status = U_ZERO_ERROR;
+
+	intl_error_reset(NULL);
+
+	ZEND_PARSE_PARAMETERS_START(1, 1)
+		Z_PARAM_STR(pattern_str)
+	ZEND_PARSE_PARAMETERS_END();
+
+	intl_stringFromChar(pattern, ZSTR_VAL(pattern_str), ZSTR_LEN(pattern_str), &status);
+
+	INTL_CHECK_STATUS(status, "Pattern is not a valid UTF-8 string");
+
+	UnicodeString result = skeletonfn(pattern, status);
+
+	INTL_CHECK_STATUS(status, errmsg);
+
+	zend_string *u8str = intl_charFromString(result, &status);
+
+	INTL_CHECK_STATUS(status, "Error converting result to UTF-8");
+
+	RETVAL_STR(u8str);
+}
+
+U_CFUNC PHP_METHOD(IntlDatePatternGenerator, getSkeleton)
+{
+	dtpg_get_skeleton(INTERNAL_FUNCTION_PARAM_PASSTHRU,
+		[](const UnicodeString &pattern, UErrorCode &status) {
+			return DateTimePatternGenerator::staticGetSkeleton(pattern, status);
+		},
+		"Error getting skeleton");
+}
+
+U_CFUNC PHP_METHOD(IntlDatePatternGenerator, getBaseSkeleton)
+{
+	dtpg_get_skeleton(INTERNAL_FUNCTION_PARAM_PASSTHRU,
+		[](const UnicodeString &pattern, UErrorCode &status) {
+			return DateTimePatternGenerator::staticGetBaseSkeleton(pattern, status);
+		},
+		"Error getting base skeleton");
+}
diff --git a/ext/intl/tests/datepatterngenerator_get_skeleton.phpt b/ext/intl/tests/datepatterngenerator_get_skeleton.phpt
new file mode 100644
index 00000000000..de425fc1667
--- /dev/null
+++ b/ext/intl/tests/datepatterngenerator_get_skeleton.phpt
@@ -0,0 +1,48 @@
+--TEST--
+IntlDatePatternGenerator::getSkeleton() and getBaseSkeleton()
+--EXTENSIONS--
+intl
+--FILE--
+<?php
+
+var_dump(IntlDatePatternGenerator::getSkeleton("dd/MMM"));
+var_dump(IntlDatePatternGenerator::getSkeleton("MMM-dd"));
+var_dump(IntlDatePatternGenerator::getBaseSkeleton("dd/MMM"));
+var_dump(IntlDatePatternGenerator::getBaseSkeleton("MMM-dd"));
+var_dump(IntlDatePatternGenerator::getSkeleton(""));
+var_dump(IntlDatePatternGenerator::getBaseSkeleton(""));
+
+$patterns = [
+    "'at' HH:mm",
+    "y年M月d日",
+    "yyyy-MM-dd",
+    "MMMMM",
+    "MMM",
+    "HH:mm:ss zzz dd/MM/y",
+    "'at HH:mm",
+];
+
+foreach ($patterns as $pattern) {
+    printf(
+        "%s => [%s] [%s]\n",
+        $pattern,
+        IntlDatePatternGenerator::getSkeleton($pattern),
+        IntlDatePatternGenerator::getBaseSkeleton($pattern),
+    );
+}
+
+?>
+--EXPECT--
+string(5) "MMMdd"
+string(5) "MMMdd"
+string(4) "MMMd"
+string(4) "MMMd"
+string(0) ""
+string(0) ""
+'at' HH:mm => [HHmm] [Hm]
+y年M月d日 => [yMd] [yMd]
+yyyy-MM-dd => [yyyyMMdd] [yMd]
+MMMMM => [MMMMM] [MMMMM]
+MMM => [MMM] [MMM]
+HH:mm:ss zzz dd/MM/y => [yMMddHHmmsszzz] [yMdHmsz]
+'at HH:mm => [] []
diff --git a/ext/intl/tests/datepatterngenerator_skeleton_error.phpt b/ext/intl/tests/datepatterngenerator_skeleton_error.phpt
new file mode 100644
index 00000000000..06a351ecd1b
--- /dev/null
+++ b/ext/intl/tests/datepatterngenerator_skeleton_error.phpt
@@ -0,0 +1,18 @@
+--TEST--
+IntlDatePatternGenerator::getSkeleton() and getBaseSkeleton(): errors
+--EXTENSIONS--
+intl
+--FILE--
+<?php
+
+foreach (["getSkeleton", "getBaseSkeleton"] as $method) {
+    var_dump(IntlDatePatternGenerator::$method("dd/MMM\x80"));
+    echo intl_get_error_message(), "\n";
+}
+
+?>
+--EXPECT--
+bool(false)
+IntlDatePatternGenerator::getSkeleton(): Pattern is not a valid UTF-8 string: U_INVALID_CHAR_FOUND
+bool(false)
+IntlDatePatternGenerator::getBaseSkeleton(): Pattern is not a valid UTF-8 string: U_INVALID_CHAR_FOUND