@@ -67,6 +67,11 @@ public function process(File $phpcsFile, $stackPtr) {
6767 $ this ->requiredSpacesBeforeClose = (int )$ this ->requiredSpacesBeforeClose ;
6868 $ tokens = $ phpcsFile ->getTokens ();
6969
70+ // Handle else/elseif brace positioning
71+ if ($ tokens [$ stackPtr ]['code ' ] === T_ELSE || $ tokens [$ stackPtr ]['code ' ] === T_ELSEIF ) {
72+ $ this ->processElseSpacing ($ phpcsFile , $ stackPtr );
73+ }
74+
7075 if (isset ($ tokens [$ stackPtr ]['parenthesis_opener ' ]) === false
7176 || isset ($ tokens [$ stackPtr ]['parenthesis_closer ' ]) === false
7277 ) {
@@ -139,4 +144,115 @@ public function process(File $phpcsFile, $stackPtr) {
139144 }
140145 }
141146
147+ /**
148+ * Process else/elseif spacing and brace positioning.
149+ *
150+ * @param \PHP_CodeSniffer\Files\File $phpcsFile
151+ * @param int $stackPtr Position of else/elseif token
152+ *
153+ * @return void
154+ */
155+ protected function processElseSpacing (File $ phpcsFile , int $ stackPtr ): void {
156+ $ tokens = $ phpcsFile ->getTokens ();
157+
158+ // Find the previous non-whitespace token (should be closing brace)
159+ $ prevNonWhitespace = $ phpcsFile ->findPrevious (T_WHITESPACE , $ stackPtr - 1 , null , true );
160+
161+ if ($ prevNonWhitespace === false || $ tokens [$ prevNonWhitespace ]['code ' ] !== T_CLOSE_CURLY_BRACKET ) {
162+ // No closing brace before else/elseif, skip
163+ return ;
164+ }
165+
166+ $ closingBrace = $ prevNonWhitespace ;
167+ $ elseToken = $ stackPtr ;
168+ $ keyword = $ tokens [$ elseToken ]['code ' ] === T_ELSE ? 'else ' : 'elseif ' ;
169+
170+ // Check if closing brace and else/elseif are on the same line
171+ if ($ tokens [$ closingBrace ]['line ' ] !== $ tokens [$ elseToken ]['line ' ]) {
172+ // They are on different lines - we need to fix this
173+ $ error = sprintf ('Expected "} %s" on the same line; found closing brace and %s on different lines ' , $ keyword , $ keyword );
174+ $ fix = $ phpcsFile ->addFixableError ($ error , $ elseToken , 'SpacingBetweenBraceAndKeyword ' );
175+
176+ if ($ fix ) {
177+ // Fix: Remove all whitespace between closing brace and else/elseif, add single space
178+ $ phpcsFile ->fixer ->beginChangeset ();
179+
180+ // Remove all tokens between closing brace and else/elseif
181+ for ($ i = $ closingBrace + 1 ; $ i < $ elseToken ; $ i ++) {
182+ $ phpcsFile ->fixer ->replaceToken ($ i , '' );
183+ }
184+
185+ // Add single space after closing brace
186+ $ phpcsFile ->fixer ->addContent ($ closingBrace , ' ' );
187+
188+ $ phpcsFile ->fixer ->endChangeset ();
189+ }
190+ }
191+
192+ // Check opening brace position
193+ $ this ->checkOpeningBrace ($ phpcsFile , $ stackPtr );
194+ }
195+
196+ /**
197+ * Check and fix the opening brace position for else/elseif.
198+ *
199+ * @param \PHP_CodeSniffer\Files\File $phpcsFile
200+ * @param int $stackPtr Position of else/elseif token
201+ *
202+ * @return void
203+ */
204+ protected function checkOpeningBrace (File $ phpcsFile , int $ stackPtr ): void {
205+ $ tokens = $ phpcsFile ->getTokens ();
206+ $ elseToken = $ stackPtr ;
207+ $ keyword = $ tokens [$ elseToken ]['code ' ] === T_ELSE ? 'else ' : 'elseif ' ;
208+
209+ // For elseif, we need to find the closing parenthesis first
210+ if ($ tokens [$ elseToken ]['code ' ] === T_ELSEIF ) {
211+ $ openParen = $ phpcsFile ->findNext (T_OPEN_PARENTHESIS , $ elseToken + 1 , null , false , null , true );
212+ if ($ openParen === false || empty ($ tokens [$ openParen ]['parenthesis_closer ' ])) {
213+ return ;
214+ }
215+ $ closeParen = $ tokens [$ openParen ]['parenthesis_closer ' ];
216+ $ searchStart = $ closeParen + 1 ;
217+ } else {
218+ // For else, search starts right after else keyword
219+ $ searchStart = $ elseToken + 1 ;
220+ }
221+
222+ // Find the opening brace
223+ $ openingBrace = $ phpcsFile ->findNext (T_OPEN_CURLY_BRACKET , $ searchStart , null , false , null , true );
224+ if ($ openingBrace === false ) {
225+ return ;
226+ }
227+
228+ // Find the token right before the opening brace (should be closing paren for elseif, or else keyword)
229+ $ prevNonWhitespace = $ phpcsFile ->findPrevious (T_WHITESPACE , $ openingBrace - 1 , null , true );
230+ if ($ prevNonWhitespace === false ) {
231+ return ;
232+ }
233+
234+ // Check if they are on the same line
235+ if ($ tokens [$ prevNonWhitespace ]['line ' ] !== $ tokens [$ openingBrace ]['line ' ]) {
236+ // They are on different lines - we need to fix this
237+ $ expectedFormat = $ tokens [$ elseToken ]['code ' ] === T_ELSEIF ? '} elseif (...) { ' : '} else { ' ;
238+ $ error = sprintf ('Expected "%s" with opening brace on the same line; found opening brace on different line ' , $ expectedFormat );
239+ $ fix = $ phpcsFile ->addFixableError ($ error , $ openingBrace , 'OpeningBraceOnDifferentLine ' );
240+
241+ if ($ fix ) {
242+ // Fix: Remove all whitespace between previous token and opening brace, add single space
243+ $ phpcsFile ->fixer ->beginChangeset ();
244+
245+ // Remove all tokens between previous token and opening brace
246+ for ($ i = $ prevNonWhitespace + 1 ; $ i < $ openingBrace ; $ i ++) {
247+ $ phpcsFile ->fixer ->replaceToken ($ i , '' );
248+ }
249+
250+ // Add single space after previous token
251+ $ phpcsFile ->fixer ->addContent ($ prevNonWhitespace , ' ' );
252+
253+ $ phpcsFile ->fixer ->endChangeset ();
254+ }
255+ }
256+ }
257+
142258}
0 commit comments