Chris@0
|
1 <?php
|
Chris@0
|
2 /**
|
Chris@17
|
3 * \Drupal\Sniffs\WhiteSpace\ScopeClosingBraceSniff.
|
Chris@0
|
4 *
|
Chris@0
|
5 * @category PHP
|
Chris@0
|
6 * @package PHP_CodeSniffer
|
Chris@0
|
7 * @link http://Drupal.php.net/package/PHP_CodeSniffer
|
Chris@0
|
8 */
|
Chris@0
|
9
|
Chris@17
|
10 namespace Drupal\Sniffs\WhiteSpace;
|
Chris@17
|
11
|
Chris@17
|
12 use PHP_CodeSniffer\Files\File;
|
Chris@17
|
13 use PHP_CodeSniffer\Sniffs\Sniff;
|
Chris@17
|
14 use PHP_CodeSniffer\Util\Tokens;
|
Chris@17
|
15
|
Chris@0
|
16 /**
|
Chris@17
|
17 * Copied from \PHP_CodeSniffer\Standards\PEAR\Sniffs\WhiteSpace\ScopeClosingBraceSniff to allow empty methods
|
Chris@0
|
18 * and classes.
|
Chris@0
|
19 *
|
Chris@0
|
20 * Checks that the closing braces of scopes are aligned correctly.
|
Chris@0
|
21 *
|
Chris@0
|
22 * @category PHP
|
Chris@0
|
23 * @package PHP_CodeSniffer
|
Chris@0
|
24 * @link http://pear.php.net/package/PHP_CodeSniffer
|
Chris@0
|
25 */
|
Chris@17
|
26 class ScopeClosingBraceSniff implements Sniff
|
Chris@0
|
27 {
|
Chris@0
|
28
|
Chris@0
|
29 /**
|
Chris@0
|
30 * The number of spaces code should be indented.
|
Chris@0
|
31 *
|
Chris@0
|
32 * @var int
|
Chris@0
|
33 */
|
Chris@0
|
34 public $indent = 2;
|
Chris@0
|
35
|
Chris@0
|
36
|
Chris@0
|
37 /**
|
Chris@0
|
38 * Returns an array of tokens this test wants to listen for.
|
Chris@0
|
39 *
|
Chris@0
|
40 * @return array
|
Chris@0
|
41 */
|
Chris@0
|
42 public function register()
|
Chris@0
|
43 {
|
Chris@17
|
44 return Tokens::$scopeOpeners;
|
Chris@0
|
45
|
Chris@0
|
46 }//end register()
|
Chris@0
|
47
|
Chris@0
|
48
|
Chris@0
|
49 /**
|
Chris@0
|
50 * Processes this test, when one of its tokens is encountered.
|
Chris@0
|
51 *
|
Chris@17
|
52 * @param \PHP_CodeSniffer\Files\File $phpcsFile All the tokens found in the document.
|
Chris@17
|
53 * @param int $stackPtr The position of the current token
|
Chris@17
|
54 * in the stack passed in $tokens.
|
Chris@0
|
55 *
|
Chris@0
|
56 * @return void
|
Chris@0
|
57 */
|
Chris@17
|
58 public function process(File $phpcsFile, $stackPtr)
|
Chris@0
|
59 {
|
Chris@0
|
60 $tokens = $phpcsFile->getTokens();
|
Chris@0
|
61
|
Chris@0
|
62 // If this is an inline condition (ie. there is no scope opener), then
|
Chris@0
|
63 // return, as this is not a new scope.
|
Chris@0
|
64 if (isset($tokens[$stackPtr]['scope_closer']) === false) {
|
Chris@0
|
65 return;
|
Chris@0
|
66 }
|
Chris@0
|
67
|
Chris@0
|
68 $scopeStart = $tokens[$stackPtr]['scope_opener'];
|
Chris@0
|
69 $scopeEnd = $tokens[$stackPtr]['scope_closer'];
|
Chris@0
|
70
|
Chris@0
|
71 // If the scope closer doesn't think it belongs to this scope opener
|
Chris@0
|
72 // then the opener is sharing its closer with other tokens. We only
|
Chris@0
|
73 // want to process the closer once, so skip this one.
|
Chris@0
|
74 if (isset($tokens[$scopeEnd]['scope_condition']) === false
|
Chris@0
|
75 || $tokens[$scopeEnd]['scope_condition'] !== $stackPtr
|
Chris@0
|
76 ) {
|
Chris@0
|
77 return;
|
Chris@0
|
78 }
|
Chris@0
|
79
|
Chris@0
|
80 // We need to actually find the first piece of content on this line,
|
Chris@0
|
81 // because if this is a method with tokens before it (public, static etc)
|
Chris@0
|
82 // or an if with an else before it, then we need to start the scope
|
Chris@0
|
83 // checking from there, rather than the current token.
|
Chris@0
|
84 $lineStart = ($stackPtr - 1);
|
Chris@0
|
85 for ($lineStart; $lineStart > 0; $lineStart--) {
|
Chris@0
|
86 if (strpos($tokens[$lineStart]['content'], $phpcsFile->eolChar) !== false) {
|
Chris@0
|
87 break;
|
Chris@0
|
88 }
|
Chris@0
|
89 }
|
Chris@0
|
90
|
Chris@0
|
91 $lineStart++;
|
Chris@0
|
92
|
Chris@0
|
93 $startColumn = 1;
|
Chris@0
|
94 if ($tokens[$lineStart]['code'] === T_WHITESPACE) {
|
Chris@0
|
95 $startColumn = $tokens[($lineStart + 1)]['column'];
|
Chris@0
|
96 } else if ($tokens[$lineStart]['code'] === T_INLINE_HTML) {
|
Chris@0
|
97 $trimmed = ltrim($tokens[$lineStart]['content']);
|
Chris@0
|
98 if ($trimmed === '') {
|
Chris@0
|
99 $startColumn = $tokens[($lineStart + 1)]['column'];
|
Chris@0
|
100 } else {
|
Chris@0
|
101 $startColumn = (strlen($tokens[$lineStart]['content']) - strlen($trimmed));
|
Chris@0
|
102 }
|
Chris@0
|
103 }
|
Chris@0
|
104
|
Chris@0
|
105 // Check that the closing brace is on it's own line.
|
Chris@0
|
106 $lastContent = $phpcsFile->findPrevious(
|
Chris@0
|
107 array(
|
Chris@0
|
108 T_WHITESPACE,
|
Chris@0
|
109 T_INLINE_HTML,
|
Chris@0
|
110 T_OPEN_TAG,
|
Chris@0
|
111 ),
|
Chris@0
|
112 ($scopeEnd - 1),
|
Chris@0
|
113 $scopeStart,
|
Chris@0
|
114 true
|
Chris@0
|
115 );
|
Chris@0
|
116
|
Chris@0
|
117 if ($tokens[$lastContent]['line'] === $tokens[$scopeEnd]['line']) {
|
Chris@0
|
118 // Only allow empty classes and methods.
|
Chris@0
|
119 if (($tokens[$tokens[$scopeEnd]['scope_condition']]['code'] !== T_CLASS
|
Chris@0
|
120 && $tokens[$tokens[$scopeEnd]['scope_condition']]['code'] !== T_INTERFACE
|
Chris@0
|
121 && in_array(T_CLASS, $tokens[$scopeEnd]['conditions']) === false
|
Chris@0
|
122 && in_array(T_INTERFACE, $tokens[$scopeEnd]['conditions']) === false)
|
Chris@0
|
123 || $tokens[$lastContent]['code'] !== T_OPEN_CURLY_BRACKET
|
Chris@0
|
124 ) {
|
Chris@0
|
125 $error = 'Closing brace must be on a line by itself';
|
Chris@0
|
126 $fix = $phpcsFile->addFixableError($error, $scopeEnd, 'Line');
|
Chris@0
|
127 if ($fix === true) {
|
Chris@0
|
128 $phpcsFile->fixer->addNewlineBefore($scopeEnd);
|
Chris@0
|
129 }
|
Chris@0
|
130 }
|
Chris@0
|
131
|
Chris@0
|
132 return;
|
Chris@0
|
133 }
|
Chris@0
|
134
|
Chris@0
|
135 // Check now that the closing brace is lined up correctly.
|
Chris@0
|
136 $lineStart = ($scopeEnd - 1);
|
Chris@0
|
137 for ($lineStart; $lineStart > 0; $lineStart--) {
|
Chris@0
|
138 if (strpos($tokens[$lineStart]['content'], $phpcsFile->eolChar) !== false) {
|
Chris@0
|
139 break;
|
Chris@0
|
140 }
|
Chris@0
|
141 }
|
Chris@0
|
142
|
Chris@0
|
143 $lineStart++;
|
Chris@0
|
144
|
Chris@0
|
145 $braceIndent = 0;
|
Chris@0
|
146 if ($tokens[$lineStart]['code'] === T_WHITESPACE) {
|
Chris@0
|
147 $braceIndent = ($tokens[($lineStart + 1)]['column'] - 1);
|
Chris@0
|
148 } else if ($tokens[$lineStart]['code'] === T_INLINE_HTML) {
|
Chris@0
|
149 $trimmed = ltrim($tokens[$lineStart]['content']);
|
Chris@0
|
150 if ($trimmed === '') {
|
Chris@0
|
151 $braceIndent = ($tokens[($lineStart + 1)]['column'] - 1);
|
Chris@0
|
152 } else {
|
Chris@0
|
153 $braceIndent = (strlen($tokens[$lineStart]['content']) - strlen($trimmed) - 1);
|
Chris@0
|
154 }
|
Chris@0
|
155 }
|
Chris@0
|
156
|
Chris@0
|
157 $fix = false;
|
Chris@0
|
158 if ($tokens[$stackPtr]['code'] === T_CASE
|
Chris@0
|
159 || $tokens[$stackPtr]['code'] === T_DEFAULT
|
Chris@0
|
160 ) {
|
Chris@0
|
161 // BREAK statements should be indented n spaces from the
|
Chris@0
|
162 // CASE or DEFAULT statement.
|
Chris@0
|
163 $expectedIndent = ($startColumn + $this->indent - 1);
|
Chris@0
|
164 if ($braceIndent !== $expectedIndent) {
|
Chris@0
|
165 $error = 'Case breaking statement indented incorrectly; expected %s spaces, found %s';
|
Chris@0
|
166 $data = array(
|
Chris@0
|
167 $expectedIndent,
|
Chris@0
|
168 $braceIndent,
|
Chris@0
|
169 );
|
Chris@0
|
170 $fix = $phpcsFile->addFixableError($error, $scopeEnd, 'BreakIndent', $data);
|
Chris@0
|
171 }
|
Chris@0
|
172 } else {
|
Chris@0
|
173 $expectedIndent = ($startColumn - 1);
|
Chris@0
|
174 if ($braceIndent !== $expectedIndent) {
|
Chris@0
|
175 $error = 'Closing brace indented incorrectly; expected %s spaces, found %s';
|
Chris@0
|
176 $data = array(
|
Chris@0
|
177 $expectedIndent,
|
Chris@0
|
178 $braceIndent,
|
Chris@0
|
179 );
|
Chris@0
|
180 $fix = $phpcsFile->addFixableError($error, $scopeEnd, 'Indent', $data);
|
Chris@0
|
181 }
|
Chris@0
|
182 }//end if
|
Chris@0
|
183
|
Chris@0
|
184 if ($fix === true) {
|
Chris@0
|
185 $spaces = str_repeat(' ', $expectedIndent);
|
Chris@0
|
186 if ($braceIndent === 0) {
|
Chris@0
|
187 $phpcsFile->fixer->addContentBefore($lineStart, $spaces);
|
Chris@0
|
188 } else {
|
Chris@0
|
189 $phpcsFile->fixer->replaceToken($lineStart, ltrim($tokens[$lineStart]['content']));
|
Chris@0
|
190 $phpcsFile->fixer->addContentBefore($lineStart, $spaces);
|
Chris@0
|
191 }
|
Chris@0
|
192 }
|
Chris@0
|
193
|
Chris@0
|
194 }//end process()
|
Chris@0
|
195
|
Chris@0
|
196
|
Chris@0
|
197 }//end class
|