Skip to content

Fix phpstan/phpstan#11619: Parameter #2 $callback of function uasort expects callable(Foo, Foo): int, 'strnatcasecmp' given.#5171

Open
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-mzyn37q
Open

Fix phpstan/phpstan#11619: Parameter #2 $callback of function uasort expects callable(Foo, Foo): int, 'strnatcasecmp' given.#5171
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-mzyn37q

Conversation

@phpstan-bot
Copy link
Collaborator

Summary

PHPStan incorrectly reported an error when passing a string-comparison function like strnatcasecmp to uasort() for arrays of Stringable objects. For example, uasort($options, 'strnatcasecmp') where $options is Foo[] and Foo implements Stringable would produce: "Parameter #2 $callback of function uasort expects callable(Foo, Foo): int, 'strnatcasecmp' given."

This is a false positive because PHP's internal functions like strnatcasecmp perform implicit type coercion from Stringable to string.

Changes

  • src/Type/CallableType.php: Propagated the $strictTypes parameter from accepts() through isSuperTypeOfInternal() to CallableTypeHelper::isParametersAcceptorSuperTypeOf()
  • src/Type/CallableTypeHelper.php: Added optional $strictTypes parameter (default true) and used it in the callable parameter acceptance check instead of the hardcoded true
  • tests/PHPStan/Rules/Functions/data/bug-11619.php: New regression test with Stringable class and uasort/usort with strnatcasecmp
  • tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php: Added testBug11619 test method

Root cause

CallableType::accepts() received a $strictTypes parameter but ignored it, always passing treatMixedAsAny=true to its internal method without forwarding $strictTypes. The CallableTypeHelper::isParametersAcceptorSuperTypeOf() hardcoded strictTypes=true when calling $theirParameter->getType()->accepts(), which caused StringType::accepts() to reject Stringable objects without checking for __toString().

The fix propagates the scope's $strictTypes value through the chain. In non-strict mode (no declare(strict_types=1)), StringType::accepts() now checks for __toString() and accepts Stringable objects. In strict mode, the existing behavior is preserved, which correctly handles cases like bug-12317 where a user-defined closure with explicit string parameter types should still be flagged.

Test

Added tests/PHPStan/Rules/Functions/data/bug-11619.php with a Stringable class Foo and calls to uasort($options, 'strnatcasecmp') and usort($options, 'strnatcasecmp'). The test expects no errors.

Fixes phpstan/phpstan#11619

…mode

- Propagated $strictTypes from CallableType::accepts() through to CallableTypeHelper
- In non-strict mode, Stringable objects are now accepted where string is expected in callable parameters
- This fixes false positives for patterns like uasort($stringableArray, 'strnatcasecmp')
- In strict_types mode, the stricter behavior is preserved (e.g. closures with explicit string params)
- New regression test in tests/PHPStan/Rules/Functions/data/bug-11619.php

Closes phpstan/phpstan#11619
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants