Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
/**
3
 * Squiz_Sniffs_ControlStructures_SwitchDeclarationSniff.
4
 *
5
 * PHP version 5
6
 *
7
 * @category  PHP
8
 * @package   PHP_CodeSniffer
9
 * @author    Greg Sherwood <gsherwood@squiz.net>
10
 * @author    Marc McIntyre <mmcintyre@squiz.net>
11
 * @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
12
 * @license   http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
13
 * @version   CVS: $Id: SwitchDeclarationSniff.php 270520 2008-12-04 06:07:51Z squiz $
14
 * @link      http://pear.php.net/package/PHP_CodeSniffer
15
 */
16
 
17
/**
18
 * Squiz_Sniffs_ControlStructures_SwitchDeclarationSniff.
19
 *
20
 * Ensures all the breaks and cases are aligned correctly according to their
21
 * parent switch's alignment and enforces other switch formatting.
22
 *
23
 * @category  PHP
24
 * @package   PHP_CodeSniffer
25
 * @author    Greg Sherwood <gsherwood@squiz.net>
26
 * @author    Marc McIntyre <mmcintyre@squiz.net>
27
 * @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
28
 * @license   http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
29
 * @version   Release: 1.2.1
30
 * @link      http://pear.php.net/package/PHP_CodeSniffer
31
 */
