55use Nette \Utils \Strings ;
66use PHPStan \DependencyInjection \AutowiredService ;
77use PHPStan \Php \PhpVersion ;
8+ use ValueError ;
89use function array_filter ;
910use function array_keys ;
1011use function count ;
1112use function in_array ;
1213use function max ;
1314use function sprintf ;
15+ use function sscanf ;
1416use function strlen ;
1517use const PREG_SET_ORDER ;
1618
@@ -26,24 +28,36 @@ public function __construct(private PhpVersion $phpVersion)
2628
2729 public function getPrintfPlaceholdersCount (string $ format ): ?int
2830 {
29- return $ this ->getPlaceholdersCount (self ::PRINTF_SPECIFIER_PATTERN , $ format, false );
31+ return $ this ->getPlaceholdersCount (self ::PRINTF_SPECIFIER_PATTERN , $ format );
3032 }
3133
3234 /** @phpstan-return array<int, non-empty-list<PrintfPlaceholder>> parameter index => placeholders */
3335 public function getPrintfPlaceholders (string $ format ): ?array
3436 {
35- return $ this ->parsePlaceholders (self ::PRINTF_SPECIFIER_PATTERN , $ format, false );
37+ return $ this ->parsePlaceholders (self ::PRINTF_SPECIFIER_PATTERN , $ format );
3638 }
3739
3840 public function getScanfPlaceholdersCount (string $ format ): ?int
3941 {
40- return $ this ->getPlaceholdersCount ('(?<specifier>[cdDeEfinosuxX%s]|\[[^\]]+\]) ' , $ format , true );
42+ if ($ this ->phpVersion ->throwsValueErrorForInternalFunctions ()) {
43+ try {
44+ $ result = sscanf ('' , '%*n ' . $ format );
45+ } catch (ValueError ) {
46+ return null ;
47+ }
48+ } else {
49+ $ result = @sscanf ('' , '%*n ' . $ format );
50+ }
51+ if ($ result === null ) {
52+ return null ;
53+ }
54+ return count ($ result );
4155 }
4256
4357 /**
4458 * @phpstan-return array<int, non-empty-list<PrintfPlaceholder>>|null parameter index => placeholders
4559 */
46- private function parsePlaceholders (string $ specifiersPattern , string $ format, bool $ isScanf ): ?array
60+ private function parsePlaceholders (string $ specifiersPattern , string $ format ): ?array
4761 {
4862 $ addSpecifier = '' ;
4963 if ($ this ->phpVersion ->supportsHhPrintfSpecifier ()) {
@@ -72,10 +86,6 @@ private function parsePlaceholders(string $specifiersPattern, string $format, bo
7286 $ showValueSuffix = false ;
7387
7488 if (isset ($ placeholder ['width ' ]) && $ placeholder ['width ' ] !== '' ) {
75- if ($ isScanf ) {
76- // In scanf, * means assignment suppression - skip this placeholder entirely
77- continue ;
78- }
7989 $ parsedPlaceholders [] = new PrintfPlaceholder (
8090 sprintf ('"%s" (width) ' , $ placeholder [0 ]),
8191 $ parameterIdx ++,
@@ -136,9 +146,9 @@ private function getAcceptingTypeBySpecifier(string $specifier): string
136146 return 'mixed ' ;
137147 }
138148
139- private function getPlaceholdersCount (string $ specifiersPattern , string $ format, bool $ isScanf ): ?int
149+ private function getPlaceholdersCount (string $ specifiersPattern , string $ format ): ?int
140150 {
141- $ placeholdersMap = $ this ->parsePlaceholders ($ specifiersPattern , $ format, $ isScanf );
151+ $ placeholdersMap = $ this ->parsePlaceholders ($ specifiersPattern , $ format );
142152 if ($ placeholdersMap === null ) {
143153 return null ;
144154 }
0 commit comments