Commit 371d460a3c3 for php

commit 371d460a3c3e6beacaf21321955b7da97aafeb4f
Author: Weilin Du <weilindu@php.net>
Date:   Sun Nov 8 01:26:01 2026 +0800

    ext/intl: Implement Collator::getRules() (#25222)

    This expose ucol_getRules to userland as Collator::getRules().

diff --git a/NEWS b/NEWS
index 30fdbb91de1..81a3b5ebd55 100644
--- a/NEWS
+++ b/NEWS
@@ -25,6 +25,7 @@ PHP                                                                        NEWS
     throwing a ValueError. (Marc Bennewitz)

 - Intl:
+  . Added Collator::getRules() and collator_get_rules(). (Weilin Du)
   . Added IntlRelativeDateTimeFormatter for locale-aware relative date and time
     formatting, with enums for styles, capitalization contexts, and units.
     (Weilin Du)
diff --git a/UPGRADING b/UPGRADING
index d91fda65368..3867497fa9c 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -124,6 +124,11 @@ PHP 8.7 UPGRADE NOTES
   . Added Transliterator::toRules() and transliterator_to_rules() to export
     rules that can be used to recreate a transliterator. The optional
     $escapeUnprintable argument escapes unprintable characters when true.
+  . Added Collator::getRules() and collator_get_rules() to retrieve collation
+    tailoring rules as a UTF-8 string. These describe locale-specific adjustments
+    to the root collation order; the full root/UCA rules are not included. An
+    empty string is returned if no tailoring rules are available, or false if
+    the rules cannot be converted to UTF-8.

 ========================================
 7. New Classes and Interfaces
diff --git a/ext/intl/collator/collator.stub.php b/ext/intl/collator/collator.stub.php
index e41684c691e..f6f465ede69 100644
--- a/ext/intl/collator/collator.stub.php
+++ b/ext/intl/collator/collator.stub.php
@@ -137,6 +137,9 @@ public function setStrength(int $strength): true {}
      */
     public function getLocale(int $type): string|false {}

+    /** @alias collator_get_rules */
+    public function getRules(): string|false {}
+
     /**
      * @tentative-return-type
      * @alias collator_get_error_code
diff --git a/ext/intl/collator/collator_arginfo.h b/ext/intl/collator/collator_arginfo.h
index bf8345cd8be..837249cb9ed 100644
Binary files a/ext/intl/collator/collator_arginfo.h and b/ext/intl/collator/collator_arginfo.h differ
diff --git a/ext/intl/collator/collator_locale.cpp b/ext/intl/collator/collator_locale.cpp
index 2d346f439c9..d09dba29a0a 100644
--- a/ext/intl/collator/collator_locale.cpp
+++ b/ext/intl/collator/collator_locale.cpp
@@ -59,3 +59,28 @@ U_CFUNC PHP_FUNCTION( collator_get_locale )
 	RETVAL_STRINGL( locale_name, strlen(locale_name) );
 }
 /* }}} */
+
+/* {{{ Gets the collation tailoring rules. */
+U_CFUNC PHP_FUNCTION(collator_get_rules)
+{
+	COLLATOR_METHOD_INIT_VARS
+
+	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O",
+			&object, Collator_ce_ptr) == FAILURE) {
+		RETURN_THROWS();
+	}
+
+	COLLATOR_METHOD_FETCH_OBJECT;
+
+	if (collator_check_initialized(co) == FAILURE) {
+		RETURN_THROWS();
+	}
+
+	int32_t rules_len;
+	const UChar *rules = ucol_getRules(co->ucoll, &rules_len);
+	zend_string *result = intl_convert_utf16_to_utf8(rules, rules_len, COLLATOR_ERROR_CODE_P(co));
+	COLLATOR_CHECK_STATUS(co, "Error converting collation rules to UTF-8");
+
+	RETURN_STR(result);
+}
+/* }}} */
diff --git a/ext/intl/php_intl.stub.php b/ext/intl/php_intl.stub.php
index a6a519e50f6..e132a4f670c 100644
--- a/ext/intl/php_intl.stub.php
+++ b/ext/intl/php_intl.stub.php
@@ -308,6 +308,8 @@ function collator_asort(Collator $object, array &$array, int $flags = Collator::

 function collator_get_locale(Collator $object, int $type): string|false {}

+function collator_get_rules(Collator $object): string|false {}
+
 function collator_get_error_code(Collator $object): int|false {}

 function collator_get_error_message(Collator $object): string|false {}
diff --git a/ext/intl/php_intl_arginfo.h b/ext/intl/php_intl_arginfo.h
index a1b95671d27..a360a0be99e 100644
Binary files a/ext/intl/php_intl_arginfo.h and b/ext/intl/php_intl_arginfo.h differ
diff --git a/ext/intl/tests/collator_get_rules.phpt b/ext/intl/tests/collator_get_rules.phpt
new file mode 100644
index 00000000000..b23af2cc622
--- /dev/null
+++ b/ext/intl/tests/collator_get_rules.phpt
@@ -0,0 +1,26 @@
+--TEST--
+Collator::getRules() returns collation tailoring rules as UTF-8
+--EXTENSIONS--
+intl
+--FILE--
+<?php
+$root = new Collator('root');
+var_dump($root->getRules());
+var_dump(collator_get_rules($root));
+
+$collator = new Collator('de_DE@collation=phonebook');
+$rules = $collator->getRules();
+var_dump(is_string($rules) && $rules !== '');
+var_dump(preg_match('/\x{00E4}/u', $rules));
+var_dump(collator_get_rules($collator) === $rules);
+
+unset($collator);
+var_dump(preg_match('/\x{00E4}/u', $rules));
+?>
+--EXPECT--
+string(0) ""
+string(0) ""
+bool(true)
+int(1)
+bool(true)
+int(1)
diff --git a/ext/intl/tests/collator_get_rules_error.phpt b/ext/intl/tests/collator_get_rules_error.phpt
new file mode 100644
index 00000000000..121b8fa0de8
--- /dev/null
+++ b/ext/intl/tests/collator_get_rules_error.phpt
@@ -0,0 +1,38 @@
+--TEST--
+Collator::getRules() resets errors and rejects unconstructed objects
+--EXTENSIONS--
+intl
+--FILE--
+<?php
+class UnconstructedCollator extends Collator {
+    public function __construct() {}
+}
+
+foreach ([fn($c) => $c->getRules(), collator_get_rules(...)] as $getRules) {
+    $collator = new Collator('root');
+    var_dump($collator->getAttribute(-1));
+    var_dump(intl_get_error_code() === U_ILLEGAL_ARGUMENT_ERROR);
+    var_dump($getRules($collator));
+    var_dump(intl_get_error_message());
+    var_dump($collator->getErrorMessage());
+
+    try {
+        $getRules(new UnconstructedCollator());
+    } catch (Error $e) {
+        echo $e->getMessage(), "\n";
+    }
+}
+?>
+--EXPECT--
+bool(false)
+bool(true)
+string(0) ""
+string(12) "U_ZERO_ERROR"
+string(12) "U_ZERO_ERROR"
+Object not initialized
+bool(false)
+bool(true)
+string(0) ""
+string(12) "U_ZERO_ERROR"
+string(12) "U_ZERO_ERROR"
+Object not initialized