32
class Squiz_Sniffs_ControlStructures_SwitchDeclarationSniff implements PHP_CodeSniffer_Sniff
33
{
34
 
35
    /**
36
     * A list of tokenizers this sniff supports.
37
     *
38
     * @var array
39
     */
40
    public $supportedTokenizers = array(
41
                                   'PHP',
42
                                   'JS',
43
                                  );
44
 
45
 
46
    /**
47
     * Returns an array of tokens this test wants to listen for.
48
     *
49
     * @return array
50
     */
51
    public function register()
52
    {
53
        return array(T_SWITCH);
54
 
55
    }//end register()
56
 
57
 
58
    /**
59
     * Processes this test, when one of its tokens is encountered.
60
     *
61
     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
62
     * @param int                  $stackPtr  The position of the current token in the
63
     *                                        stack passed in $tokens.
64
     *
65
     * @return void
66
     */
67
    public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
68
    {
69
        $tokens = $phpcsFile->getTokens();
70
 
71
        $switch        = $tokens[$stackPtr];
72
        $nextCase      = $stackPtr;
73
        $caseAlignment = ($switch['column'] + 4);
74
        $caseCount     = 0;
75
 
76
        while (($nextCase = $phpcsFile->findNext(array(T_CASE, T_SWITCH), ($nextCase + 1), $switch['scope_closer'])) !== false) {
77
            // Skip nested SWITCH statements; they are handled on their own.
78
            if ($tokens[$nextCase]['code'] === T_SWITCH) {
79
                $nextCase = $tokens[$nextCase]['scope_closer'];
80
                continue;
81
            }
82
 
83
            $caseCount++;
84
 
85
            $content = $tokens[$nextCase]['content'];
86
            if ($content !== strtolower($content)) {
87
                $expected = strtolower($content);
88
                $error    = "CASE keyword must be lowercase; expected \"$expected\" but found \"$content\"";
89
                $phpcsFile->addError($error, $nextCase);
90
            }
91
 
92
            if ($tokens[$nextCase]['column'] !== $caseAlignment) {
93
                $error = 'CASE keyword must be indented 4 spaces from SWITCH keyword';
94
                $phpcsFile->addError($error, $nextCase);
95
            }
96
 
97
            if ($tokens[($nextCase + 1)]['type'] !== 'T_WHITESPACE' || $tokens[($nextCase + 1)]['content'] !== ' ') {
98
                $error = 'CASE keyword must be followed by a single space';
99
                $phpcsFile->addError($error, $nextCase);
100
            }
101
 
102
            $opener = $tokens[$nextCase]['scope_opener'];
103
            if ($tokens[($opener - 1)]['type'] === 'T_WHITESPACE') {
104
                $error = 'There must be no space before the colon in a CASE statement';
105
                $phpcsFile->addError($error, $nextCase);
106
            }
107
 
108
            $nextBreak = $phpcsFile->findNext(array(T_BREAK), ($nextCase + 1), $switch['scope_closer']);
109
            if ($nextBreak !== false && isset($tokens[$nextBreak]['scope_condition']) === true) {
110
                // Only check this BREAK statement if it matches the current CASE
111
                // statement. This stops the same break (used for multiple CASEs) being
112
                // checked more than once.
113
                if ($tokens[$nextBreak]['scope_condition'] === $nextCase) {
114
                    if ($tokens[$nextBreak]['column'] !== $caseAlignment) {
115
                        $error = 'BREAK statement must be indented 4 spaces from SWITCH keyword';
116
                        $phpcsFile->addError($error, $nextBreak);
117
                    }
118
 
119
                    /*
120
                        Ensure empty CASE statements are not allowed.
121
                        They must have some code content in them. A comment is not
122
                        enough.
123
                    */
124
 
125
                    $foundContent = false;
126
                    for ($i = ($tokens[$nextCase]['scope_opener'] + 1); $i < $nextBreak; $i++) {
127
                        if ($tokens[$i]['code'] === T_CASE) {
128
                            $i = $tokens[$i]['scope_opener'];
129
                            continue;
130
                        }
131
 
132
                        if (in_array($tokens[$i]['code'], PHP_CodeSniffer_Tokens::$emptyTokens) === false) {
133
                            $foundContent = true;
134
                            break;
135
                        }
136
                    }
137
 
138
                    if ($foundContent === false) {
139
                        $error = 'Empty CASE statements are not allowed';
140
                        $phpcsFile->addError($error, $nextCase);
141
                    }
142
 
143
                    /*
144
                        Ensure there is no blank line before
145
                        the BREAK statement.
146
                    */
147
 
148
                    $breakLine = $tokens[$nextBreak]['line'];
149
                    $prevLine  = 0;
150
                    for ($i = ($nextBreak - 1); $i > $stackPtr; $i--) {
151
                        if ($tokens[$i]['type'] !== 'T_WHITESPACE') {
152
                            $prevLine = $tokens[$i]['line'];
153
                            break;
154
                        }
155
                    }
156
 
157
                    if ($prevLine !== ($breakLine - 1)) {
158
                        $error = 'Blank lines are not allowed before BREAK statements';
159
                        $phpcsFile->addError($error, $nextBreak);
160
                    }
161
 
162
                    /*
163
                        Ensure the BREAK statement is followed by
164
                        a single blank line, or the end switch brace.
165
                    */
166
 
167
                    $breakLine = $tokens[$nextBreak]['line'];
168
                    $nextLine  = $tokens[$tokens[$stackPtr]['scope_closer']]['line'];
169
                    $semicolon = $phpcsFile->findNext(T_SEMICOLON, $nextBreak);
170
                    for ($i = ($semicolon + 1); $i < $tokens[$stackPtr]['scope_closer']; $i++) {
171
                        if ($tokens[$i]['type'] !== 'T_WHITESPACE') {
172
                            $nextLine = $tokens[$i]['line'];
173
                            break;
174
                        }
175
                    }
176
 
177
                    if ($nextLine !== ($breakLine + 2) && $i !== $tokens[$stackPtr]['scope_closer']) {
178
                        $error = 'BREAK statements must be followed by a single blank line';
179
                        $phpcsFile->addError($error, $nextBreak);
180
                    }
181
                }//end if
182
            } else {
183
                $nextBreak = $tokens[$nextCase]['scope_closer'];
184
            }//end if
185
 
186
            /*
187
                Ensure CASE statements are not followed by
188
                blank lines.
189
            */
190
 
191
            $caseLine = $tokens[$nextCase]['line'];
192
            $nextLine = $tokens[$nextBreak]['line'];
193
            for ($i = ($opener + 1); $i < $nextBreak; $i++) {
194
                if ($tokens[$i]['type'] !== 'T_WHITESPACE') {
195
                    $nextLine = $tokens[$i]['line'];
196
                    break;
197
                }
198
            }
199
 
200
            if ($nextLine !== ($caseLine + 1)) {
201
                $error = 'Blank lines are not allowed after CASE statements';
202
                $phpcsFile->addError($error, $nextCase);
203
            }
204
        }//end while
205
 
206
        $default = $phpcsFile->findPrevious(T_DEFAULT, $switch['scope_closer'], $switch['scope_opener']);
207
 
208
        // Make sure this default belongs to us.
209
        if ($default !== false) {
210
            $conditions = array_keys($tokens[$default]['conditions']);
211
            $owner      = array_pop($conditions);
212
            if ($owner !== $stackPtr) {
213
                $default = false;
214
            }
215
        }
216
 
217
        if ($default !== false) {
218
            $content = $tokens[$default]['content'];
219
            if ($content !== strtolower($content)) {
220
                $expected = strtolower($content);
221
                $error    = "DEFAULT keyword must be lowercase; expected \"$expected\" but found \"$content\"";
222
                $phpcsFile->addError($error, $default);
223
            }
224
 
225
            $opener = $tokens[$default]['scope_opener'];
226
            if ($tokens[($opener - 1)]['type'] === 'T_WHITESPACE') {
227
                $error = 'There must be no space before the colon in a DEFAULT statement';
228
                $phpcsFile->addError($error, $default);
229
            }
230
 
231
            if ($tokens[$default]['column'] !== $caseAlignment) {
232
                $error = 'DEFAULT keyword must be indented 4 spaces from SWITCH keyword';
233
                $phpcsFile->addError($error, $default);
234
            }
235
 
236
            $nextBreak = $phpcsFile->findNext(array(T_BREAK), ($default + 1), $switch['scope_closer']);
237
            if ($nextBreak !== false) {
238
                if ($tokens[$nextBreak]['column'] !== $caseAlignment) {
239
                    $error = 'BREAK statement must be indented 4 spaces from SWITCH keyword';
240
                    $phpcsFile->addError($error, $nextBreak);
241
                }
242
 
243
                /*
244
                    Ensure the BREAK statement is not followed by
245
                    a blank line.
246
                */
247
 
248
                $breakLine = $tokens[$nextBreak]['line'];
249
                $nextLine  = $tokens[$tokens[$stackPtr]['scope_closer']]['line'];
250
                $semicolon = $phpcsFile->findNext(T_SEMICOLON, $nextBreak);
251
                for ($i = ($semicolon + 1); $i < $tokens[$stackPtr]['scope_closer']; $i++) {
252
                    if ($tokens[$i]['type'] !== 'T_WHITESPACE') {
253
                        $nextLine = $tokens[$i]['line'];
254
                        break;
255
                    }
256
                }
257
 
258
                if ($nextLine !== ($breakLine + 1)) {
259
                    $error = 'Blank lines are not allowed after the DEFAULT case\'s BREAK statement';
260
                    $phpcsFile->addError($error, $nextBreak);
261
                }
262
            } else {
263
                $error = 'DEFAULT case must have a BREAK statement';
264
                $phpcsFile->addError($error, $default);
265
 
266
                $nextBreak = $tokens[$default]['scope_closer'];
267
            }//end if
268
 
269
            /*
270
                Ensure empty DEFAULT statements are not allowed.
271
                They must (at least) have a comment describing why
272
                the default case is being ignored.
273
            */
274
 
275
            $foundContent = false;
276
            for ($i = ($tokens[$default]['scope_opener'] + 1); $i < $nextBreak; $i++) {
277
                if ($tokens[$i]['type'] !== 'T_WHITESPACE') {
278
                    $foundContent = true;
279
                    break;
280
                }
281
            }
282
 
283
            if ($foundContent === false) {
284
                $error = 'Comment required for empty DEFAULT case';
285
                $phpcsFile->addError($error, $default);
286
            }
287
 
288
            /*
289
                Ensure DEFAULT statements are not followed by
290
                blank lines.
291
            */
292
 
293
            $defaultLine = $tokens[$default]['line'];
294
            $nextLine    = $tokens[$nextBreak]['line'];
295
            for ($i = ($opener + 1); $i < $nextBreak; $i++) {
296
                if ($tokens[$i]['type'] !== 'T_WHITESPACE') {
297
                    $nextLine = $tokens[$i]['line'];
298
                    break;
299
                }
300
            }
301
 
302
            if ($nextLine !== ($defaultLine + 1)) {
303
                $error = 'Blank lines are not allowed after DEFAULT statements';
304
                $phpcsFile->addError($error, $default);
305
            }
306
 
307
        } else {
308
            $error = 'All SWITCH statements must contain a DEFAULT case';
309
            $phpcsFile->addError($error, $stackPtr);
310
        }//end if
311
 
312
        if ($tokens[$switch['scope_closer']]['column'] !== $switch['column']) {
313
            $error = 'Closing brace of SWITCH statement must be aligned with SWITCH keyword';
314
            $phpcsFile->addError($error, $switch['scope_closer']);
315
        }
316
 
317
        if ($caseCount === 0) {
318
            $error = 'SWITCH statements must contain at least one CASE statement';
319
            $phpcsFile->addError($error, $stackPtr);
320
        }
321
 
322
    }//end process()
323
 
324
 
325
}//end class
326
 
327
?>