21
21
use function array_shift ;
22
22
use function count ;
23
23
use function in_array ;
24
+ use function intval ;
24
25
use function is_string ;
25
26
use function preg_match ;
26
27
use function sprintf ;
28
+ use function substr ;
27
29
use function vsprintf ;
28
30
29
31
class SprintfFunctionDynamicReturnTypeExtension implements DynamicFunctionReturnTypeExtension
@@ -46,28 +48,49 @@ public function getTypeFromFunctionCall(
46
48
}
47
49
48
50
$ formatType = $ scope ->getType ($ args [0 ]->value );
51
+
49
52
if (count ($ formatType ->getConstantStrings ()) > 0 ) {
50
- $ skip = false ;
53
+ $ singlePlaceholderEarlyReturn = null ;
51
54
foreach ($ formatType ->getConstantStrings () as $ constantString ) {
52
55
// The printf format is %[argnum$][flags][width][.precision]
53
- if (preg_match ('/^%([0-9]*\$)?[0-9]*\.?[0-9]*[bdeEfFgGhHouxX]$/ ' , $ constantString ->getValue (), $ matches ) === 1 ) {
54
- // invalid positional argument
55
- if (array_key_exists (1 , $ matches ) && $ matches [1 ] === '0$ ' ) {
56
+ if (preg_match ('/^%([0-9]*\$)?[0-9]*\.?[0-9]*([sbdeEfFgGhHouxX])$/ ' , $ constantString ->getValue (), $ matches ) === 1 ) {
57
+ if (array_key_exists (1 , $ matches ) && ($ matches [1 ] !== '' )) {
58
+ // invalid positional argument
59
+ if ($ matches [1 ] === '0$ ' ) {
60
+ return null ;
61
+ }
62
+ $ checkArg = intval (substr ($ matches [1 ], 0 , -1 ));
63
+ } else {
64
+ $ checkArg = 1 ;
65
+ }
66
+
67
+ // constant string specifies a numbered argument that does not exist
68
+ if (!array_key_exists ($ checkArg , $ args )) {
56
69
return null ;
57
70
}
58
71
72
+ // if the format string is just a placeholder and specified an argument
73
+ // of stringy type, then the return value will be of the same type
74
+ $ checkArgType = $ scope ->getType ($ args [$ checkArg ]->value );
75
+
76
+ if ($ matches [2 ] === 's ' && $ checkArgType ->isString ()->yes ()) {
77
+ $ singlePlaceholderEarlyReturn = $ checkArgType ;
78
+ } elseif ($ matches [2 ] !== 's ' ) {
79
+ $ singlePlaceholderEarlyReturn = new IntersectionType ([
80
+ new StringType (),
81
+ new AccessoryNumericStringType (),
82
+ ]);
83
+ }
84
+
59
85
continue ;
60
86
}
61
87
62
- $ skip = true ;
88
+ $ singlePlaceholderEarlyReturn = null ;
63
89
break ;
64
90
}
65
91
66
- if (!$ skip ) {
67
- return new IntersectionType ([
68
- new StringType (),
69
- new AccessoryNumericStringType (),
70
- ]);
92
+ if ($ singlePlaceholderEarlyReturn !== null ) {
93
+ return $ singlePlaceholderEarlyReturn ;
71
94
}
72
95
}
73
96
0 commit comments