Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 58
0.00% covered (danger)
0.00%
0 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
SpaceBeforeClassBraceSniff
0.00% covered (danger)
0.00%
0 / 58
0.00% covered (danger)
0.00%
0 / 3
462
0.00% covered (danger)
0.00%
0 / 1
 register
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 process
0.00% covered (danger)
0.00%
0 / 47
0.00% covered (danger)
0.00%
0 / 1
182
 getStructureKeyword
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
56
1<?php
2/**
3 * make sure a space before class open brace.
4 * fail: class TestClass\t{
5 * fail: class TestClass{
6 * fail: class TestClass   {
7 * pass: class TestClass {
8 */
9
10namespace MediaWiki\Sniffs\WhiteSpace;
11
12use PHP_CodeSniffer\Files\File;
13use PHP_CodeSniffer\Sniffs\Sniff;
14use PHP_CodeSniffer\Util\Tokens;
15use UnexpectedValueException;
16
17class SpaceBeforeClassBraceSniff implements Sniff {
18    /**
19     * @inheritDoc
20     */
21    public function register(): array {
22        return Tokens::$ooScopeTokens;
23    }
24
25    /**
26     * @param File $phpcsFile
27     * @param int $stackPtr The index of current token.
28     * @return void
29     */
30    public function process( File $phpcsFile, $stackPtr ) {
31        $tokens = $phpcsFile->getTokens();
32        if ( !isset( $tokens[$stackPtr]['scope_opener'] ) ) {
33            return;
34        }
35        $structKeyword = $this->getStructureKeyword( $tokens[$stackPtr]['code'] );
36        $openBrace = $tokens[$stackPtr]['scope_opener'];
37        // Find previous non-whitespace token from the opening brace
38        $pre = $phpcsFile->findPrevious( T_WHITESPACE, $openBrace - 1, null, true );
39
40        $afterClass = $stackPtr;
41        if ( $tokens[$stackPtr]['code'] === T_ANON_CLASS && isset( $tokens[$stackPtr]['parenthesis_closer'] ) ) {
42            // The anon class could be multi-line, start after the class definition
43            $afterClass = $tokens[$stackPtr]['parenthesis_closer'];
44        }
45
46        if ( $tokens[$openBrace]['line'] - $tokens[$afterClass]['line'] >= 2 ) {
47            // If the class ... { statement is more than two lines, then
48            // the { should be on a line by itself.
49            if ( $tokens[$pre]['line'] === $tokens[$openBrace]['line'] ) {
50                $fix = $phpcsFile->addFixableWarning(
51                    "Expected $structKeyword open brace to be on a new line",
52                    $openBrace,
53                    'BraceNotOnOwnLine'
54                );
55                if ( $fix ) {
56                    $phpcsFile->fixer->addNewlineBefore( $openBrace );
57                }
58            }
59            return;
60        }
61        $spaceCount = 0;
62        for ( $start = $pre + 1; $start < $openBrace; $start++ ) {
63            $content = $tokens[$start]['content'];
64            $contentSize = strlen( $content );
65            $spaceCount += $contentSize;
66        }
67
68        if ( $spaceCount !== 1 ) {
69            $fix = $phpcsFile->addFixableWarning(
70                "Expected 1 space before $structKeyword open brace. Found %s.",
71                $openBrace,
72                'NoSpaceBeforeBrace',
73                [ $spaceCount ]
74            );
75            if ( $fix ) {
76                $phpcsFile->fixer->beginChangeset();
77                $phpcsFile->fixer->replaceToken( $openBrace, '' );
78                $phpcsFile->fixer->addContent( $pre, ' {' );
79                $phpcsFile->fixer->endChangeset();
80            }
81        }
82
83        if ( $tokens[$openBrace]['line'] !== $tokens[$pre]['line'] ) {
84            $fix = $phpcsFile->addFixableWarning(
85                "Expected $structKeyword open brace to be on the same line as the `$structKeyword` keyword.",
86                $openBrace,
87                'BraceNotOnSameLine'
88            );
89            if ( $fix ) {
90                $phpcsFile->fixer->beginChangeset();
91                for ( $i = $pre + 1; $i < $openBrace; $i++ ) {
92                    $phpcsFile->fixer->replaceToken( $i, '' );
93                }
94                $phpcsFile->fixer->endChangeset();
95            }
96        }
97    }
98
99    /**
100     * Returns the keyword used to define the OOP structure of the given type.
101     *
102     * @param int|string $tokenType
103     * @return string
104     */
105    private function getStructureKeyword( $tokenType ): string {
106        switch ( $tokenType ) {
107            case T_CLASS:
108            case T_ANON_CLASS:
109                return 'class';
110            case T_INTERFACE:
111                return 'interface';
112            case T_TRAIT:
113                return 'trait';
114            case T_ENUM:
115                return 'enum';
116            default:
117                throw new UnexpectedValueException( "Token $tokenType not handled" );
118        }
119    }
120}