Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 3 additions & 8 deletions lib/VM/EnumCaseSupport.php
Original file line number Diff line number Diff line change
Expand Up @@ -306,9 +306,10 @@ public static function compareEnumCaseEntrySpaceship(EnumCaseEntry $left, EnumCa
}

/**
* Transitive ordering for sort()/asort() on enum case arrays (#5546, php-src array.c php_array_compare_transitive).
* Transitive ordering for sort()/asort() on enum case arrays (#5546, #5691, php-src array.c).
*
* Backed enums: backing scalar spaceship. Unit enums: object handle (spl_object_id order).
* php-src SORT_REGULAR uses stable object-handle order for enum zvals, not backing scalars
* (php_array_data_compare_unstable_i / php_array_compare_transitive, PR #20517).
*/
public static function compareEnumCasesForSort(Variable $left, Variable $right): int
{
Expand All @@ -323,12 +324,6 @@ public static function compareEnumCasesForSort(Variable $left, Variable $right):
if ($leftName === $rightName) {
return 0;
}
if (null !== $leftClass->backedType) {
return Variable::spaceshipCompare(
self::backingValueForMinMax($left),
self::backingValueForMinMax($right)
);
}

return self::objectIdForEnumSort($left) <=> self::objectIdForEnumSort($right);
}
Expand Down
21 changes: 20 additions & 1 deletion test/compliance/cases/stdlib/sort_enum_cases.phpt
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
--TEST--
stdlib sort() on enum case arrays preserves objects (#5546, ext/standard/array.c)
stdlib sort() on enum case arrays preserves objects (#5546, #5691, ext/standard/array.c)
--FILE--
<?php
enum EInt: int { case A = 1; case B = 2; case C = 3; }
enum EUnit { case A; case B; }
enum EStr: string { case A = 'b'; case B = 'a'; }
enum EOrder: int { case C = 3; case A = 1; case B = 2; }

$a = [EInt::C, EInt::A, EInt::B];
sort($a);
Expand All @@ -16,9 +18,26 @@ sort($b);
foreach ($b as $v) {
echo $v->name, ($v instanceof EUnit ? '' : '!'), "\n";
}

$c = [EStr::B, EStr::A];
sort($c);
foreach ($c as $v) {
echo $v->name, ($v instanceof EStr ? '' : '!'), "\n";
}

$d = [EOrder::B, EOrder::C, EOrder::A];
sort($d);
foreach ($d as $v) {
echo $v->name, ($v instanceof EOrder ? '' : '!'), "\n";
}
--EXPECT--
A
B
C
A
B
A
B
A
B
C
34 changes: 34 additions & 0 deletions test/unit/SortEnumCasesBuiltinTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,38 @@ enum EInt: int { case A = 1; case B = 2; }

$this->assertSame('B,A', $output);
}

/** @covers issue #5691 */
public function testStringBackedEnumSortUsesObjectHandleOrder(): void
{
ob_start();
$runtime = new Runtime();
$runtime->run($runtime->parseAndCompile(<<<'PHP'
<?php
enum EStr: string { case A = 'b'; case B = 'a'; }
$a = [EStr::B, EStr::A];
sort($a);
echo $a[0]->name, ',', $a[1]->name;
PHP, 'sort_string_backed_enum.php'));
$output = ob_get_clean();

$this->assertSame('A,B', $output);
}

/** @covers issue #5691 */
public function testIntBackedEnumSortUsesDeclarationHandleNotBackingValue(): void
{
ob_start();
$runtime = new Runtime();
$runtime->run($runtime->parseAndCompile(<<<'PHP'
<?php
enum EOrder: int { case C = 3; case A = 1; case B = 2; }
$a = [EOrder::B, EOrder::C, EOrder::A];
sort($a);
echo implode(',', array_map(fn($v) => $v->name, $a));
PHP, 'sort_enum_declaration_order.php'));
$output = ob_get_clean();

$this->assertSame('C,A,B', $output);
}
}