• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

PHPCSStandards / PHP_CodeSniffer / 15253296250

26 May 2025 11:55AM UTC coverage: 78.632% (+0.3%) from 78.375%
15253296250

Pull #1105

github

web-flow
Merge d9441d98f into caf806050
Pull Request #1105: Skip tests when 'git' command is not available

19665 of 25009 relevant lines covered (78.63%)

88.67 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.94
/src/Standards/Generic/Sniffs/WhiteSpace/DisallowTabIndentSniff.php
1
<?php
2
/**
3
 * Throws errors if tabs are used for indentation.
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/PHPCSStandards/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
8
 */
9

10
namespace PHP_CodeSniffer\Standards\Generic\Sniffs\WhiteSpace;
11

12
use PHP_CodeSniffer\Files\File;
13
use PHP_CodeSniffer\Sniffs\Sniff;
14

15
class DisallowTabIndentSniff implements Sniff
16
{
17

18
    /**
19
     * The --tab-width CLI value that is being used.
20
     *
21
     * @var integer
22
     */
23
    private $tabWidth = null;
24

25

26
    /**
27
     * Returns an array of tokens this test wants to listen for.
28
     *
29
     * @return array<int|string>
30
     */
31
    public function register()
3✔
32
    {
33
        return [
2✔
34
            T_OPEN_TAG,
3✔
35
            T_OPEN_TAG_WITH_ECHO,
3✔
36
        ];
2✔
37

38
    }//end register()
39

40

41
    /**
42
     * Processes this test, when one of its tokens is encountered.
43
     *
44
     * @param \PHP_CodeSniffer\Files\File $phpcsFile All the tokens found in the document.
45
     * @param int                         $stackPtr  The position of the current token in
46
     *                                               the stack passed in $tokens.
47
     *
48
     * @return int
49
     */
50
    public function process(File $phpcsFile, $stackPtr)
3✔
51
    {
52
        if ($this->tabWidth === null) {
3✔
53
            if (isset($phpcsFile->config->tabWidth) === false || $phpcsFile->config->tabWidth === 0) {
3✔
54
                // We have no idea how wide tabs are, so assume 4 spaces for metrics.
55
                $this->tabWidth = 4;
×
56
            } else {
57
                $this->tabWidth = $phpcsFile->config->tabWidth;
3✔
58
            }
59
        }
60

61
        $tokens      = $phpcsFile->getTokens();
3✔
62
        $checkTokens = [
2✔
63
            T_WHITESPACE             => true,
3✔
64
            T_INLINE_HTML            => true,
3✔
65
            T_DOC_COMMENT_WHITESPACE => true,
3✔
66
            T_DOC_COMMENT_STRING     => true,
3✔
67
            T_COMMENT                => true,
3✔
68
            T_END_HEREDOC            => true,
3✔
69
            T_END_NOWDOC             => true,
3✔
70
            T_YIELD_FROM             => true,
3✔
71
        ];
2✔
72

73
        for ($i = 0; $i < $phpcsFile->numTokens; $i++) {
3✔
74
            if (isset($checkTokens[$tokens[$i]['code']]) === false) {
3✔
75
                continue;
3✔
76
            }
77

78
            // If tabs are being converted to spaces by the tokeniser, the
79
            // original content should be checked instead of the converted content.
80
            if (isset($tokens[$i]['orig_content']) === true) {
3✔
81
                $content = $tokens[$i]['orig_content'];
3✔
82
            } else {
83
                $content = $tokens[$i]['content'];
3✔
84
            }
85

86
            if ($content === '') {
3✔
87
                continue;
×
88
            }
89

90
            // If this is an inline HTML token or a subsequent line of a multi-line comment,
91
            // split off the indentation as that is the only part to take into account for the metrics.
92
            $indentation = $content;
3✔
93
            if (($tokens[$i]['code'] === T_INLINE_HTML
3✔
94
                || $tokens[$i]['code'] === T_COMMENT)
3✔
95
                && preg_match('`^(\s*)\S.*`s', $content, $matches) > 0
3✔
96
            ) {
97
                if (isset($matches[1]) === true) {
3✔
98
                    $indentation = $matches[1];
3✔
99
                }
100
            }
101

102
            if (($tokens[$i]['code'] === T_DOC_COMMENT_WHITESPACE
3✔
103
                || $tokens[$i]['code'] === T_COMMENT)
3✔
104
                && $indentation === ' '
3✔
105
            ) {
106
                // Ignore all non-indented comments, especially for recording metrics.
107
                continue;
3✔
108
            }
109

110
            $recordMetrics = true;
3✔
111
            if ($content === $indentation
3✔
112
                && isset($tokens[($i + 1)]) === true
3✔
113
                && $tokens[$i]['line'] < $tokens[($i + 1)]['line']
3✔
114
            ) {
115
                // Don't record metrics for empty lines.
116
                $recordMetrics = false;
3✔
117
            }
118

119
            $foundTabs = substr_count($content, "\t");
3✔
120

121
            $error     = 'Spaces must be used to indent lines; tabs are not allowed';
3✔
122
            $errorCode = 'TabsUsed';
3✔
123
            if ($tokens[$i]['column'] === 1) {
3✔
124
                if ($recordMetrics === true) {
3✔
125
                    $foundIndentSpaces = substr_count($indentation, ' ');
3✔
126
                    $foundIndentTabs   = substr_count($indentation, "\t");
3✔
127

128
                    if ($foundIndentTabs > 0 && $foundIndentSpaces === 0) {
3✔
129
                        $phpcsFile->recordMetric($i, 'Line indent', 'tabs');
3✔
130
                    } else if ($foundIndentTabs === 0 && $foundIndentSpaces > 0) {
3✔
131
                        $phpcsFile->recordMetric($i, 'Line indent', 'spaces');
3✔
132
                    } else if ($foundIndentTabs > 0 && $foundIndentSpaces > 0) {
3✔
133
                        $spacePosition  = strpos($indentation, ' ');
3✔
134
                        $tabAfterSpaces = strpos($indentation, "\t", $spacePosition);
3✔
135
                        if ($tabAfterSpaces !== false) {
3✔
136
                            $phpcsFile->recordMetric($i, 'Line indent', 'mixed');
3✔
137
                        } else {
138
                            // Check for use of precision spaces.
139
                            $numTabs = (int) floor($foundIndentSpaces / $this->tabWidth);
3✔
140
                            if ($numTabs === 0) {
3✔
141
                                $phpcsFile->recordMetric($i, 'Line indent', 'tabs');
3✔
142
                            } else {
143
                                $phpcsFile->recordMetric($i, 'Line indent', 'mixed');
3✔
144
                            }
145
                        }
146
                    }
147
                }//end if
148
            } else {
149
                // Look for tabs so we can report and replace, but don't
150
                // record any metrics about them because they aren't
151
                // line indent tokens.
152
                if ($foundTabs > 0) {
3✔
153
                    $error     = 'Spaces must be used for alignment; tabs are not allowed';
3✔
154
                    $errorCode = 'NonIndentTabsUsed';
3✔
155
                }
156
            }//end if
157

158
            if ($foundTabs === 0) {
3✔
159
                continue;
3✔
160
            }
161

162
            // Report, but don't auto-fix tab identation for a PHP 7.3+ flexible heredoc/nowdoc closer.
163
            // Auto-fixing this would cause parse errors as the indentation of the heredoc/nowdoc contents
164
            // needs to use the same type of indentation. Also see: https://3v4l.org/7OF3M .
165
            if ($tokens[$i]['code'] === T_END_HEREDOC || $tokens[$i]['code'] === T_END_NOWDOC) {
3✔
166
                $phpcsFile->addError($error, $i, $errorCode.'HeredocCloser');
2✔
167
                continue;
2✔
168
            }
169

170
            $fix = $phpcsFile->addFixableError($error, $i, $errorCode);
3✔
171
            if ($fix === true) {
3✔
172
                if (isset($tokens[$i]['orig_content']) === true) {
3✔
173
                    // Use the replacement that PHPCS has already done.
174
                    $phpcsFile->fixer->replaceToken($i, $tokens[$i]['content']);
3✔
175
                } else {
176
                    // Replace tabs with spaces, using an indent of tabWidth spaces.
177
                    // Other sniffs can then correct the indent if they need to.
178
                    $newContent = str_replace("\t", str_repeat(' ', $this->tabWidth), $tokens[$i]['content']);
×
179
                    $phpcsFile->fixer->replaceToken($i, $newContent);
×
180
                }
181
            }
182
        }//end for
183

184
        // Ignore the rest of the file.
185
        return $phpcsFile->numTokens;
3✔
186

187
    }//end process()
188

189

190
}//end class
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc