Skip to content

Commit d6bd267

Browse files
authored
support positional arguments in sprintf() constant format inference
1 parent 113fed0 commit d6bd267

File tree

2 files changed

+31
-2
lines changed

2 files changed

+31
-2
lines changed

src/Type/Php/SprintfFunctionDynamicReturnTypeExtension.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public function getTypeFromFunctionCall(
3333
FunctionReflection $functionReflection,
3434
FuncCall $functionCall,
3535
Scope $scope,
36-
): Type
36+
): ?Type
3737
{
3838
$args = $functionCall->getArgs();
3939
if (count($args) === 0) {
@@ -43,7 +43,13 @@ public function getTypeFromFunctionCall(
4343
$formatType = $scope->getType($args[0]->value);
4444

4545
if ($formatType instanceof ConstantStringType) {
46-
if (preg_match('/^%[0-9]*\.?[0-9]+[bdeEfFgGhHouxX]$/', $formatType->getValue()) === 1) {
46+
// The printf format is %[argnum$][flags][width][.precision]
47+
if (preg_match('/^%([0-9]*\$)?[0-9]*\.?[0-9]+[bdeEfFgGhHouxX]$/', $formatType->getValue(), $matches) === 1) {
48+
// invalid positional argument
49+
if ($matches[1] === '0$') {
50+
return null;
51+
}
52+
4753
return new IntersectionType([
4854
new StringType(),
4955
new AccessoryNumericStringType(),

tests/PHPStan/Analyser/data/bug-7387.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,27 @@ public function specifiers(int $i) {
4242
assertType('numeric-string', sprintf('%14X', $i));
4343

4444
}
45+
46+
public function positionalArgs($mixed, int $i, float $f, string $s) {
47+
// https://3v4l.org/vVL0c
48+
assertType('non-empty-string', sprintf('%2$14s', $mixed, $i));
49+
50+
assertType('numeric-string', sprintf('%2$.14F', $mixed, $i));
51+
assertType('numeric-string', sprintf('%2$.14F', $mixed, $f));
52+
assertType('numeric-string', sprintf('%2$.14F', $mixed, $s));
53+
54+
assertType('numeric-string', sprintf('%2$1.14F', $mixed, $i));
55+
assertType('numeric-string', sprintf('%2$2.14F', $mixed, $f));
56+
assertType('numeric-string', sprintf('%2$3.14F', $mixed, $s));
57+
58+
assertType('numeric-string', sprintf('%2$14F', $mixed, $i));
59+
assertType('numeric-string', sprintf('%2$14F', $mixed, $f));
60+
assertType('numeric-string', sprintf('%2$14F', $mixed, $s));
61+
62+
assertType('numeric-string', sprintf('%10$14F', $mixed, $s));
63+
}
64+
65+
public function invalidPositionalArgFormat($mixed, string $s) {
66+
assertType('string', sprintf('%0$14F', $mixed, $s));
67+
}
4568
}

0 commit comments

Comments
 (0)