3
* Squiz_Sniffs_ControlStructures_SwitchDeclarationSniff.
8
* @package PHP_CodeSniffer
9
* @author Greg Sherwood <gsherwood@squiz.net>
10
* @author Marc McIntyre <mmcintyre@squiz.net>
11
* @copyright 2006-2011 Squiz Pty Ltd (ABN 77 084 670 600)
12
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
13
* @link http://pear.php.net/package/PHP_CodeSniffer
17
* Squiz_Sniffs_ControlStructures_SwitchDeclarationSniff.
19
* Ensures all the breaks and cases are aligned correctly according to their
20
* parent switch's alignment and enforces other switch formatting.
23
* @package PHP_CodeSniffer
24
* @author Greg Sherwood <gsherwood@squiz.net>
25
* @author Marc McIntyre <mmcintyre@squiz.net>
26
* @copyright 2006-2011 Squiz Pty Ltd (ABN 77 084 670 600)
27
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
28
* @version Release: 1.3.4
29
* @link http://pear.php.net/package/PHP_CodeSniffer
31
class Squiz_Sniffs_ControlStructures_SwitchDeclarationSniff implements PHP_CodeSniffer_Sniff
35
* A list of tokenizers this sniff supports.
39
public $supportedTokenizers = array(
46
* Returns an array of tokens this test wants to listen for.
50
public function register()
52
return array(T_SWITCH);
58
* Processes this test, when one of its tokens is encountered.
60
* @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
61
* @param int $stackPtr The position of the current token in the
62
* stack passed in $tokens.
66
public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
68
$tokens = $phpcsFile->getTokens();
70
// We can't process SWITCH statements unless we know where they start and end.
71
if (isset($tokens[$stackPtr]['scope_opener']) === false
72
|| isset($tokens[$stackPtr]['scope_closer']) === false
77
$switch = $tokens[$stackPtr];
78
$nextCase = $stackPtr;
79
$caseAlignment = ($switch['column'] + 4);
81
$foundDefault = false;
83
while (($nextCase = $phpcsFile->findNext(array(T_CASE, T_DEFAULT, T_SWITCH), ($nextCase + 1), $switch['scope_closer'])) !== false) {
84
// Skip nested SWITCH statements; they are handled on their own.
85
if ($tokens[$nextCase]['code'] === T_SWITCH) {
86
$nextCase = $tokens[$nextCase]['scope_closer'];
90
if ($tokens[$nextCase]['code'] === T_DEFAULT) {
98
if ($tokens[$nextCase]['content'] !== strtolower($tokens[$nextCase]['content'])) {
99
$expected = strtolower($tokens[$nextCase]['content']);
100
$error = strtoupper($type).' keyword must be lowercase; expected "%s" but found "%s"';
103
$tokens[$nextCase]['content'],
105
$phpcsFile->addError($error, $nextCase, $type.'NotLower', $data);
108
if ($tokens[$nextCase]['column'] !== $caseAlignment) {
109
$error = strtoupper($type).' keyword must be indented 4 spaces from SWITCH keyword';
110
$phpcsFile->addError($error, $nextCase, $type.'Indent');
114
&& ($tokens[($nextCase + 1)]['type'] !== 'T_WHITESPACE'
115
|| $tokens[($nextCase + 1)]['content'] !== ' ')
117
$error = 'CASE keyword must be followed by a single space';
118
$phpcsFile->addError($error, $nextCase, 'SpacingAfterCase');
121
$opener = $tokens[$nextCase]['scope_opener'];
122
if ($tokens[($opener - 1)]['type'] === 'T_WHITESPACE') {
123
$error = 'There must be no space before the colon in a '.strtoupper($type).' statement';
124
$phpcsFile->addError($error, $nextCase, 'SpaceBeforeColon'.$type);
127
$nextBreak = $tokens[$nextCase]['scope_closer'];
128
if ($tokens[$nextBreak]['code'] === T_BREAK) {
129
if ($tokens[$nextBreak]['scope_condition'] === $nextCase) {
130
// Only need to check a couple of things once, even if the
131
// break is shared between multiple case statements, or even
133
if ($tokens[$nextBreak]['column'] !== $caseAlignment) {
134
$error = 'BREAK statement must be indented 4 spaces from SWITCH keyword';
135
$phpcsFile->addError($error, $nextBreak, 'BreakIndent');
138
$breakLine = $tokens[$nextBreak]['line'];
140
for ($i = ($nextBreak - 1); $i > $stackPtr; $i--) {
141
if ($tokens[$i]['type'] !== 'T_WHITESPACE') {
142
$prevLine = $tokens[$i]['line'];
147
if ($prevLine !== ($breakLine - 1)) {
148
$error = 'Blank lines are not allowed before BREAK statements';
149
$phpcsFile->addError($error, $nextBreak, 'SpacingBeforeBreak');
152
$breakLine = $tokens[$nextBreak]['line'];
153
$nextLine = $tokens[$tokens[$stackPtr]['scope_closer']]['line'];
154
$semicolon = $phpcsFile->findNext(T_SEMICOLON, $nextBreak);
155
for ($i = ($semicolon + 1); $i < $tokens[$stackPtr]['scope_closer']; $i++) {
156
if ($tokens[$i]['type'] !== 'T_WHITESPACE') {
157
$nextLine = $tokens[$i]['line'];
162
if ($type === 'Case') {
163
// Ensure the BREAK statement is followed by
164
// a single blank line, or the end switch brace.
165
if ($nextLine !== ($breakLine + 2) && $i !== $tokens[$stackPtr]['scope_closer']) {
166
$error = 'BREAK statements must be followed by a single blank line';
167
$phpcsFile->addError($error, $nextBreak, 'SpacingAfterBreak');
170
// Ensure the BREAK statement is not followed by a blank line.
171
if ($nextLine !== ($breakLine + 1)) {
172
$error = 'Blank lines are not allowed after the DEFAULT case\'s BREAK statement';
173
$phpcsFile->addError($error, $nextBreak, 'SpacingAfterDefaultBreak');
177
$caseLine = $tokens[$nextCase]['line'];
178
$nextLine = $tokens[$nextBreak]['line'];
179
for ($i = ($opener + 1); $i < $nextBreak; $i++) {
180
if ($tokens[$i]['type'] !== 'T_WHITESPACE') {
181
$nextLine = $tokens[$i]['line'];
186
if ($nextLine !== ($caseLine + 1)) {
187
$error = 'Blank lines are not allowed after '.strtoupper($type).' statements';
188
$phpcsFile->addError($error, $nextCase, 'SpacingAfter'.$type);
192
if ($type === 'Case') {
193
// Ensure empty CASE statements are not allowed.
194
// They must have some code content in them. A comment is not enough.
195
$foundContent = false;
196
for ($i = ($tokens[$nextCase]['scope_opener'] + 1); $i < $nextBreak; $i++) {
197
if ($tokens[$i]['code'] === T_CASE) {
198
$i = $tokens[$i]['scope_opener'];
202
if (in_array($tokens[$i]['code'], PHP_CodeSniffer_Tokens::$emptyTokens) === false) {
203
$foundContent = true;
208
if ($foundContent === false) {
209
$error = 'Empty CASE statements are not allowed';
210
$phpcsFile->addError($error, $nextCase, 'EmptyCase');
213
// Ensure empty DEFAULT statements are not allowed.
214
// They must (at least) have a comment describing why
215
// the default case is being ignored.
216
$foundContent = false;
217
for ($i = ($tokens[$nextCase]['scope_opener'] + 1); $i < $nextBreak; $i++) {
218
if ($tokens[$i]['type'] !== 'T_WHITESPACE') {
219
$foundContent = true;
224
if ($foundContent === false) {
225
$error = 'Comment required for empty DEFAULT case';
226
$phpcsFile->addError($error, $nextCase, 'EmptyDefault');
229
} else if ($type === 'Default') {
230
$error = 'DEFAULT case must have a BREAK statement';
231
$phpcsFile->addError($error, $nextCase, 'DefaultNoBreak');
235
if ($foundDefault === false) {
236
$error = 'All SWITCH statements must contain a DEFAULT case';
237
$phpcsFile->addError($error, $stackPtr, 'MissingDefault');
240
if ($tokens[$switch['scope_closer']]['column'] !== $switch['column']) {
241
$error = 'Closing brace of SWITCH statement must be aligned with SWITCH keyword';
242
$phpcsFile->addError($error, $switch['scope_closer'], 'CloseBraceAlign');
245
if ($caseCount === 0) {
246
$error = 'SWITCH statements must contain at least one CASE statement';
247
$phpcsFile->addError($error, $stackPtr, 'MissingCase');