Skip to content

Commit 015e2a5

Browse files
committed
sort, sortBy: reject SORT_ASC/SORT_DESC with a clear error, document flags
Sorting is always ascending, but the parameter is named $flags and PHP sort flags include direction constants, so SORT_DESC was the obvious guess for descending. sortBy(SORT_DESC) fataled with a confusing array_multisort() TypeError naming library internals, and sort(SORT_DESC) silently sorted with an undefined flag. Both now throw InvalidArgumentException saying sorting is always ascending and pointing at SQL ORDER BY ... DESC. Both docblocks list every comparison flag as bullets with SORT_ASC/SORT_DESC marked as throwing.
1 parent 6e1cd5f commit 015e2a5

2 files changed

Lines changed: 47 additions & 1 deletion

File tree

src/SmartArrayBase.php

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -389,12 +389,24 @@ public function contains(mixed $value): bool
389389
//region Sorting & Filtering
390390

391391
/**
392-
* Returns a new SmartArray sorted by values, using PHP sort() function.
392+
* Returns a new SmartArray sorted ascending by values, using PHP sort() function.
393393
* Only works on flat arrays (throws on nested).
394+
*
395+
* $flags sets how values compare, not the direction - sorting is always ascending:
396+
* - SORT_REGULAR - default, PHP's normal comparison rules (numbers before strings)
397+
* - SORT_NUMERIC - compare as numbers
398+
* - SORT_STRING - compare as strings
399+
* - SORT_NATURAL - natural order for embedded numbers: "item10" sorts after "item9"
400+
* - SORT_LOCALE_STRING - compare as strings using the current locale
401+
* - SORT_FLAG_CASE - case-insensitive, combined with a string flag: SORT_NATURAL|SORT_FLAG_CASE
402+
* - SORT_ASC/SORT_DESC - throws: direction constants, not comparison flags
394403
*/
395404
public function sort(int $flags = SORT_REGULAR): static
396405
{
397406
$this->assertFlatArray();
407+
if ($flags === SORT_ASC || $flags === SORT_DESC) {
408+
throw new InvalidArgumentException("sort(): sorting is always ascending, SORT_ASC/SORT_DESC are directions, not comparison flags. For descending, sort in SQL with ORDER BY ... DESC");
409+
}
398410

399411
$sorted = $this->toArray();
400412
sort($sorted, $flags);
@@ -410,10 +422,22 @@ public function sort(int $flags = SORT_REGULAR): static
410422
*
411423
* Numeric row keys are re-indexed; string keys are preserved
412424
* (array_multisort() default behavior).
425+
*
426+
* $flags sets how field values compare, not the direction - sorting is always ascending:
427+
* - SORT_REGULAR - default, PHP's normal comparison rules (numbers before strings)
428+
* - SORT_NUMERIC - compare as numbers
429+
* - SORT_STRING - compare as strings
430+
* - SORT_NATURAL - natural order for embedded numbers: "item10" sorts after "item9"
431+
* - SORT_LOCALE_STRING - compare as strings using the current locale
432+
* - SORT_FLAG_CASE - case-insensitive, combined with a string flag: SORT_NATURAL|SORT_FLAG_CASE
433+
* - SORT_ASC/SORT_DESC - throws: direction constants, not comparison flags
413434
*/
414435
public function sortBy(string $field, int $flags = SORT_REGULAR): static
415436
{
416437
$this->assertNestedArray();
438+
if ($flags === SORT_ASC || $flags === SORT_DESC) {
439+
throw new InvalidArgumentException("sortBy(): sorting is always ascending, SORT_ASC/SORT_DESC are directions, not comparison flags. For descending, sort in SQL with ORDER BY ... DESC");
440+
}
417441
$this->warnIfMissing($field);
418442

419443
// sort by field value, treating missing fields as null (?? also covers non-array rows in mixed data)

tests/Unit/FilterUniqueSortTest.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,17 @@ public function testSortOnNestedThrows(string $class): void
134134
$class::new([['a' => 1]])->sort();
135135
}
136136

137+
#[DataProvider('modeProvider')]
138+
public function testSortRejectsDirectionConstants(string $class): void
139+
{
140+
// The obvious wrong guess for descending gets a clear error, not
141+
// undefined flag behavior
142+
$this->expectException(InvalidArgumentException::class);
143+
$this->expectExceptionMessage('sort(): sorting is always ascending');
144+
145+
$class::new(['b', 'a'])->sort(SORT_DESC);
146+
}
147+
137148
//endregion
138149
//region sortBy()
139150

@@ -210,5 +221,16 @@ public function testSortByOnFlatThrows(string $class): void
210221
$class::new(['a', 'b'])->sortBy('name');
211222
}
212223

224+
#[DataProvider('modeProvider')]
225+
public function testSortByRejectsDirectionConstants(string $class): void
226+
{
227+
// Previously fataled with a confusing array_multisort() TypeError
228+
// naming library internals
229+
$this->expectException(InvalidArgumentException::class);
230+
$this->expectExceptionMessage('sortBy(): sorting is always ascending');
231+
232+
$class::new([['n' => 2], ['n' => 1]])->sortBy('n', SORT_DESC);
233+
}
234+
213235
//endregion
214236
}

0 commit comments

Comments
 (0)