diff --git a/docs/bootstrap-inventory.md b/docs/bootstrap-inventory.md
index 981149d4b3d..f8aa44ac896 100644
--- a/docs/bootstrap-inventory.md
+++ b/docs/bootstrap-inventory.md
@@ -13161,13 +13161,13 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag
- new grapheme_strrpos (line 354)
- new grapheme_strripos (line 355)
- new grapheme_extract (line 356)
-- new grapheme_levenshtein (line 357)
-- new grapheme_str_split (line 358)
-- new grapheme_strimwidth (line 359)
-- new intl_get_error_code (line 360)
-- new intl_get_error_message (line 361)
-- new intl_is_failure (line 362)
-- new intl_error_name (line 363)
+- new grapheme_levenshtein (line 358)
+- new grapheme_str_split (line 359)
+- new grapheme_strimwidth (line 360)
+- new intl_get_error_code (line 361)
+- new intl_get_error_message (line 362)
+- new intl_is_failure (line 363)
+- new intl_error_name (line 364)
- 4 class method(s)
### `ext/intl/NormalizerGetRawDecomposition.php`
@@ -41044,7 +41044,7 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag
### `lib/CompilerVersion.php`
**Warnings** (review for bootstrap subset):
-- 243 class method(s)
+- 245 class method(s)
### `lib/CurlyBraceOffsetRejector.php`
diff --git a/docs/capabilities.md b/docs/capabilities.md
index c53bffb529c..f374d465516 100644
--- a/docs/capabilities.md
+++ b/docs/capabilities.md
@@ -501,21 +501,8 @@ Auto-generated by `script/capability-matrix.php`. Do not edit by hand.
| `gmp_testbit` | yes | yes | yes | gmp | |
| `gmp_xor` | yes | yes | yes | gmp | |
| `gmstrftime` | yes | yes | yes | standard | JIT PHPT; AOT PHPT |
-| `gnupg_adddecryptkey` | yes | no | no | gnupg | not implemented for JIT in this compiler build (issue #6668) |
-| `gnupg_addencryptkey` | yes | no | no | gnupg | not implemented for JIT in this compiler build (issue #6668) |
-| `gnupg_addsignkey` | yes | no | no | gnupg | not implemented for JIT in this compiler build (issue #6668) |
-| `gnupg_cleardecryptkeys` | yes | no | no | gnupg | not implemented for JIT in this compiler build (issue #6668) |
-| `gnupg_clearencryptkeys` | yes | no | no | gnupg | not implemented for JIT in this compiler build (issue #6668) |
-| `gnupg_clearsignkeys` | yes | no | no | gnupg | not implemented for JIT in this compiler build (issue #6668) |
-| `gnupg_decrypt` | yes | no | no | gnupg | not implemented for JIT in this compiler build (issue #6668) |
-| `gnupg_encrypt` | yes | no | no | gnupg | not implemented for JIT in this compiler build (issue #6668) |
-| `gnupg_geterror` | yes | no | no | gnupg | not implemented for JIT in this compiler build (issue #6668) |
-| `gnupg_init` | yes | no | no | gnupg | not implemented for JIT in this compiler build (issue #6668) |
-| `gnupg_keyinfo` | yes | no | no | gnupg | not implemented for JIT in this compiler build (issue #6668) |
-| `gnupg_sign` | yes | no | no | gnupg | not implemented for JIT in this compiler build (issue #6668) |
-| `gnupg_verify` | yes | no | no | gnupg | not implemented for JIT in this compiler build (issue #6668) |
| `grapheme_extract` | yes | yes | yes | intl | JIT PHPT |
-| `grapheme_levenshtein` | yes | yes | yes | intl | JIT PHPT |
+| `grapheme_levenshtein` | no | no | no | intl | impl present; gated (8.4.0 ref, since 8.4.0); JIT PHPT |
| `grapheme_stripos` | yes | yes | yes | intl | JIT PHPT; AOT PHPT |
| `grapheme_stristr` | yes | yes | yes | intl | |
| `grapheme_strlen` | yes | yes | yes | intl | JIT PHPT |
@@ -667,7 +654,6 @@ Auto-generated by `script/capability-matrix.php`. Do not edit by hand.
| `intltz_get_error_code` | yes | no | no | intl | not implemented for JIT in this compiler build (issue #20925) |
| `intltz_get_error_message` | yes | no | no | intl | not implemented for JIT in this compiler build (issue #20925) |
| `intltz_get_gmt` | yes | no | no | intl | not implemented for JIT in this compiler build (issue #20859) |
-| `intltz_get_iana_id` | yes | no | no | intl | not implemented for JIT in this compiler build (issue #20925) |
| `intltz_get_id` | yes | no | no | intl | not implemented for JIT in this compiler build (issue #20859) |
| `intltz_get_id_for_windows_id` | yes | no | no | intl | not implemented for JIT in this compiler build (issue #20925) |
| `intltz_get_offset` | yes | no | no | intl | not implemented for JIT in this compiler build (issue #20925) |
diff --git a/docs/pages/capability-comparison.html b/docs/pages/capability-comparison.html
index 6beac802ef4..92ae4f403d9 100644
--- a/docs/pages/capability-comparison.html
+++ b/docs/pages/capability-comparison.html
@@ -64,8 +64,8 @@
PHP capability comparison
Stdlib builtins
- 1171 / 1566 full (VM+JIT+AOT)
- 362 partial · 33 gaps
+ 1170 / 1552 full (VM+JIT+AOT)
+ 348 partial · 34 gaps
Legend
@@ -680,21 +680,8 @@ Stdlib builtins
`gmp_testbit` | yes | yes | yes | yes | — |
`gmp_xor` | yes | yes | yes | yes | — |
`gmstrftime` | yes | yes | yes | yes | — |
-`gnupg_adddecryptkey` | yes | yes | no | no | — |
-`gnupg_addencryptkey` | yes | yes | no | no | — |
-`gnupg_addsignkey` | yes | yes | no | no | — |
-`gnupg_cleardecryptkeys` | yes | yes | no | no | — |
-`gnupg_clearencryptkeys` | yes | yes | no | no | — |
-`gnupg_clearsignkeys` | yes | yes | no | no | — |
-`gnupg_decrypt` | yes | yes | no | no | — |
-`gnupg_encrypt` | yes | yes | no | no | — |
-`gnupg_geterror` | yes | yes | no | no | — |
-`gnupg_init` | yes | yes | no | no | — |
-`gnupg_keyinfo` | yes | yes | no | no | — |
-`gnupg_sign` | yes | yes | no | no | — |
-`gnupg_verify` | yes | yes | no | no | — |
`grapheme_extract` | yes | yes | yes | yes | — |
-`grapheme_levenshtein` | yes | yes | yes | yes | — |
+`grapheme_levenshtein` | yes | no | no | no | — |
`grapheme_stripos` | yes | yes | yes | yes | — |
`grapheme_stristr` | yes | yes | yes | yes | — |
`grapheme_strlen` | yes | yes | yes | yes | — |
@@ -846,7 +833,6 @@ Stdlib builtins
`intltz_get_error_code` | yes | yes | no | no | — |
`intltz_get_error_message` | yes | yes | no | no | — |
`intltz_get_gmt` | yes | yes | no | no | — |
-`intltz_get_iana_id` | yes | yes | no | no | — |
`intltz_get_id` | yes | yes | no | no | — |
`intltz_get_id_for_windows_id` | yes | yes | no | no | — |
`intltz_get_offset` | yes | yes | no | no | — |
diff --git a/ext/intl/Module.php b/ext/intl/Module.php
index add1c6c723e..022e097d5c8 100644
--- a/ext/intl/Module.php
+++ b/ext/intl/Module.php
@@ -354,7 +354,8 @@ public function getFunctions(): array
new grapheme_strrpos(),
new grapheme_strripos(),
new grapheme_extract(),
- new grapheme_levenshtein(),
+ // Never shipped by Zend — withhold on every profile (#22661 / #6998).
+ ...(CompilerVersion::supportsGraphemeLevenshtein() ? [new grapheme_levenshtein()] : []),
...(CompilerVersion::supportsGraphemeStrSplit() ? [new grapheme_str_split()] : []),
...(CompilerVersion::supportsGraphemeStrimwidth() ? [new grapheme_strimwidth()] : []),
new intl_get_error_code(),
diff --git a/ext/intl/grapheme_levenshtein.php b/ext/intl/grapheme_levenshtein.php
index 4ebb278788e..e53b40d3486 100644
--- a/ext/intl/grapheme_levenshtein.php
+++ b/ext/intl/grapheme_levenshtein.php
@@ -15,9 +15,10 @@
use PHPLLVM\Value;
/**
- * grapheme_levenshtein() — grapheme-cluster edit distance (php-src ext/intl/grapheme; #6998).
+ * grapheme_levenshtein() — grapheme-cluster edit distance (not shipped by Zend; kept unregistered, #22661).
*
- * VM: {@see VmGrapheme}; JIT: compile-time fold via {@see JitGrapheme} (runtime defer #6998).
+ * Historical experiment (#6998). Zend/php-src has no stub entry (php/php-src#10180).
+ * Internal helper remains at {@see VmGrapheme::levenshtein()} for unit tests.
*/
final class grapheme_levenshtein extends Internal
{
diff --git a/lib/CompilerVersion.php b/lib/CompilerVersion.php
index f54588dea1b..112b7272fe1 100644
--- a/lib/CompilerVersion.php
+++ b/lib/CompilerVersion.php
@@ -1917,6 +1917,25 @@ public static function advertisesGraphemeStrContains(): bool
return false;
}
+ /**
+ * grapheme_levenshtein() — never shipped by Zend/php-src (ext/intl/grapheme/grapheme.stub.php).
+ *
+ * Prior registration (#6998) was wrong-direction: ICU levenshtein RFC never landed in stubs
+ * (php/php-src#10180). Always withheld — #22661.
+ */
+ public static function supportsGraphemeLevenshtein(): bool
+ {
+ return false;
+ }
+
+ /**
+ * grapheme_levenshtein() visible to function_exists() — always false (Zend never ships it; #22661).
+ */
+ public static function advertisesGraphemeLevenshtein(): bool
+ {
+ return false;
+ }
+
/**
* PHP 8.4+ grapheme_strimwidth() (ext/intl/grapheme/grapheme_string.c, issue #9793, #17010).
*
diff --git a/script/capability-syntax-lib.php b/script/capability-syntax-lib.php
index bbf1addcd67..76de116ad35 100644
--- a/script/capability-syntax-lib.php
+++ b/script/capability-syntax-lib.php
@@ -1394,6 +1394,13 @@ function withheldBuiltinGateDefinitions(): array
'module' => 'mbstring',
'relPath' => 'ext/mbstring/mb_ucwords.php',
],
+ [
+ 'names' => ['grapheme_levenshtein'],
+ 'gate' => [PHPCompiler\CompilerVersion::class, 'supportsGraphemeLevenshtein'],
+ 'since' => '8.4.0',
+ 'module' => 'intl',
+ 'relPath' => 'ext/intl/grapheme_levenshtein.php',
+ ],
[
'names' => ['generator_to_array'],
'gate' => [PHPCompiler\CompilerVersion::class, 'supportsGeneratorToArray'],
diff --git a/test/compliance/JITTest.php b/test/compliance/JITTest.php
index cc65dd46a1e..d3fce199302 100755
--- a/test/compliance/JITTest.php
+++ b/test/compliance/JITTest.php
@@ -431,6 +431,16 @@ public static function providePHPTests(): \Generator
&& str_contains($name, 'mb_ucwords_phantom')) {
continue;
}
+ if (!CompilerVersion::supportsGraphemeLevenshtein()
+ && str_contains($name, 'grapheme_levenshtein')
+ && !str_contains($name, 'grapheme_levenshtein_phantom')
+ && !str_contains($name, 'grapheme_levenshtein_forward')) {
+ continue;
+ }
+ if (CompilerVersion::supportsGraphemeLevenshtein()
+ && str_contains($name, 'grapheme_levenshtein_phantom')) {
+ continue;
+ }
if (!CompilerVersion::supportsStreamSupports()
&& (('stdlib/stream_supports' === $name)
|| str_contains($name, 'stream_support_constants')
diff --git a/test/compliance/VMTest.php b/test/compliance/VMTest.php
index 337e0f73c20..8712ea4182f 100755
--- a/test/compliance/VMTest.php
+++ b/test/compliance/VMTest.php
@@ -508,6 +508,16 @@ public static function providePHPTests(): \Generator
&& str_contains($name, 'mb_ucwords_phantom')) {
continue;
}
+ if (!CompilerVersion::supportsGraphemeLevenshtein()
+ && str_contains($name, 'grapheme_levenshtein')
+ && !str_contains($name, 'grapheme_levenshtein_phantom')
+ && !str_contains($name, 'grapheme_levenshtein_forward')) {
+ continue;
+ }
+ if (CompilerVersion::supportsGraphemeLevenshtein()
+ && str_contains($name, 'grapheme_levenshtein_phantom')) {
+ continue;
+ }
// convert_cyr_string / money_format removed in php-src 8.0 (#21481): functional cases use
// PROFILE=7.4 via --ENV--; phantom_* cases assert absence on 8.2/8.4 — always include
// (do not gate on supportsConvertCyrString()/supportsMoneyFormat()).
diff --git a/test/compliance/cases/stdlib/grapheme_levenshtein_forward_profile.phpt b/test/compliance/cases/stdlib/grapheme_levenshtein_forward_profile.phpt
new file mode 100644
index 00000000000..90e53697c67
--- /dev/null
+++ b/test/compliance/cases/stdlib/grapheme_levenshtein_forward_profile.phpt
@@ -0,0 +1,11 @@
+--TEST--
+stdlib grapheme_levenshtein() — phantom on PHP_COMPILER_PROFILE=8.4 (Zend never ships; #22661)
+--ENV--
+PHP_COMPILER_PROFILE=8.4
+--FILE--
+assertFalse(CompilerVersion::supportsGraphemeLevenshtein());
+ $this->assertFalse(CompilerVersion::advertisesGraphemeLevenshtein());
+ } finally {
+ if (false === $prev) {
+ putenv('PHP_COMPILER_PROFILE');
+ } else {
+ putenv('PHP_COMPILER_PROFILE='.$prev);
+ }
+ }
+ }
+
+ public function testGraphemeLevenshteinWithheldOnForwardProfile(): void
+ {
+ $prev = getenv('PHP_COMPILER_PROFILE');
+ putenv('PHP_COMPILER_PROFILE=8.4');
+ try {
+ $this->assertFalse(CompilerVersion::supportsGraphemeLevenshtein());
+ $this->assertFalse(CompilerVersion::advertisesGraphemeLevenshtein());
+ } finally {
+ if (false === $prev) {
+ putenv('PHP_COMPILER_PROFILE');
+ } else {
+ putenv('PHP_COMPILER_PROFILE='.$prev);
+ }
+ }
+ }
+
public function testVmDoesNotRegisterMbStrPadOnDefault84DevReference(): void
{
$runtime = new Runtime();