Skip to content

Report non-zero integer literals passed directly to parameters with allowed constants - #6565

Open
phpstan-bot wants to merge 1 commit into
phpstan:2.3.xfrom
phpstan-bot:create-pull-request/patch-9iikj0l
Open

phpstan-bot wants to merge 1 commit into
phpstan:2.3.xfrom
phpstan-bot:create-pull-request/patch-9iikj0l

Conversation

@phpstan-bot

Copy link
Copy Markdown
Collaborator

Summary

Following up on the allowed-constants checks for flag parameters, this reports hardcoded integer literals that are passed directly (in the AST) to parameters that expect a constant, or a bitmask of constants. For example json_encode($x, 1), json_encode($x, 1 | 2), and the 2 in json_encode($x, $one | 2) are now reported.

Changes

  • src/Rules/FunctionCallParametersCheck.php: new findNonZeroIntegerLiterals() walks BitwiseOr trees and collects non-zero Scalar\Int_ leaves. The check reports each of them as Integer literal %s is not allowed for %s of … (identifier argument.integerLiteral, tip "Use constants instead.") when the parameter has getAllowedConstants() !== null. 0 is still allowed because it is the usual "no flags" value, which an existing test already expects.
  • New message parameter wired through every caller of the shared check: CallToFunctionParametersRule, CallMethodsRule, CallStaticMethodsRule, InstantiationRule, AttributesCheck, CallCallablesRule, and CallUserFuncRule.

Root cause

The allowed-constants check only looked at arguments made entirely of constant fetches (resolveConstantReflections()). An integer literal, or a | expression that mixed in literals or variables, returned null, so the argument wasn't checked at all.

Test

  • tests/PHPStan/Rules/Functions/data/bug-14727.php: the playground reproducer, plus cases for 0, a mix of constant and literal, named arguments, a single-value parameter (array_unique), and json_decode's $flags.
  • Added a literal case to each of the existing constant-parameter-check data files for methods, static methods, constructors, first-class callables and call_user_func(). They share the same code path and all report the literal.
  • Updated the expectations in testRoundModePhp84 and testNamedParametersForMultiVariantFunctions, which pass literal modes.

Fixes phpstan/phpstan#14727

🤖 Generated with Claude Code

…llowed constants

- FunctionCallParametersCheck: for parameters that have an allowed-constants
  definition (single or bitmask), walk the argument's `|` tree and report every
  hardcoded non-zero `Scalar\Int_` leaf (identifier `argument.integerLiteral`,
  tip "Use constants instead."). `0` stays allowed as the "no flags" value;
  variables and other dynamic operands are ignored.
- Added the new message to all callers of the shared check: functions,
  methods, static methods, constructors, attributes, callables and
  call_user_func().
- Updated existing expectations for round() mode and
  PDOStatement::setFetchMode() which pass integer literals.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@VincentLanglet

Copy link
Copy Markdown
Contributor

I have three thought about this @staabm

  1. This seems opinionated to me so should be behind an option/flag

  2. I don't have literal-string param in mind, but shouldn't it be the same ?

@staabm

staabm commented Sep 23, 2026

Copy link
Copy Markdown
Contributor

this PR adds one more case to a rule which was implemented in #5256

  1. This seems opinionated to me so should be behind an option/flag

why do you think its opinionated?

  1. I don't have literal-string param in mind, but shouldn't it be the same ?

the rules in question are about bitmasks(int) and single-value ints.
all variants we support atm can be found in resources/constantToFunctionParameterMap.php

I think its all about ints atm

@VincentLanglet

Copy link
Copy Markdown
Contributor

this PR adds one more case to a rule which was implemented in #5256

  1. This seems opinionated to me so should be behind an option/flag

why do you think its opinionated?

That's not the same idea than #5256 to me.

The rule was about reporting

json_decode($json, true, JSON_THROW_ON_ERROR);

because JSON_THROW_ON_ERROR is meant to be used as a flag for the fourth parameter so the developer certainly made a mistake and wants

json_decode($json, true, flags: JSON_THROW_ON_ERROR);

But, if someone writes

json_decode($json, true, flags: 4194304);

this is a perfectly valid code which does the same ; that's why it could be a flag only enabled on strict-rules...

I agree that it's not recommended to use an integer rather than the constant and I dunno if there is usecase where people does. But, still, to me it's like a new rule (the issue is flagged as feature-request) and not a bugfix. So it will require ondrej review and I prefer it because I don't feel confident merging this by myself.

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.

Report directly hardcoded constant literals for flag params

3 participants