Skip to content

Conversation

@jmarble
Copy link

@jmarble jmarble commented Nov 18, 2025

Summary

Fixes #20262 by making SORT_REGULAR fall back to a fully transitive comparator whenever loose comparisons would otherwise be non-transitive (e.g. numeric strings vs numeric zvals). That keeps duplicates grouped so array_unique() and the sort family behave consistently.

Highlights

  • Introduce shared zend comparison helpers (long/double/string) and reuse them across both Zend and ext/standard to avoid duplicating the transitive logic.
  • Add transitivity detection plus a dedicated php_array_sort_regular() fast path so only the arrays that need this comparator pay for it.
  • Rework the sort entry points and array_unique() to call the fast path automatically when required.
  • Negligible performance regressions, with measurable gains of up to ~1.7x for sort() and array_unique()

Introduces localized transitive comparators (including array/object
recursion and HashTable helpers) so SORT_REGULAR sorting and
array_unique() can enforce consistent ordering without touching
zend_compare(). The sort entry points now detect when mixed numeric
string semantics are needed and delegate to php_array_sort_regular(),
while array_unique() switches to the same comparator when required.

Fixes: phpGH-20262
Implement scalar/string fast paths and enum/object ordering rules so
both regular and transitive SORT_REGULAR comparators skip redundant
zend_compare() calls. Introduce php_array_compare_non_numeric_strings()
and dedicated transitive string/number helpers to minimize numeric
parsing.
Avoid temporary zend_string allocations in `compare_long_to_string()`
and `compare_double_to_string()` by using stack buffers, and streamline
`zendi_smart_strcmp()` with non-numeric short-circuits plus single-pass
numeric parsing to cut redundant work in general comparisons.
- Introduce the transitive-aware compare_long/double/string helpers in
  Zend.
- Switch array sorts and array_unique to use them and adjust
  php_hash_values_need_transitivity().
Copy link
Member

@Girgias Girgias left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Various comments and questions and this needs a rebase as I refactored the sorting code to remove a bunch of duplication.

@jmarble
Copy link
Author

jmarble commented Nov 18, 2025

@Girgias thank you for taking the time to provide the careful review! Looks like I was able to capture your sorting code refactor when I created this new branch. I'll push a fresh commit what I addressed in your code comments. Thanks again for the help!

- Call zend_compare_symbol_tables() for array pairs and keep the
  enum-specific path without the ZEND_UNCOMPARABLE check.
- Point the regular key/data variants directly at their impls via
  DEFINE_SORT_VARIANTS_USING(), retaining
  php_array_hash_compare_transitive() as the required compare_func_t
  adapter.
- Make php_hash_has_string_keys() const-qualified and keep
  php_array_compare_transitive() for nested arrays/objects/enums while
  scalar fast paths stay in the main comparator.
@jmarble jmarble marked this pull request as ready for review November 18, 2025 19:49
@jmarble jmarble requested a review from bukka as a code owner November 18, 2025 19:49
@jmarble jmarble requested a review from Girgias November 18, 2025 19:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

array_unique() with SORT_REGULAR returns duplicate values

2 participants