5
5
use Nette \Utils \Strings ;
6
6
use PHPStan \DependencyInjection \AutowiredService ;
7
7
use PHPStan \Php \PhpVersion ;
8
- use PHPStan \Type \ErrorType ;
9
- use PHPStan \Type \IntegerType ;
10
- use PHPStan \Type \Type ;
11
8
use function array_filter ;
12
- use function array_flip ;
13
9
use function array_keys ;
14
- use function array_map ;
15
- use function array_reduce ;
16
10
use function count ;
17
11
use function in_array ;
18
12
use function max ;
19
- use function sort ;
20
13
use function sprintf ;
21
14
use function strlen ;
22
- use function usort ;
23
15
use const PREG_SET_ORDER ;
24
16
25
17
/** @phpstan-type AcceptingTypeString 'strict-int'|'int'|'float'|'string'|'mixed' */
@@ -38,71 +30,18 @@ public function getPrintfPlaceholdersCount(string $format): int
38
30
return $ this ->getPlaceholdersCount (self ::PRINTF_SPECIFIER_PATTERN , $ format );
39
31
}
40
32
41
- /** @return array<int, array{string, callable(Type): bool}> position => [type name, matches callback] */
42
- public function getPrintfPlaceholderAcceptingTypes (string $ format ): array
33
+ /** @phpstan- return array<int, non-empty-list<PrintfPlaceholder>> parameter index => placeholders */
34
+ public function getPrintfPlaceholders (string $ format ): array
43
35
{
44
- $ placeholders = $ this ->parsePlaceholders (self ::PRINTF_SPECIFIER_PATTERN , $ format );
45
- $ result = [];
46
- // Type on the left can go to the type on the right, but not vice versa.
47
- $ typeSequenceMap = array_flip (['int ' , 'float ' , 'string ' , 'mixed ' ]);
48
-
49
- foreach ($ placeholders as $ position => $ types ) {
50
- sort ($ types );
51
- $ typeNames = array_map (
52
- static fn (string $ t ) => $ t === 'strict-int '
53
- ? 'int '
54
- : $ t ,
55
- $ types ,
56
- );
57
- $ typeName = array_reduce (
58
- $ typeNames ,
59
- static fn (string $ carry , string $ type ) => $ typeSequenceMap [$ carry ] < $ typeSequenceMap [$ type ]
60
- ? $ carry
61
- : $ type ,
62
- 'mixed ' ,
63
- );
64
- $ result [$ position ] = [
65
- $ typeName ,
66
- static function (Type $ t ) use ($ types ): bool {
67
- foreach ($ types as $ acceptingType ) {
68
- switch ($ acceptingType ) {
69
- case 'strict-int ' :
70
- $ subresult = (new IntegerType ())->accepts ($ t , true )->yes ();
71
- break ;
72
- case 'int ' :
73
- $ subresult = ! $ t ->toInteger () instanceof ErrorType;
74
- break ;
75
- case 'float ' :
76
- $ subresult = ! $ t ->toFloat () instanceof ErrorType;
77
- break ;
78
- // The function signature already limits the parameters to stringable types, so there's
79
- // no point in checking string again here.
80
- case 'string ' :
81
- case 'mixed ' :
82
- default :
83
- $ subresult = true ;
84
- break ;
85
- }
86
-
87
- if (!$ subresult ) {
88
- return false ;
89
- }
90
- }
91
-
92
- return true ;
93
- },
94
- ];
95
- }
96
-
97
- return $ result ;
36
+ return $ this ->parsePlaceholders (self ::PRINTF_SPECIFIER_PATTERN , $ format );
98
37
}
99
38
100
39
public function getScanfPlaceholdersCount (string $ format ): int
101
40
{
102
41
return $ this ->getPlaceholdersCount ('(?<specifier>[cdDeEfinosuxX%s]|\[[^\]]+\]) ' , $ format );
103
42
}
104
43
105
- /** @phpstan-return array<int, non-empty-list<AcceptingTypeString >> position => type */
44
+ /** @phpstan-return array<int, non-empty-list<PrintfPlaceholder >> parameter index => placeholders */
106
45
private function parsePlaceholders (string $ specifiersPattern , string $ format ): array
107
46
{
108
47
$ addSpecifier = '' ;
@@ -123,38 +62,49 @@ private function parsePlaceholders(string $specifiersPattern, string $format): a
123
62
$ placeholders = array_filter ($ matches , static fn (array $ match ): bool => strlen ($ match ['before ' ]) % 2 === 0 );
124
63
125
64
$ result = [];
126
- $ positionalPlaceholders = [];
127
- $ idx = 0 ;
65
+ $ parsedPlaceholders = [];
66
+ $ parameterIdx = 0 ;
67
+ $ placeholderNumber = 0 ;
128
68
129
69
foreach ($ placeholders as $ placeholder ) {
70
+ $ placeholderNumber ++;
71
+ $ showValueSuffix = false ;
72
+
130
73
if (isset ($ placeholder ['width ' ]) && $ placeholder ['width ' ] !== '' ) {
131
- $ result [$ idx ++] = ['strict-int ' => 1 ];
74
+ $ parsedPlaceholders [] = new PrintfPlaceholder (
75
+ sprintf ('"%s" (width) ' , $ placeholder [0 ]),
76
+ $ parameterIdx ++,
77
+ $ placeholderNumber ,
78
+ 'strict-int ' ,
79
+ );
80
+ $ showValueSuffix = true ;
132
81
}
133
82
134
83
if (isset ($ placeholder ['precision ' ]) && $ placeholder ['precision ' ] !== '' ) {
135
- $ result [ $ idx ++ ] = [ ' strict-int ' => 1 ];
136
- }
137
-
138
- if ( isset ( $ placeholder [ ' position ' ]) && $ placeholder [ ' position ' ] !== '' ) {
139
- // It may reference future position, so we have to process them later.
140
- $ positionalPlaceholders [] = $ placeholder ;
141
- continue ;
84
+ $ parsedPlaceholders [ ] = new PrintfPlaceholder (
85
+ sprintf ( ' "%s" (precision) ' , $ placeholder [ 0 ]),
86
+ $ parameterIdx ++,
87
+ $ placeholderNumber ,
88
+ ' strict-int ' ,
89
+ ) ;
90
+ $ showValueSuffix = true ;
142
91
}
143
92
144
- $ result [$ idx ++][$ this ->getAcceptingTypeBySpecifier ($ placeholder ['specifier ' ] ?? '' )] = 1 ;
93
+ $ parsedPlaceholders [] = new PrintfPlaceholder (
94
+ sprintf ('"%s" ' , $ placeholder [0 ]) . ($ showValueSuffix ? ' (value) ' : '' ),
95
+ isset ($ placeholder ['position ' ]) && $ placeholder ['position ' ] !== ''
96
+ ? $ placeholder ['position ' ] - 1
97
+ : $ parameterIdx ++,
98
+ $ placeholderNumber ,
99
+ $ this ->getAcceptingTypeBySpecifier ($ placeholder ['specifier ' ] ?? '' ),
100
+ );
145
101
}
146
102
147
- usort (
148
- $ positionalPlaceholders ,
149
- static fn (array $ a , array $ b ) => (int ) $ a ['position ' ] <=> (int ) $ b ['position ' ],
150
- );
151
-
152
- foreach ($ positionalPlaceholders as $ placeholder ) {
153
- $ idx = $ placeholder ['position ' ] - 1 ;
154
- $ result [$ idx ][$ this ->getAcceptingTypeBySpecifier ($ placeholder ['specifier ' ] ?? '' )] = 1 ;
103
+ foreach ($ parsedPlaceholders as $ placeholder ) {
104
+ $ result [$ placeholder ->parameterIndex ][] = $ placeholder ;
155
105
}
156
106
157
- return array_map ( static fn ( array $ a ) => array_keys ( $ a ), $ result) ;
107
+ return $ result ;
158
108
}
159
109
160
110
/** @phpstan-return 'string'|'int'|'float'|'mixed' */
0 commit comments