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

PHPCSStandards / PHPCSExtra / 4521944295

pending completion
4521944295

Pull #217

github

GitHub
Merge 19bc00367 into 5226c513f
Pull Request #217: Universal/DisallowInlineTabs: add extra tests

5 of 5 new or added lines in 1 file covered. (100.0%)

1 existing line in 1 file now uncovered.

1907 of 1914 relevant lines covered (99.63%)

3.97 hits per line

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

97.62
/Universal/Sniffs/WhiteSpace/DisallowInlineTabsSniff.php
1
<?php
2
/**
3
 * PHPCSExtra, a collection of sniffs and standards for use with PHP_CodeSniffer.
4
 *
5
 * @package   PHPCSExtra
6
 * @copyright 2020 PHPCSExtra Contributors
7
 * @license   https://opensource.org/licenses/LGPL-3.0 LGPL3
8
 * @link      https://github.com/PHPCSStandards/PHPCSExtra
9
 */
10

11
namespace PHPCSExtra\Universal\Sniffs\WhiteSpace;
12

13
use PHP_CodeSniffer\Files\File;
14
use PHP_CodeSniffer\Sniffs\Sniff;
15
use PHP_CodeSniffer\Util\Tokens;
16
use PHPCSExtra\Universal\Helpers\DummyTokenizer;
17
use PHPCSUtils\BackCompat\Helper;
18
use PHPCSUtils\Tokens\Collections;
19

20
/**
21
 * Enforces using spaces for mid-line alignment.
22
 *
23
 * While tab versus space based indentation is a question of preference, for mid-line
24
 * alignment, spaces should always be preferred, as using tabs will result in inconsistent
25
 * formatting depending on the dev-user's chosen tab width.
26
 *
27
 * This sniff is especially useful for tab-indentation based standards which use the
28
 * `Generic.Whitespace.DisallowSpaceIndent` sniff to enforce this.
29
 *
30
 * **DO** make sure to set the PHPCS native `tab-width` configuration for the best results.
31
 * <code>
32
 *   <arg name="tab-width" value="4"/>
33
 * </code>
34
 *
35
 * The PHPCS native `Generic.Whitespace.DisallowTabIndent` sniff oversteps its reach and silently
36
 * does mid-line tab to space replacements as well.
37
 * However, the sister-sniff `Generic.Whitespace.DisallowSpaceIndent` leaves mid-line tabs/spaces alone.
38
 * This sniff fills that gap.
39
 *
40
 * @since 1.0.0
41
 */
42
final class DisallowInlineTabsSniff implements Sniff
43
{
44

45
    /**
46
     * The --tab-width CLI value that is being used.
47
     *
48
     * @since 1.0.0
49
     *
50
     * @var int
51
     */
52
    private $tabWidth;
53

54
    /**
55
     * Tokens to check for mid-line tabs.
56
     *
57
     * @since 1.0.0
58
     *
59
     * @var array
60
     */
61
    private $find = [
62
        \T_WHITESPACE             => true,
63
        \T_DOC_COMMENT_WHITESPACE => true,
64
        \T_DOC_COMMENT_STRING     => true,
65
        \T_COMMENT                => true,
66
    ];
67

68
    /**
69
     * Registers the tokens that this sniff wants to listen for.
70
     *
71
     * @since 1.0.0
72
     *
73
     * @return int[]
74
     */
75
    public function register()
76
    {
77
        return Collections::phpOpenTags();
4✔
78
    }
79

80
    /**
81
     * Processes this test, when one of its tokens is encountered.
82
     *
83
     * @since 1.0.0
84
     *
85
     * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
86
     * @param int                         $stackPtr  The position of the current token
87
     *                                               in the stack passed in $tokens.
88
     *
89
     * @return int Integer stack pointer to skip the rest of the file.
90
     */
91
    public function process(File $phpcsFile, $stackPtr)
92
    {
93
        if (isset($this->tabWidth) === false) {
4✔
94
            $this->tabWidth = Helper::getTabWidth($phpcsFile);
4✔
95
        }
2✔
96

97
        if (defined('PHP_CODESNIFFER_IN_TESTS')) {
4✔
98
            $this->tabWidth = Helper::getCommandLineData($phpcsFile, 'tabWidth');
4✔
99
        }
2✔
100

101
        $tokens = $phpcsFile->getTokens();
4✔
102
        $dummy  = new DummyTokenizer('', $phpcsFile->config);
4✔
103

104
        for ($i = 0; $i < $phpcsFile->numTokens; $i++) {
4✔
105
            // Skip all non-whitespace tokens and skip whitespace at the start of a new line.
106
            if (isset($this->find[$tokens[$i]['code']]) === false
4✔
107
                || (($tokens[$i]['code'] === \T_WHITESPACE
4✔
108
                    || $tokens[$i]['code'] === \T_DOC_COMMENT_WHITESPACE)
4✔
109
                    && $tokens[$i]['column'] === 1)
4✔
110
            ) {
2✔
111
                continue;
4✔
112
            }
113

114
            // If tabs haven't been converted to spaces by the tokenizer, do so now.
115
            $token = $tokens[$i];
4✔
116
            if (isset($token['orig_content']) === false) {
4✔
117
                if ($token['content'] === '' || \strpos($token['content'], "\t") === false) {
4✔
118
                    // If there are no tabs, we can continue, no matter what.
119
                    continue;
4✔
120
                }
121

122
                $dummy->replaceTabsInToken($token);
4✔
123
            }
2✔
124

125
            $origContent = $token['orig_content'];
4✔
126

127
            if ($origContent === '' || \strpos($origContent, "\t") === false) {
4✔
128
                // If there are no tabs, we can continue, no matter what.
UNCOV
129
                continue;
×
130
            }
131

132
            $multiLineComment = false;
4✔
133
            if (($tokens[$i]['code'] === \T_COMMENT
4✔
134
                || isset(Tokens::$phpcsCommentTokens[$tokens[$i]['code']]))
4✔
135
                 && $tokens[$i]['column'] === 1
4✔
136
                 && ($tokens[($i - 1)]['code'] === \T_COMMENT
4✔
137
                 || isset(Tokens::$phpcsCommentTokens[$tokens[($i - 1)]['code']]))
4✔
138
            ) {
2✔
139
                $multiLineComment = true;
4✔
140
            }
2✔
141

142
            if ($multiLineComment === true) {
4✔
143
                // This is the subsequent line of a multi-line comment. Account for indentation.
144
                $commentOnly = \ltrim($origContent);
4✔
145
                if ($commentOnly === '' || \strpos($commentOnly, "\t") === false) {
4✔
146
                    continue;
4✔
147
                }
148
            }
2✔
149

150
            $fix = $phpcsFile->addFixableError(
4✔
151
                'Spaces must be used for mid-line alignment; tabs are not allowed',
4✔
152
                $i,
2✔
153
                'NonIndentTabsUsed'
2✔
154
            );
2✔
155

156
            if ($fix === false) {
4✔
157
                continue;
4✔
158
            }
159

160
            $indent = '';
4✔
161
            if ($multiLineComment === true) {
4✔
162
                // Take the original indent (tabs/spaces) and combine with the tab-replaced comment content.
163
                $indent           = \str_replace($commentOnly, '', $origContent);
4✔
164
                $token['content'] = \ltrim($token['content']);
4✔
165
            }
2✔
166

167
            $phpcsFile->fixer->replaceToken($i, $indent . $token['content']);
4✔
168
        }
2✔
169

170
        // Scanned the whole file in one go. Don't scan this file again.
171
        return $phpcsFile->numTokens;
4✔
172
    }
173
}
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