Case-insensitive substr_compare, ksort, krsort are still locale-dependent
Maintainer antworten meist innerhalb von 1 Tag
@lazerg arbeitet bereits daran.
Seit 22.10.2026.
Bewertung
- Schwierigkeit
- 2/5
- Geschätzter Aufwand
- 1-3 Stunden
- Anfängerfreundlichkeit
- 73/100
Rechercherichtung
Beginne beim Aufruf von substr_compare() zu zend_binary_strncasecmp_l() und beim Vergleichsoperator für case-insensitive Array-Schlüssel php_array_key_compare_string_case_unstable_i() in php-src; das Issue nennt beide Einstiegspunkte und die Ersatzvergleichsfunktionen. Prüfe die bestehenden Tests für substr_compare(), ksort() und krsort() mit SORT_STRING | SORT_FLAG_CASE und ergänze anschließend Regressionstests für die türkische Locale. Fertig ist die Arbeit, wenn sich diese Fälle wie das beschriebene ASCII-Verhalten ohne Beachtung der Groß- und Kleinschreibung verhalten und die bestehenden Tests bestehen.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Beschreibung
Description
The following code:
<?php
foreach (['C', 'tr_TR.UTF-8'] as $locale) {
if (setlocale(LC_CTYPE, $locale) === false) {
throw new RuntimeException("Locale unavailable: $locale");
}
$values = ['I', 'h'];
sort($values, SORT_STRING | SORT_FLAG_CASE);
$keys = ['I' => 0, 'h' => 0];
ksort($keys, SORT_STRING | SORT_FLAG_CASE);
echo $locale, "\n";
echo ' stripos: ', var_export(stripos('I', 'i'), true), "\n";
echo ' strncasecmp: ', strncasecmp('I', 'i', 1), "\n";
echo ' substr_compare: ', substr_compare('I', 'i', 0, 1, true), "\n";
echo ' sort: ', implode(',', $values), "\n";
echo ' ksort: ', implode(',', array_keys($keys)), "\n";
}
Resulted in this output:
C
stripos: 0
strncasecmp: 0
substr_compare: 0
sort: h,I
ksort: h,I
tr_TR.UTF-8
stripos: 0
strncasecmp: 0
substr_compare: -32
sort: h,I
ksort: I,h
But I expected this output instead:
C
stripos: 0
strncasecmp: 0
substr_compare: 0
sort: h,I
ksort: h,I
tr_TR.UTF-8
stripos: 0
strncasecmp: 0
substr_compare: 0
sort: h,I
ksort: h,I
PHP 8.2 switched strtolower(), stripos(), sort() with SORT_STRING | SORT_FLAG_CASE # and related functions to ASCII case conversion, but substr_compare() with $case_insensitive = true and ksort()/krsort() with SORT_STRING | SORT_FLAG_CASE still follow LC_CTYPE.
The output above is from PHP 8.2.35, 8.3.36, 8.4.27 and 8.5.12. PHP 8.1.34 prints the same, except that stripos() and sort() also follow the locale (false and I,h). 8.2 changed those two but not substr_compare() or ksort().
Reproducing on PHP 8.1 to 8.5 with Docker
The official php:*-cli images have no tr_TR.UTF-8 locale, so each run generates it first. With the snippet above saved as repro.php:
for v in 8.1 8.2 8.3 8.4 8.5; do
docker run --rm -i "php:$v-cli" sh -c '
apt-get update -qq >/dev/null &&
apt-get install -y -qq locales >/dev/null 2>&1 &&
localedef -i tr_TR -f UTF-8 tr_TR.UTF-8 &&
php -v | head -n 1 &&
php' < repro.php
done
The 8.2 UPGRADING notes say sorting with SORT_FLAG_CASE uses ASCII case conversion, and the strtolower-ascii RFC says case-insensitive substr_compare() already did.
On master, substr_compare() calls zend_binary_strncasecmp_l(), and php_array_key_compare_string_case_unstable_i(), which ksort() and krsort() use, calls zend_binary_strcasecmp_l().
Switching these two call sites to zend_binary_strncasecmp() and zend_binary_strcasecmp() would give the documented behavior.
See https://github.com/php/php-src/pull/7506.
PHP Version
PHP 8.5.12 (cli) (built: Oct 21 2026 15:53:40) (NTS)
Copyright (c) The PHP Group
Built by https://github.com/docker-library/php
Zend Engine v4.5.12, Copyright (c) Zend Technologies
with Zend OPcache v8.5.12, Copyright (c), by Zend Technologies
Operating System
Debian GNU/Linux 13 (trixie), glibc 2.41 (php:8.5-cli Docker image)
- Vorherrschende Sprache
- C
- Sterne
- 40.7k
- Forks
- 8.3k
- Ø Merge
- 2 T. 15 Std.
- Gemergte PRs (30 T.)
- 155
Entwicklungsumgebung
- Kein Dockerfile und keine Docker-Compose-Datei
- Keine Pull-Request-Vorlage
- Beitragsleitfaden lesen
Erste Schritte
- Lesen Sie das ganze Issue und danach den Beitragsleitfaden des Projekts.
- Schreiben Sie ins Issue, dass Sie es übernehmen — das erspart doppelte Arbeit.
- Forken Sie das Repository und arbeiten Sie in einem Branch.
- Öffnen Sie einen Pull Request, der die Issue-Nummer nennt.
Mehr aus php/php-src
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 72/100
Maintainer antworten meist innerhalb von 1 Tag
-
HTTP wrapper leaks Location buffer when an empty Location header is followed by another Location headerEvtl. vergeben @bercedev hat das vor 2 Tagen übernommen. OffenBug Category: Streams Status: Verified
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 78/100
Maintainer antworten meist innerhalb von 1 Tag
-
`Time\Duration::__unserialize()` accepts a "negative zero" payload.Evtl. vergeben @TimWolla hat das heute übernommen. OffenBug Status: Needs Triage
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 71/100
php/php-src#24505 · 2 Kommentare · 1 zugewiesene Person ·
Maintainer antworten meist innerhalb von 1 Tag
-
master: part of the mbstring bailout fix got lost in a mergeEvtl. vergeben @lazerg hat das vor 2 Tagen übernommen. Offen
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 78/100
Maintainer antworten meist innerhalb von 1 Tag
-
Bug Status: Needs Triage
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 64/100
Maintainer antworten meist innerhalb von 1 Tag
Ähnliche Issues
-
category:port-update
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 66/100
Maintainer antworten meist innerhalb von 2 Tagen
-
emerge failure MARK mark-unstable media-kit
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 82/100
macaroni-os/mark-issues#834 ·
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 76/100
NVIDIA/open-gpu-kernel-modules#1479 · 2 Kommentare ·
Maintainer antworten meist innerhalb von 1 Tag
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 76/100
-
Schwierigkeit 1/5 Unter einer Stunde Anfängerfreundlichkeit 93/100
webarkit/WebARKitLib#107 ·
Maintainer antworten meist innerhalb von 1 Tag