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