@@ -34,6 +34,16 @@ final class FunctionLengthSniff implements Sniff
34
34
*/
35
35
public $ ignoreDocBlocks = true ;
36
36
37
+ /**
38
+ * @var true
39
+ */
40
+ public $ ignoreSingleLineComments = true ;
41
+
42
+ /**
43
+ * @var true
44
+ */
45
+ public $ ignoreWhiteLines = true ;
46
+
37
47
/**
38
48
* @return int[]
39
49
*/
@@ -49,16 +59,30 @@ public function register(): array
49
59
public function process (File $ file , $ position )
50
60
{
51
61
$ length = $ this ->getStructureLengthInLines ($ file , $ position );
62
+ if ($ length <= $ this ->maxLength ) {
63
+ return ;
64
+ }
52
65
53
- if ($ length > $ this ->maxLength ) {
54
- $ error = sprintf (
55
- 'Your function is too long. Currently using %d lines. Can be up to %d lines. ' ,
56
- $ length ,
57
- $ this ->maxLength
58
- );
59
-
60
- $ file ->addError ($ error , $ position , 'TooLong ' );
66
+ $ ignored = [];
67
+ $ suffix = '' ;
68
+ $ this ->ignoreWhiteLines and $ ignored [] = 'white lines ' ;
69
+ $ this ->ignoreSingleLineComments and $ ignored [] = 'single line comments ' ;
70
+ $ this ->ignoreDocBlocks and $ ignored [] = 'doc blocks ' ;
71
+ if ($ ignored ) {
72
+ $ suffix = ' (ignoring ' ;
73
+ $ last = array_pop ($ ignored );
74
+ $ others = implode (', ' , $ ignored );
75
+ $ suffix .= $ others ? "{$ others } and {$ last }) " : "{$ last }) " ;
61
76
}
77
+
78
+ $ error = sprintf (
79
+ 'Your function is too long. Currently using %d lines%s, max is %d. ' ,
80
+ $ length ,
81
+ $ suffix ,
82
+ $ this ->maxLength
83
+ );
84
+
85
+ $ file ->addError ($ error , $ position , 'TooLong ' );
62
86
}
63
87
64
88
/**
@@ -77,25 +101,100 @@ public function getStructureLengthInLines(File $file, int $position): int
77
101
return 0 ;
78
102
}
79
103
80
- $ opener = $ token ['scope_opener ' ];
81
- $ closer = $ token ['scope_closer ' ];
82
- $ length = $ tokens [$ closer ]['line ' ] - $ tokens [$ opener ]['line ' ];
104
+ $ start = $ token ['scope_opener ' ];
105
+ $ end = $ token ['scope_closer ' ];
106
+ $ length = $ tokens [$ end ]['line ' ] - $ tokens [$ start ]['line ' ];
83
107
84
- if (! $ this ->ignoreDocBlocks ) {
108
+ if ($ length < $ this ->maxLength ) {
85
109
return $ length ;
86
110
}
87
111
88
- $ decrease = 0 ;
89
- for ($ i = $ opener + 1 ; $ i < $ closer ; $ i ++) {
90
- if ($ tokens [$ i ]['code ' ] === T_DOC_COMMENT_OPEN_TAG ) {
91
- $ openerLine = (int )$ tokens [$ i ]['line ' ];
92
- $ closer = $ tokens [$ i ]['comment_closer ' ] ?? null ;
93
- $ decrease += is_numeric ($ closer )
94
- ? (int )$ tokens [$ closer ]['line ' ] - ($ openerLine - 1 )
95
- : 1 ;
112
+ return $ length - $ this ->collectLinesToExclude ($ start , $ end , $ tokens );
113
+ }
114
+
115
+ /**
116
+ * @param int $start
117
+ * @param int $end
118
+ * @param array $tokens
119
+ * @return int
120
+ */
121
+ private function collectLinesToExclude (
122
+ int $ start ,
123
+ int $ end ,
124
+ array $ tokens
125
+ ): int {
126
+
127
+ $ linesData = $ docblocks = [];
128
+
129
+ $ skipLines = [$ tokens [$ start + 1 ]['line ' ], $ tokens [$ end - 1 ]['line ' ]];
130
+ for ($ i = $ start + 1 ; $ i < $ end - 1 ; $ i ++) {
131
+ if (in_array ($ tokens [$ i ]['line ' ], $ skipLines , true )) {
132
+ continue ;
96
133
}
134
+
135
+ $ docblocks = $ this ->docBlocksData ($ tokens , $ i , $ docblocks );
136
+ $ linesData = $ this ->ignoredLinesData ($ tokens [$ i ], $ linesData );
137
+ }
138
+
139
+ $ empty = array_filter (array_column ($ linesData , 'empty ' ));
140
+ $ onlyComment = array_filter (array_column ($ linesData , 'only-comment ' ));
141
+
142
+ $ toExcludeCount = array_sum ($ docblocks );
143
+ if ($ this ->ignoreWhiteLines ) {
144
+ $ toExcludeCount += count ($ empty );
145
+ }
146
+ if ($ this ->ignoreSingleLineComments ) {
147
+ $ toExcludeCount += count ($ onlyComment ) - count ($ empty );
148
+ }
149
+
150
+ return $ toExcludeCount ;
151
+ }
152
+
153
+ /**
154
+ * @param array $token
155
+ * @param array $lines
156
+ * @return array
157
+ */
158
+ private function ignoredLinesData (array $ token , array $ lines ): array
159
+ {
160
+ $ line = $ token ['line ' ];
161
+ if (!array_key_exists ($ line , $ lines )) {
162
+ $ lines [$ line ] = ['empty ' => true , 'only-comment ' => true ];
163
+ }
164
+
165
+ if (!in_array ($ token ['code ' ], [T_COMMENT , T_WHITESPACE ], true )) {
166
+ $ lines [$ line ]['only-comment ' ] = false ;
97
167
}
98
168
99
- return max (0 , $ length - $ decrease );
169
+ if ($ token ['code ' ] !== T_WHITESPACE ) {
170
+ $ lines [$ line ]['empty ' ] = false ;
171
+ }
172
+
173
+ return $ lines ;
174
+ }
175
+
176
+ /**
177
+ * @param array $tokens
178
+ * @param int $position
179
+ * @param array $docBlocks
180
+ * @return array
181
+ */
182
+ private function docBlocksData (
183
+ array $ tokens ,
184
+ int $ position ,
185
+ array $ docBlocks
186
+ ): array {
187
+ if (!$ this ->ignoreDocBlocks
188
+ || $ tokens [$ position ]['code ' ] !== T_DOC_COMMENT_OPEN_TAG
189
+ ) {
190
+ return $ docBlocks ;
191
+ }
192
+
193
+ $ closer = $ tokens [$ position ]['comment_closer ' ] ?? null ;
194
+ $ docBlocks [] = is_numeric ($ closer )
195
+ ? 1 + ($ tokens [$ closer ]['line ' ] - $ tokens [$ position ]['line ' ])
196
+ : 1 ;
197
+
198
+ return $ docBlocks ;
100
199
}
101
200
}
0 commit comments