Skip to content

Accept decimal-int-string arguments for %d printf placeholders in strict mode - #5997

Merged
staabm merged 1 commit into
phpstan:2.2.xfrom
phpstan-bot:create-pull-request/patch-n6g171s
Jul 5, 2026
Merged

Accept decimal-int-string arguments for %d printf placeholders in strict mode#5997
staabm merged 1 commit into
phpstan:2.2.xfrom
phpstan-bot:create-pull-request/patch-n6g171s

Conversation

@phpstan-bot

Copy link
Copy Markdown
Collaborator

Summary

In strict printf placeholder mode (checkStrictPrintfPlaceholderTypes), passing an integer-valued string such as '42' to a %d placeholder was reported as an error. As discussed in the issue, a decimal-int-string is printed by %d without any lossy conversion, so it should be accepted. This change allows decimal-int-string for %d-family placeholders while continuing to reject numeric-string and other strings that %d would trim or alter.

Changes

  • src/Rules/Functions/PrintfPlaceholder.php: for the int accepting type in strict mode, accept int or decimal-int-string (string & AccessoryDecimalIntegerStringType) instead of only int. Mirrors how the float case already accepts numeric-string.
  • tests/PHPStan/Rules/Functions/data/printf-param-types.php and PrintfParameterTypeRuleTest.php: added regression cases for decimal-int-string (OK), numeric-string (error), non-decimal-int-string (error), and constant strings '42'/'-1'/'0' (OK) vs '00'/'+1' (error).

Root cause

The strict int branch checked only (new IntegerType())->accepts($argumentType, true)->yes(), which rejects any string type. %d casts its argument to an integer; for a decimal-int-string this cast is lossless (the string already is the canonical decimal representation of an integer), so there is no reason to report it. The fix reuses the existing AccessoryDecimalIntegerStringType accessory type — the same abstraction PHPStan uses to decide whether a string array key is cast to int.

Analogous cases probed:

  • float (%f, %e, %g, …): already accepts numeric-string, which is a superset of decimal-int-string, so it was already correct — no change needed.
  • strict-int (width/precision, e.g. %*d): intentionally kept int-only; these are a deliberately stricter category and the reported issue concerns only the %d value slot.
  • Constant strings: '42', '-1', '0' are recognized as decimal-int-string by ConstantStringType::isDecimalIntegerString(), so they are now accepted too (covering the printf('%d', '42') example from the discussion), while '00' / '+1' / '1.23' remain errors.

Test

PrintfParameterTypeRuleTest::testStrict gains cases asserting that decimal-int-string and constant int-strings are accepted for %d, while numeric-string, non-decimal-int-string, and non-canonical constant strings still produce errors. The non-strict test continues to pass unchanged. The regression fails without the source change (the decimal-int-string case is reported as an unexpected error).

Fixes phpstan/phpstan#13609

@VincentLanglet
VincentLanglet requested a review from staabm July 4, 2026 18:49
@VincentLanglet VincentLanglet self-assigned this Jul 4, 2026
…ict mode

- PrintfPlaceholder::doesArgumentTypeMatchPlaceholder() now accepts a
  `decimal-int-string` (string&AccessoryDecimalIntegerStringType) for the
  `int` acceptingType when strict placeholder type checks are enabled, in
  addition to plain `int`. Such strings ("42", "-1", "0") are printed by %d
  without any lossy conversion.
- numeric-string and non-decimal-int-string ("00", "+1", "1.23") are still
  rejected because %d would trim/alter their value.
- Left the stricter `strict-int` acceptingType (width/precision, %*d) and the
  `float`/`string`/`mixed` cases unchanged: %f already allows numeric-string
  (a superset of decimal-int-string), and width/precision remain int-only by
  design.
@staabm
staabm force-pushed the create-pull-request/patch-n6g171s branch from c89dcf5 to 637cb1d Compare July 5, 2026 09:42
@staabm
staabm merged commit e4551cc into phpstan:2.2.x Jul 5, 2026
670 of 672 checks passed
@staabm
staabm deleted the create-pull-request/patch-n6g171s branch July 5, 2026 09:55
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.

"sprintf is expected to be int by placeholder" and int-string

3 participants