3
3
* Copyright © Magento, Inc. All rights reserved.
4
4
* See COPYING.txt for license details.
5
5
*/
6
+ declare (strict_types=1 );
7
+
6
8
namespace Magento2 \Sniffs \Commenting ;
7
9
8
10
use PHP_CodeSniffer \Files \File ;
9
11
use PHP_CodeSniffer \Sniffs \AbstractVariableSniff ;
10
- use PHP_CodeSniffer \Util \Tokens ;
11
12
use Magento2 \Helpers \Commenting \PHPDocFormattingValidator ;
12
13
13
14
/**
14
15
* Class ClassPropertyPHPDocFormattingSniff
15
16
*/
16
17
class ClassPropertyPHPDocFormattingSniff extends AbstractVariableSniff
17
18
{
18
-
19
19
/**
20
20
* @var array
21
21
*/
22
- private $ ignoreTokens = [
22
+ private const TOKENS_ALLOWED_BETWEEN_PHPDOC_AND_PROPERTY_NAME = [
23
23
T_PUBLIC ,
24
24
T_PRIVATE ,
25
25
T_PROTECTED ,
26
26
T_VAR ,
27
27
T_STATIC ,
28
28
T_WHITESPACE ,
29
+ T_NS_SEPARATOR ,
30
+ T_STRING ,
31
+ T_COMMENT
29
32
];
30
33
31
34
/**
@@ -38,15 +41,9 @@ class ClassPropertyPHPDocFormattingSniff extends AbstractVariableSniff
38
41
*/
39
42
public function __construct ()
40
43
{
41
- $ scopes = Tokens::$ ooScopeTokens ;
42
44
$ this ->PHPDocFormattingValidator = new PHPDocFormattingValidator ();
43
- $ listen = [
44
- T_VARIABLE ,
45
- T_DOUBLE_QUOTED_STRING ,
46
- T_HEREDOC ,
47
- ];
48
45
49
- parent ::__construct ($ scopes , $ listen , true );
46
+ parent ::__construct ();
50
47
}
51
48
52
49
/**
@@ -56,111 +53,147 @@ public function processMemberVar(File $phpcsFile, $stackPtr)
56
53
{
57
54
$ tokens = $ phpcsFile ->getTokens ();
58
55
59
- $ commentEnd = $ phpcsFile ->findPrevious ($ this ->ignoreTokens , ($ stackPtr - 1 ), null , true );
56
+ $ commentEnd = $ phpcsFile ->findPrevious (
57
+ self ::TOKENS_ALLOWED_BETWEEN_PHPDOC_AND_PROPERTY_NAME ,
58
+ $ stackPtr - 1 ,
59
+ null ,
60
+ true
61
+ );
60
62
61
- if ($ commentEnd !== false && $ tokens [$ commentEnd ]['code ' ] === T_STRING ) {
62
- $ commentEnd = $ phpcsFile ->findPrevious ($ this ->ignoreTokens , ($ commentEnd - 1 ), null , true );
63
- } elseif ($ commentEnd === false
64
- || ($ tokens [$ commentEnd ]['code ' ] !== T_DOC_COMMENT_CLOSE_TAG
65
- && $ tokens [$ commentEnd ]['code ' ] !== T_COMMENT )
66
- ) {
63
+ if ($ commentEnd === false || $ tokens [$ commentEnd ]['code ' ] !== T_DOC_COMMENT_CLOSE_TAG ) {
67
64
$ phpcsFile ->addWarning ('Missing PHP DocBlock for class property. ' , $ stackPtr , 'Missing ' );
68
65
return ;
69
66
}
70
67
71
68
$ commentStart = $ tokens [$ commentEnd ]['comment_opener ' ];
72
- $ foundVar = null ;
69
+ $ varAnnotationPosition = null ;
73
70
foreach ($ tokens [$ commentStart ]['comment_tags ' ] as $ tag ) {
74
71
if ($ tokens [$ tag ]['content ' ] === '@var ' ) {
75
- if ($ foundVar !== null ) {
76
- $ error = 'Only one @var tag is allowed for class property declaration. ' ;
77
- $ phpcsFile ->addWarning ($ error , $ tag , 'DuplicateVar ' );
72
+ if ($ varAnnotationPosition !== null ) {
73
+ $ phpcsFile ->addWarning (
74
+ 'Only one @var tag is allowed for class property declaration. ' ,
75
+ $ tag ,
76
+ 'DuplicateVar '
77
+ );
78
78
} else {
79
- $ foundVar = $ tag ;
79
+ $ varAnnotationPosition = $ tag ;
80
80
}
81
81
}
82
82
}
83
83
84
- if ($ foundVar === null ) {
85
- $ error = 'Class properties must have type declaration using @var tag. ' ;
86
- $ phpcsFile ->addWarning ($ error , $ stackPtr , 'MissingVar ' );
84
+ if ($ varAnnotationPosition === null ) {
85
+ $ phpcsFile ->addWarning (
86
+ 'Class properties must have type declaration using @var tag. ' ,
87
+ $ stackPtr ,
88
+ 'MissingVar '
89
+ );
87
90
return ;
88
91
}
89
92
90
- $ string = $ phpcsFile ->findNext (T_DOC_COMMENT_STRING , $ foundVar , $ commentEnd );
91
- if ($ string === false || $ tokens [$ string ]['line ' ] !== $ tokens [$ foundVar ]['line ' ]) {
92
- $ error = 'Content missing for @var tag in class property declaration. ' ;
93
- $ phpcsFile ->addWarning ($ error , $ foundVar , 'EmptyVar ' );
93
+ $ string = $ phpcsFile ->findNext (T_DOC_COMMENT_STRING , $ varAnnotationPosition , $ commentEnd );
94
+ if ($ string === false || $ tokens [$ string ]['line ' ] !== $ tokens [$ varAnnotationPosition ]['line ' ]) {
95
+ $ phpcsFile ->addWarning (
96
+ 'Content missing for @var tag in class property declaration. ' ,
97
+ $ varAnnotationPosition ,
98
+ 'EmptyVar '
99
+ );
94
100
return ;
95
101
}
96
102
97
103
// Check if class has already have meaningful description after @var tag
98
- $ isShortDescriptionAfterVar = $ phpcsFile ->findNext (
104
+ $ shortDescriptionAfterVarPosition = $ phpcsFile ->findNext (
99
105
T_DOC_COMMENT_STRING ,
100
- $ foundVar + 4 ,
106
+ $ varAnnotationPosition + 4 ,
101
107
$ commentEnd ,
102
108
false ,
103
109
null ,
104
110
false
105
111
);
112
+
106
113
if ($ this ->PHPDocFormattingValidator ->providesMeaning (
107
- $ isShortDescriptionAfterVar ,
114
+ $ shortDescriptionAfterVarPosition ,
108
115
$ commentStart ,
109
116
$ tokens
110
117
) !== true ) {
111
118
preg_match (
112
119
'`^((?:\|?(?:array\([^\)]*\)|[ \\\\\[\]]+))*)( .*)?`i ' ,
113
- $ tokens [($ foundVar + 2 )]['content ' ],
120
+ $ tokens [($ varAnnotationPosition + 2 )]['content ' ],
114
121
$ varParts
115
122
);
116
123
if ($ varParts [1 ]) {
117
124
return ;
118
125
}
119
- $ error = 'Short description must be before @var tag. ' ;
120
- $ phpcsFile ->addWarning ($ error , $ isShortDescriptionAfterVar , 'ShortDescriptionAfterVar ' );
121
- return ;
126
+ $ phpcsFile ->addWarning (
127
+ 'Short description must be before @var tag. ' ,
128
+ $ shortDescriptionAfterVarPosition ,
129
+ 'ShortDescriptionAfterVar '
130
+ );
122
131
}
123
132
124
- // Check if class has already have meaningful description before @var tag
125
- $ isShortDescriptionPreviousVar = $ phpcsFile ->findPrevious (
133
+ $ this ->processPropertyShortDescription ($ phpcsFile , $ stackPtr , $ varAnnotationPosition , $ commentStart );
134
+ }
135
+
136
+ /**
137
+ * Check if class has already have meaningful description before var tag
138
+ *
139
+ * @param File $phpcsFile
140
+ * @param int $propertyNamePosition
141
+ * @param int $varAnnotationPosition
142
+ * @param int $commentStart
143
+ */
144
+ private function processPropertyShortDescription (
145
+ File $ phpcsFile ,
146
+ int $ propertyNamePosition ,
147
+ int $ varAnnotationPosition ,
148
+ int $ commentStart
149
+ ): void {
150
+ $ propertyShortDescriptionPosition = $ phpcsFile ->findPrevious (
126
151
T_DOC_COMMENT_STRING ,
127
- $ foundVar ,
152
+ $ varAnnotationPosition ,
128
153
$ commentStart ,
129
154
false ,
130
155
null ,
131
156
false
132
157
);
133
158
134
- if ($ isShortDescriptionPreviousVar === false ) {
159
+ if ($ propertyShortDescriptionPosition === false ) {
135
160
return ;
136
161
}
137
162
138
- $ propertyNamePosition = $ phpcsFile ->findNext (
139
- T_VARIABLE ,
140
- $ foundVar ,
141
- null ,
142
- false ,
143
- null ,
144
- false
145
- );
146
- if ($ propertyNamePosition === false ) {
147
- return ;
148
- };
163
+ $ tokens = $ phpcsFile ->getTokens ();
149
164
$ propertyName = trim ($ tokens [$ propertyNamePosition ]['content ' ], '$ ' );
150
- $ shortDescription = strtolower ($ tokens [$ isShortDescriptionPreviousVar ]['content ' ]);
165
+ $ shortDescription = strtolower ($ tokens [$ propertyShortDescriptionPosition ]['content ' ]);
151
166
152
- if ($ shortDescription === strtolower ($ propertyName )) {
153
- $ error = 'Short description duplicates class property name. ' ;
154
- $ phpcsFile ->addWarning ($ error , $ isShortDescriptionPreviousVar , 'AlreadyHaveMeaningfulNameVar ' );
155
- return ;
167
+ if ($ this ->isDuplicate ($ propertyName , $ shortDescription )) {
168
+ $ phpcsFile ->addWarning (
169
+ 'Short description duplicates class property name. ' ,
170
+ $ propertyShortDescriptionPosition ,
171
+ 'AlreadyHaveMeaningfulNameVar '
172
+ );
156
173
}
174
+ }
157
175
158
- $ propertyNameParts = array_filter (preg_split ('/(?=[A-Z])/ ' , $ propertyName ));
176
+ /**
177
+ * Does short description duplicate the property name
178
+ *
179
+ * @param string $propertyName
180
+ * @param string $shortDescription
181
+ * @return bool
182
+ */
183
+ private function isDuplicate (string $ propertyName , string $ shortDescription ): bool
184
+ {
185
+ return $ this ->clean ($ propertyName ) === $ this ->clean ($ shortDescription );
186
+ }
159
187
160
- if ($ shortDescription === strtolower (implode (' ' , $ propertyNameParts ))) {
161
- $ error = 'Short description duplicates class property name. ' ;
162
- $ phpcsFile ->addWarning ($ error , $ isShortDescriptionPreviousVar , 'AlreadyHaveMeaningfulNameVar ' );
163
- }
188
+ /**
189
+ * Return only A-Za-z characters converted to lowercase from the string
190
+ *
191
+ * @param string $string
192
+ * @return string
193
+ */
194
+ private function clean (string $ string ): string
195
+ {
196
+ return strtolower (preg_replace ('/[^A-Za-z]/ ' , '' , $ string ));
164
197
}
165
198
166
199
/**
0 commit comments