6
6
7
7
use PHP_CodeSniffer \Files \File ;
8
8
use PHP_CodeSniffer \Sniffs \Sniff ;
9
+ use PHP_CodeSniffer \Util \Common ;
9
10
use PHP_CodeSniffer \Util \Tokens ;
11
+ use SymfonyCustom \Helpers \SniffHelper ;
10
12
11
13
/**
12
14
* Throws errors if scalar type name are not valid.
13
15
*/
14
16
class ValidScalarTypeNameSniff implements Sniff
15
17
{
16
- /**
17
- * Types to replace: key is type to replace, value is type to replace with.
18
- *
19
- * @var array
20
- */
21
- public $ types = [
22
- 'boolean ' => 'bool ' ,
23
- 'double ' => 'float ' ,
24
- 'integer ' => 'int ' ,
25
- 'real ' => 'float ' ,
26
- ];
27
-
28
18
/**
29
19
* @return array
30
20
*/
31
21
public function register (): array
32
22
{
33
- $ tokens = Tokens::$ castTokens ;
34
- $ tokens [] = T_DOC_COMMENT_OPEN_TAG ;
35
-
36
- return $ tokens ;
23
+ return [T_DOC_COMMENT_TAG ];
37
24
}
38
25
39
26
/**
@@ -43,91 +30,94 @@ public function register(): array
43
30
public function process (File $ phpcsFile , $ stackPtr ): void
44
31
{
45
32
$ tokens = $ phpcsFile ->getTokens ();
46
- if (T_DOC_COMMENT_OPEN_TAG === $ tokens [$ stackPtr ]['code ' ]) {
47
- $ this ->validateDocComment ($ phpcsFile , $ stackPtr );
48
- } else {
49
- $ this ->validateCast ($ phpcsFile , $ stackPtr );
50
- }
51
- }
52
33
53
- /**
54
- * @param File $phpcsFile
55
- * @param int $stackPtr
56
- */
57
- private function validateDocComment (File $ phpcsFile , int $ stackPtr ): void
58
- {
59
- $ tokens = $ phpcsFile ->getTokens ();
60
- foreach ($ tokens [$ stackPtr ]['comment_tags ' ] as $ commentTag ) {
61
- if (in_array (
62
- $ tokens [$ commentTag ]['content ' ],
63
- ['@param ' , '@return ' , '@var ' ]
64
- )
65
- ) {
66
- $ docString = $ phpcsFile ->findNext (T_DOC_COMMENT_STRING , $ commentTag );
67
- if (false !== $ docString ) {
68
- $ stringParts = explode (' ' , $ tokens [$ docString ]['content ' ]);
69
- $ typeName = $ stringParts [0 ];
70
- $ this ->validateTypeName ($ phpcsFile , $ docString , $ typeName );
34
+ if (in_array ($ tokens [$ stackPtr ]['content ' ], SniffHelper::TAGS_WITH_TYPE )) {
35
+ preg_match (
36
+ '`^((?:\|?(?:array\([^\)]*\)|[ \\\\a-z0-9\[\<\,\>\]]+))*)( .*)?`i ' ,
37
+ $ tokens [($ stackPtr + 2 )]['content ' ],
38
+ $ match
39
+ );
40
+
41
+ if (isset ($ match [1 ]) === false ) {
42
+ return ;
43
+ }
44
+
45
+ // Check type (can be multiple, separated by '|').
46
+ $ type = $ match [1 ];
47
+ $ typeNames = explode ('| ' , $ type );
48
+ $ suggestedNames = [];
49
+ foreach ($ typeNames as $ i => $ typeName ) {
50
+ $ suggestedName = $ this ->getValidTypeName ($ typeName );
51
+
52
+ if (in_array ($ suggestedName , $ suggestedNames , true ) === false ) {
53
+ $ suggestedNames [] = $ suggestedName ;
71
54
}
72
55
}
73
- }
74
- }
75
56
76
- /**
77
- * @param File $phpcsFile
78
- * @param int $stackPtr
79
- */
80
- private function validateCast (File $ phpcsFile , int $ stackPtr ): void
81
- {
82
- $ tokens = $ phpcsFile ->getTokens ();
83
- preg_match ('/^\(\s*(\S+)\s*\)$/ ' , $ tokens [$ stackPtr ]['content ' ], $ matches );
84
- $ typeName = $ matches [1 ];
57
+ $ suggestedType = implode ('| ' , $ suggestedNames );
58
+ if ($ type !== $ suggestedType ) {
59
+ $ fix = $ phpcsFile ->addFixableError (
60
+ 'For type-hinting in PHPDocs, use %s instead of %s ' ,
61
+ $ stackPtr + 2 ,
62
+ 'Invalid ' ,
63
+ [$ suggestedType , $ type ]
64
+ );
65
+
66
+ if ($ fix ) {
67
+ $ replacement = $ suggestedType ;
68
+ if (isset ($ match [2 ])) {
69
+ $ replacement .= $ match [2 ];
70
+ }
85
71
86
- $ this ->validateTypeName ($ phpcsFile , $ stackPtr , $ typeName );
72
+ $ phpcsFile ->fixer ->replaceToken ($ stackPtr + 2 , $ replacement );
73
+ }
74
+ }
75
+ }
87
76
}
88
77
89
78
/**
90
- * @param File $phpcsFile
91
- * @param int $stackPtr
92
79
* @param string $typeName
80
+ *
81
+ * @return string
93
82
*/
94
- private function validateTypeName ( File $ phpcsFile , int $ stackPtr , string $ typeName ): void
83
+ private function getValidTypeName ( string $ typeName ): string
95
84
{
96
- $ validTypeName = $ this ->getValidTypeName ($ typeName );
97
-
98
- if (null !== $ validTypeName ) {
99
- $ needFix = $ phpcsFile ->addFixableError (
100
- 'For type-hinting in PHPDocs and casting, use %s instead of %s ' ,
101
- $ stackPtr ,
102
- '' ,
103
- [$ validTypeName , $ typeName ]
104
- );
105
- if ($ needFix ) {
106
- $ tokens = $ phpcsFile ->getTokens ();
107
- $ phpcsFile ->fixer ->beginChangeset ();
108
- $ newContent = str_replace (
109
- $ typeName ,
110
- $ validTypeName ,
111
- $ tokens [$ stackPtr ]['content ' ]
112
- );
113
- $ phpcsFile ->fixer ->replaceToken ($ stackPtr , $ newContent );
114
- $ phpcsFile ->fixer ->endChangeset ();
115
- }
85
+ $ parts = preg_split ('/([\<\,\>])/ ' , $ typeName , -1 , PREG_SPLIT_DELIM_CAPTURE );
86
+ $ partsNumber = count ($ parts ) - 1 ;
87
+
88
+ $ validType = '' ;
89
+ for ($ i = 0 ; $ i < $ partsNumber ; $ i += 2 ) {
90
+ $ validType .= $ this ->suggestType ($ parts [$ i ]).$ parts [$ i + 1 ];
116
91
}
92
+
93
+ if ('' !== $ parts [$ partsNumber ]) {
94
+ $ validType .= $ this ->suggestType ($ parts [$ partsNumber ]);
95
+ }
96
+
97
+ return $ validType ;
117
98
}
118
99
119
100
/**
120
101
* @param string $typeName
121
102
*
122
- * @return string|null
103
+ * @return string
123
104
*/
124
- private function getValidTypeName (string $ typeName ): ? string
105
+ private function suggestType (string $ typeName ): string
125
106
{
126
- $ typeName = strtolower ($ typeName );
127
- if (isset ($ this ->types [$ typeName ])) {
128
- return $ this ->types [$ typeName ];
107
+ if ('[] ' === substr ($ typeName , -2 )) {
108
+ return $ this ->suggestType (substr ($ typeName , 0 , -2 )).'[] ' ;
109
+ }
110
+
111
+ $ lowerType = strtolower ($ typeName );
112
+ switch ($ lowerType ) {
113
+ case 'bool ' :
114
+ case 'boolean ' :
115
+ return 'bool ' ;
116
+ case 'int ' :
117
+ case 'integer ' :
118
+ return 'int ' ;
129
119
}
130
120
131
- return null ;
121
+ return Common:: suggestType ( $ typeName ) ;
132
122
}
133
123
}
0 commit comments