====== PHP RFC: SORT_STRICT Flag ====== * Version: 0.1 * Date: 2025-11-29 * Author: Jason Marble, jmarble@intuitivetechnology.com * Status: Draft * Implementation: https://github.com/php/php-src/pull/20683 ===== Introduction ===== This RFC proposes ''SORT_STRICT'', a new flag that enables deterministic, type-aware sorting without sacrificing PHP's flexibility with mixed-type arrays. PHP was famously created as a loosely typed language, prioritizing flexibility and ease of use. However, the introduction of strict types in PHP 7 marked a massive paradigm shift for the language. Over the last decade, the ecosystem has matured significantly; major frameworks like Symfony and Laravel, along with modern coding standards like PER, have effectively pushed strict typing from an optional feature to a de facto standard for professional PHP development. Despite this evolution, there is no native way to sort or filter arrays without type coercion. The ''sort()'' family defaults to ''SORT_REGULAR'' (loose equality), while ''array_unique()'' defaults to ''SORT_STRING'' (string casting), neither of which distinguishes between values of different types. This creates a friction point where modern application logic is strict, but utility functions remain loose. Currently, developers wishing to filter unique values strictly or sort mixed-type arrays deterministically must implement userspace closures using ''usort'' or specific loops. This is verbose, slower than internal C implementations, and prone to error. ===== Proposal ===== Add a new constant ''SORT_STRICT'' that can be passed to the following functions: * **Value Sorting**: ''sort()'', ''rsort()'' * **Associative Value Sorting**: ''asort()'', ''arsort()'' * **Key Sorting**: ''ksort()'', ''krsort()'' * **Multi-Array Sorting**: ''array_multisort()'' * **Uniqueness**: ''array_unique()'' ==== Comparison Logic: The "Strict Total Order" ==== ''SORT_STRICT'' implements a two-step comparison: === Step 1: Compare Types === Values are first compared by their type according to this hierarchy: NULL < Bool < Int < Float < String < Array < Object < Resource This ordering progresses from simple to complex types, aligning with PHP's internal type representation. If types differ, the comparison is decided here. No type coercion occurs. === Step 2: Compare Values === If types are identical, standard PHP comparison semantics apply: ^ Type ^ Comparison Method ^ | NULL | Always equal | | Bool | ''false < true'' | | Int | Numeric comparison | | Float | Numeric comparison | | String | Binary comparison (''strcmp'') | | Array | Standard array comparison, with ''SORT_STRICT'' applied recursively to elements | | Object | Standard object comparison, with ''SORT_STRICT'' applied recursively to properties of same-class objects (enums are compared by identity) | | Resource | Resource ID comparison | ==== Differences from SORT_REGULAR ==== ''SORT_STRICT'' applies no type coercion at any level: - Different types are ordered by type, never compared by value. - Same-type values are compared directly, without coercion (e.g., strings lexicographically, not numerically). The following table illustrates common scenarios where ''SORT_STRICT'' provides deterministic results versus ''SORT_REGULAR'': ^ Input Array ^ SORT_REGULAR Result ^ SORT_STRICT Result ^ Reason ^ | ''[1, "1"]'' | Equal (treats as duplicates) | Distinct (Int vs String) | Different types are not equal | | ''["10", "2"]'' | ''["2", "10"]'' (Numeric) | ''["10", "2"]'' (Lexicographic) | Strings are not cast to ints | | ''["1e3", "1000"]'' | Equal (treats as duplicates) | ''["1000", "1e3"]'' (Lexicographic) | No numeric string coercion | | ''[null, false]'' | Equal (treats as duplicates) | Distinct (Null vs Bool) | Different types are not equal | ==== Examples ==== === array_unique(): Preserving Type-Distinct Values === Prevent data loss when filtering arrays containing distinct values that loose comparison treats as duplicates: $values = [0, "0", null, false, ""]; var_dump(array_unique($values, SORT_REGULAR)); // 3 values lost to type coercion // array(2) { // [0]=> int(0) // [4]=> string(0) "" // } var_dump(array_unique($values, SORT_STRICT)); // All 5 values preserved // array(5) { // [0]=> int(0) // [1]=> string(1) "0" // [2]=> NULL // [3]=> bool(false) // [4]=> string(0) "" // } === sort(): Type-Aware Ordering === Sort mixed-type arrays with deterministic, type-based ordering: $values = ["1", true, 1, null, 1.0]; sort($values, SORT_STRICT); var_dump($values); // Sorted by type hierarchy: NULL < Bool < Int < Float < String // array(5) { // [0]=> NULL // [1]=> bool(true) // [2]=> int(1) // [3]=> float(1.0) // [4]=> string(1) "1" // } === sort(): Order-Independent Value Matching === Compare arrays for identical values regardless of order: $expected = [1, "1", true]; $actual = [true, 1, "1"]; sort($expected, SORT_REGULAR); sort($actual, SORT_REGULAR); var_dump($expected === $actual); // bool(false) - result depends on input order sort($expected, SORT_STRICT); sort($actual, SORT_STRICT); var_dump($expected === $actual); // bool(true) - consistent type-based ordering ===== Backward Incompatible Changes ===== None. This RFC adds a new constant and does not modify existing behavior. ===== Proposed PHP Version(s) ===== Next PHP 8.x ===== RFC Impact ===== ==== To the Ecosystem ==== IDEs, static analyzers, and language servers will need to recognize the new ''SORT_STRICT'' constant. No breaking changes are expected. ==== To Existing Extensions ==== None. ==== To SAPIs ==== None. ===== Open Issues ===== None currently. ===== Future Scope ===== The ''array_diff()'' and ''array_intersect()'' families currently hardcode string comparison (''SORT_STRING'') with no way to change the comparison mode. A future RFC could add a flags parameter to these functions, exposing all sort flags including ''SORT_STRICT'': * ''array_diff(array $array, array ...$arrays, int $flags = SORT_STRING)'' * ''array_intersect(array $array, array ...$arrays, int $flags = SORT_STRING)'' This would enable: // Current behavior (string comparison) array_diff([1, 2], ["1", "3"]); // [2] - int 1 matches string "1" // With SORT_STRICT array_diff([1, 2], ["1", "3"], SORT_STRICT); // [1, 2] - no matches, different types // With SORT_NUMERIC array_diff(["10", "2"], [2, 10], SORT_NUMERIC); // [] - all match numerically ===== Voting Choices ===== Primary vote requiring a 2/3 majority: * Yes * No ===== Patches and Tests ===== WIP ===== Implementation ===== TODO: After acceptance. ===== References ===== * [[https://github.com/php/php-src/pull/7806|PR #7806: Add SORT_STRICT option to array_unique() (C. Scott Ananian)]] (2022) * [[https://github.com/php/php-src/issues/10526|Issue #10526: array_unique: add way to compare items with an identity check]] (2023) * [[https://github.com/php/php-src/pull/10567|PR #10567: Implement SORT_STRICT flag (Niels Dossche)]] (2023) * [[https://github.com/php/php-src/issues/20262|Issue #20262: array_unique() with SORT_REGULAR returns duplicate values]] (2025) ===== Rejected Features ===== None yet. ===== Changelog ===== * 2025-11-29: Initial draft