Case-insensitive substr_compare, ksort, krsort are still locale-dependent
Mantenedores costumam responder em até 1 dia
@lazerg já está trabalhando nisso.
Desde 22/10/2026.
Avaliação
- Dificuldade
- 2/5
- Tempo estimado
- 1-3 horas
- Facilidade para iniciantes
- 73/100
Direção de pesquisa
Comece pela chamada de substr_compare() a zend_binary_strncasecmp_l() e pelo comparador de chaves de array que não diferencia maiúsculas de minúsculas php_array_key_compare_string_case_unstable_i() no php-src; a issue identifica ambos os pontos de entrada e as funções de comparação substitutas. Confira os testes existentes de substr_compare(), ksort() e krsort() com SORT_STRING | SORT_FLAG_CASE e, em seguida, adicione testes de regressão para a localidade turca. O trabalho estará concluído quando esses casos corresponderem ao comportamento ASCII descrito, que não diferencia maiúsculas de minúsculas, e os testes existentes passarem.
Escrita pelo modelo de indexação a partir do texto da issue.
Descrição
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)
- Linguagem predominante
- C
- Estrelas
- 40.7k
- Forks
- 8.3k
- Merge médio
- 2d 15h
- PRs com merge (30d)
- 155
Preparar o ambiente
- Sem Dockerfile nem arquivo Docker Compose
- Sem modelo de pull request
- Ler o guia de contribuição
Primeiros passos
- Leia a issue inteira e depois o guia de contribuição do projeto.
- Comente na issue dizendo que vai assumir — evita que duas pessoas façam o mesmo trabalho.
- Faça um fork do repositório e trabalhe em uma branch.
- Abra um pull request que referencie o número da issue.
Mais de php/php-src
-
Bug Status: Needs Triage
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 78/100
Mantenedores costumam responder em até 1 dia
-
Bug Status: Needs Triage
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 71/100
php/php-src#24505 · 2 comentários ·
Mantenedores costumam responder em até 1 dia
-
master: part of the mbstring bailout fix got lost in a mergeTalvez já em andamento @lazerg assumiu há 1 dia. Aberta
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 78/100
Mantenedores costumam responder em até 1 dia
-
Bug Status: Needs Triage
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 64/100
Mantenedores costumam responder em até 1 dia
-
quoted_printable_encode() produces lines longer than 76 characters for bytes 0xF5 to 0xFFTalvez já em andamento Um pull request vinculado a esta issue está aberto ou já foi mesclado. Aberta
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 76/100
Mantenedores costumam responder em até 1 dia
Todas as issues de php/php-src
Issues semelhantes
-
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 76/100
polhenarejos/pico-fido#302 ·
-
开发板墙缺少 ESP32_S31_MOSAICOAberta
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 67/100
espressif/esp-vision#16 · 1 comentário ·
-
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 74/100
libsdl-org/SDL#16577 ·
Mantenedores costumam responder em até 1 dia
-
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 66/100
Mantenedores costumam responder em até 1 dia
-
halt_error adds a trailing newline for non-string input, contrary to the manualTalvez já em andamento @Huang-404-Q assumiu hoje. Aberta
Dificuldade 1/5 1-3 horas Facilidade para iniciantes 88/100
Mantenedores costumam responder em até 2 dias