array_unique() with SORT_REGULAR returns duplicate values
Nadie ha tomado este issue todavía.
- Lenguaje dominante
- C
- Estrellas
- 40.4k
- Forks
- 8.1k
- Merge medio
- 2 d 13 h
- PR fusionados (30 d)
- 96
Descripción
Description
The following code:
<?php
$units = ['5', '10', '5', '3A', '5', '5'];
$unique = array_unique($units, SORT_REGULAR);
print_r($unique);
Resulted in this output:
Array
(
[0] => 5
[1] => 10
[3] => 3A
[4] => 5
)
But I expected this output instead:
Array
(
[0] => 5
[1] => 10
[3] => 3A
)
Demonstrations:
- simple array of strings: https://3v4l.org/M5lcP
- array of objects: https://3v4l.org/5kr1t
- array of arrays: https://3v4l.org/0HVoV
Root Cause
The algorithm:
- Sort array using comparison function from
php_get_data_compare_func_unstable() - Walk through sorted array comparing only adjacent elements
- Delete duplicates when adjacent elements compare equal
The bug:
SORT_REGULAR uses zend_compare() which calls zendi_smart_strcmp() for string comparisons. This function has non-transitive behavior when mixing numeric and non-numeric strings:
"5" < "10"→ true (numeric comparison: 5 < 10)"10" < "3A"→ true (lexicographic: "1" < "3")"3A" < "5"→ true (lexicographic: "3" < "5") Creates a cycle!
Because the comparison is non-transitive, sorting algorithms (which require transitive comparisons) produce inconsistent results depending on input order.
The deduplication walks through comparing adjacent elements:
lastkept = position_0; // "5"
position_1 "10" != "5" → keep, lastkept = position_1
position_2 "10" == "10" → delete
position_3 "3A" != "10" → keep, lastkept = position_3
position_4 "5" != "3A" → keep ← Bug! Never compared to position_0
position_5 "5" == "5" → delete
The root issue: Non-transitive comparisons break the sorting algorithm's guarantee that equal values will be grouped together. The adjacent-only comparison is correct - but it requires the array to be properly sorted first, which requires transitive comparisons.
Comparison with SORT_STRING
<?php
$units = ['5', '10', '5', '3A', '5', '5'];
echo count(array_unique($units, SORT_REGULAR)) . "\n"; // 4 ✗ Wrong
echo count(array_unique($units, SORT_STRING)) . "\n"; // 3 ✓ Correct
SORT_STRING uses lexical comparison without numeric extraction, so duplicates stay grouped.
Workaround
For simple arrays of scalar values, you can use array_unique with default SORT_STRING flag.
<?php
$unique = array_unique($array, SORT_STRING);
For arrays or objects.
$uniqueAddr = [];
foreach ($addresses as $addr) {
if (! in_array($addr, $uniqueAddr)) {
$uniqueAddr[] = $addr;
}
}
PHP Version
PHP 8.4.13 (cli) (built: Sep 26 2025 00:45:36) (NTS clang 15.0.0)
Copyright (c) The PHP Group
Built by Laravel Herd
Zend Engine v4.4.13, Copyright (c) Zend Technologies
with Zend OPcache v8.4.13, Copyright (c), by Zend Technologies
Guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Línea de trabajo
Comienza reproduciendo el ejemplo proporcionado de array_unique() y, a continuación, rastrea php_get_data_compare_func_unstable(), zend_compare() y zendi_smart_strcmp(), tal como se describe en el issue. Comprueba las demostraciones enlazadas y el comportamiento existente de SORT_REGULAR y SORT_STRING. La tarea estará terminada cuando el caso de duplicado indicado esté cubierto por una prueba de regresión y el comportamiento de comparación elegido sea coherente sin romper otros modos.
Escrito por el modelo de indexación a partir del texto del issue.
Evaluación
- Stack tecnológico
- c, php
- Área
- backend
- Tipo de issue
- Error
- Dificultad
- 5/5
- Tiempo estimado
- Más de una semana
- Estado de actividad
- Estancado
- Claridad
- Bastante claro
- Aptitud para principiantes
- 38/100