1<?php
2/**
3 * Checks that the opening brace of a class/interface/trait is on the same line as the class declaration.
4 *
5 * @author    Greg Sherwood <gsherwood@squiz.net>
6 * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
7 * @license   https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
8 */
9
10namespace PHP_CodeSniffer\Standards\Generic\Sniffs\Classes;
11
12use PHP_CodeSniffer\Files\File;
13use PHP_CodeSniffer\Sniffs\Sniff;
14
15class OpeningBraceSameLineSniff implements Sniff
16{
17
18
19    /**
20     * Returns an array of tokens this test wants to listen for.
21     *
22     * @return array
23     */
24    public function register()
25    {
26        return [
27            T_CLASS,
28            T_INTERFACE,
29            T_TRAIT,
30        ];
31
32    }//end register()
33
34
35    /**
36     * Processes this test, when one of its tokens is encountered.
37     *
38     * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
39     * @param int                         $stackPtr  The position of the current token
40     *                                               in the stack passed in $tokens.
41     *
42     * @return void
43     */
44    public function process(File $phpcsFile, $stackPtr)
45    {
46        $tokens          = $phpcsFile->getTokens();
47        $scopeIdentifier = $phpcsFile->findNext(T_STRING, ($stackPtr + 1));
48        $errorData       = [strtolower($tokens[$stackPtr]['content']).' '.$tokens[$scopeIdentifier]['content']];
49
50        if (isset($tokens[$stackPtr]['scope_opener']) === false) {
51            $error = 'Possible parse error: %s missing opening or closing brace';
52            $phpcsFile->addWarning($error, $stackPtr, 'MissingBrace', $errorData);
53            return;
54        }
55
56        $openingBrace = $tokens[$stackPtr]['scope_opener'];
57
58        // Is the brace on the same line as the class/interface/trait declaration ?
59        $lastClassLineToken = $phpcsFile->findPrevious(T_WHITESPACE, ($openingBrace - 1), $stackPtr, true);
60        $lastClassLine      = $tokens[$lastClassLineToken]['line'];
61        $braceLine          = $tokens[$openingBrace]['line'];
62        $lineDifference     = ($braceLine - $lastClassLine);
63
64        if ($lineDifference > 0) {
65            $phpcsFile->recordMetric($stackPtr, 'Class opening brace placement', 'new line');
66            $error = 'Opening brace should be on the same line as the declaration for %s';
67            $fix   = $phpcsFile->addFixableError($error, $openingBrace, 'BraceOnNewLine', $errorData);
68            if ($fix === true) {
69                $phpcsFile->fixer->beginChangeset();
70                $phpcsFile->fixer->addContent($lastClassLineToken, ' {');
71                $phpcsFile->fixer->replaceToken($openingBrace, '');
72                $phpcsFile->fixer->endChangeset();
73            }
74        } else {
75            $phpcsFile->recordMetric($stackPtr, 'Class opening brace placement', 'same line');
76        }
77
78        // Is the opening brace the last thing on the line ?
79        $next = $phpcsFile->findNext(T_WHITESPACE, ($openingBrace + 1), null, true);
80        if ($tokens[$next]['line'] === $tokens[$openingBrace]['line']) {
81            if ($next === $tokens[$stackPtr]['scope_closer']) {
82                // Ignore empty classes.
83                return;
84            }
85
86            $error = 'Opening brace must be the last content on the line';
87            $fix   = $phpcsFile->addFixableError($error, $openingBrace, 'ContentAfterBrace');
88            if ($fix === true) {
89                $phpcsFile->fixer->addNewline($openingBrace);
90            }
91        }
92
93        // Only continue checking if the opening brace looks good.
94        if ($lineDifference > 0) {
95            return;
96        }
97
98        // Is there precisely one space before the opening brace ?
99        if ($tokens[($openingBrace - 1)]['code'] !== T_WHITESPACE) {
100            $length = 0;
101        } else if ($tokens[($openingBrace - 1)]['content'] === "\t") {
102            $length = '\t';
103        } else {
104            $length = $tokens[($openingBrace - 1)]['length'];
105        }
106
107        if ($length !== 1) {
108            $error = 'Expected 1 space before opening brace; found %s';
109            $data  = [$length];
110            $fix   = $phpcsFile->addFixableError($error, $openingBrace, 'SpaceBeforeBrace', $data);
111            if ($fix === true) {
112                if ($length === 0 || $length === '\t') {
113                    $phpcsFile->fixer->addContentBefore($openingBrace, ' ');
114                } else {
115                    $phpcsFile->fixer->replaceToken(($openingBrace - 1), ' ');
116                }
117            }
118        }
119
120    }//end process()
121
122
123}//end class
124