Skip to content

Report ksort() on a list as a call with no effect #15126

Description

@ondrejmirtes

Feature request

ksort() on a list is always a no-op with the default SORT_REGULAR flags: a list's keys are 0..n-1 in ascending order already. The same is true for arrays PHPStan knows are empty or have a single element.

This is the same class of dead call that ArrayValuesRule already reports:

I hit this in the wild: shipmonk/dead-code-detector builds a list of tips and then calls ksort() on it, clearly intending sort(). Because the ksort() silently did nothing, the tip order followed hash-map insertion order instead, which varies between parallel runs — so the tool's output was non-deterministic. A rule would have caught it immediately.

<?php declare(strict_types = 1);

/** @param list<string> $list */
function doFoo(array $list): void
{
	ksort($list); // no-op, keys are already 0..n-1 ascending
}

function doBar(): void
{
	$tips = [];
	$tips[] = 'a';
	$tips[] = 'b';
	ksort($tips); // no-op, same reason
}

Neither is reported at level 10 today.

Things the rule needs to be careful about:

  • Only the default flags qualify. ksort($list, SORT_STRING) is not a no-op — string comparison orders "10" before "2". SORT_REGULAR, SORT_NUMERIC, and no argument at all are safe; anything unknown should be skipped. SORT_NATURAL/SORT_FLAG_CASE also reorder nothing on a list, but there's no need to be exhaustive — bailing out whenever the flags aren't a known-safe constant is fine.
  • krsort() is not a no-op on a list — it reverses it. Only ksort() should be reported.
  • ksort() on an array with at most one element is a no-op regardless of whether it's a list, mirroring arrayValues.empty.

Suggested identifiers, following the existing naming: ksort.list and ksort.empty.

asort()/arsort()/sort()/rsort() sort by value, so they're outside the scope of this request.

Did PHPStan help you today? Did it make you happy in any way?

It found a real non-determinism bug in a third-party extension for me today — just not this particular one, which is why I'm asking for it :)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions