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

PHPCSStandards / PHP_CodeSniffer / 9101918600

15 May 2024 07:59PM UTC coverage: 73.231% (-0.1%) from 73.363%
9101918600

push

github

jrfnl
Generic/SpaceAfterNot: improve code coverage

17793 of 24297 relevant lines covered (73.23%)

69.17 hits per line

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

92.39
/src/Standards/PSR2/Sniffs/Classes/PropertyDeclarationSniff.php
1
<?php
2
/**
3
 * Verifies that properties are declared correctly.
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\PSR2\Sniffs\Classes;
11

12
use Exception;
13
use PHP_CodeSniffer\Files\File;
14
use PHP_CodeSniffer\Sniffs\AbstractVariableSniff;
15
use PHP_CodeSniffer\Util\Tokens;
16

17
class PropertyDeclarationSniff extends AbstractVariableSniff
18
{
19

20

21
    /**
22
     * Processes the function tokens within the class.
23
     *
24
     * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
25
     * @param int                         $stackPtr  The position where the token was found.
26
     *
27
     * @return void
28
     */
29
    protected function processMemberVar(File $phpcsFile, $stackPtr)
3✔
30
    {
31
        $tokens = $phpcsFile->getTokens();
3✔
32

33
        if ($tokens[$stackPtr]['content'][1] === '_') {
3✔
34
            $error = 'Property name "%s" should not be prefixed with an underscore to indicate visibility';
3✔
35
            $data  = [$tokens[$stackPtr]['content']];
3✔
36
            $phpcsFile->addWarning($error, $stackPtr, 'Underscore', $data);
3✔
37
        }
38

39
        // Detect multiple properties defined at the same time. Throw an error
40
        // for this, but also only process the first property in the list so we don't
41
        // repeat errors.
42
        $find   = Tokens::$scopeModifiers;
3✔
43
        $find[] = T_VARIABLE;
3✔
44
        $find[] = T_VAR;
3✔
45
        $find[] = T_READONLY;
3✔
46
        $find[] = T_SEMICOLON;
3✔
47
        $find[] = T_OPEN_CURLY_BRACKET;
3✔
48

49
        $prev = $phpcsFile->findPrevious($find, ($stackPtr - 1));
3✔
50
        if ($tokens[$prev]['code'] === T_VARIABLE) {
3✔
51
            return;
3✔
52
        }
53

54
        if ($tokens[$prev]['code'] === T_VAR) {
3✔
55
            $error = 'The var keyword must not be used to declare a property';
3✔
56
            $phpcsFile->addError($error, $stackPtr, 'VarUsed');
3✔
57
        }
58

59
        $next = $phpcsFile->findNext([T_VARIABLE, T_SEMICOLON], ($stackPtr + 1));
3✔
60
        if ($next !== false && $tokens[$next]['code'] === T_VARIABLE) {
3✔
61
            $error = 'There must not be more than one property declared per statement';
3✔
62
            $phpcsFile->addError($error, $stackPtr, 'Multiple');
3✔
63
        }
64

65
        try {
66
            $propertyInfo = $phpcsFile->getMemberProperties($stackPtr);
3✔
67
            if (empty($propertyInfo) === true) {
3✔
68
                return;
3✔
69
            }
70
        } catch (Exception $e) {
×
71
            // Turns out not to be a property after all.
72
            return;
×
73
        }
74

75
        if ($propertyInfo['type'] !== '') {
3✔
76
            $typeToken = $propertyInfo['type_end_token'];
3✔
77
            $error     = 'There must be 1 space after the property type declaration; %s found';
3✔
78
            if ($tokens[($typeToken + 1)]['code'] !== T_WHITESPACE) {
3✔
79
                $data = ['0'];
3✔
80
                $fix  = $phpcsFile->addFixableError($error, $typeToken, 'SpacingAfterType', $data);
3✔
81
                if ($fix === true) {
3✔
82
                    $phpcsFile->fixer->addContent($typeToken, ' ');
3✔
83
                }
84
            } else if ($tokens[($typeToken + 1)]['content'] !== ' ') {
3✔
85
                $next = $phpcsFile->findNext(T_WHITESPACE, ($typeToken + 1), null, true);
3✔
86
                if ($tokens[$next]['line'] !== $tokens[$typeToken]['line']) {
3✔
87
                    $found = 'newline';
3✔
88
                } else {
89
                    $found = $tokens[($typeToken + 1)]['length'];
3✔
90
                }
91

92
                $data = [$found];
3✔
93

94
                $nextNonWs = $phpcsFile->findNext(Tokens::$emptyTokens, ($typeToken + 1), null, true);
3✔
95
                if ($nextNonWs !== $next) {
3✔
96
                    $phpcsFile->addError($error, $typeToken, 'SpacingAfterType', $data);
×
97
                } else {
98
                    $fix = $phpcsFile->addFixableError($error, $typeToken, 'SpacingAfterType', $data);
3✔
99
                    if ($fix === true) {
3✔
100
                        if ($found === 'newline') {
3✔
101
                            $phpcsFile->fixer->beginChangeset();
3✔
102
                            for ($x = ($typeToken + 1); $x < $next; $x++) {
3✔
103
                                $phpcsFile->fixer->replaceToken($x, '');
3✔
104
                            }
105

106
                            $phpcsFile->fixer->addContent($typeToken, ' ');
3✔
107
                            $phpcsFile->fixer->endChangeset();
3✔
108
                        } else {
109
                            $phpcsFile->fixer->replaceToken(($typeToken + 1), ' ');
3✔
110
                        }
111
                    }
112
                }
113
            }//end if
114
        }//end if
115

116
        if ($propertyInfo['scope_specified'] === false) {
3✔
117
            $error = 'Visibility must be declared on property "%s"';
3✔
118
            $data  = [$tokens[$stackPtr]['content']];
3✔
119
            $phpcsFile->addError($error, $stackPtr, 'ScopeMissing', $data);
3✔
120
        }
121

122
        /*
123
         * Note: per PSR-PER section 4.6, the order should be:
124
         * - Inheritance modifier: `abstract` or `final`.
125
         * - Visibility modifier: `public`, `protected`, or `private`.
126
         * - Scope modifier: `static`.
127
         * - Mutation modifier: `readonly`.
128
         * - Type declaration.
129
         * - Name.
130
         *
131
         * Ref: https://www.php-fig.org/per/coding-style/#46-modifier-keywords
132
         *
133
         * At this time (PHP 8.2), inheritance modifiers cannot be applied to properties and
134
         * the `static` and `readonly` modifiers are mutually exclusive and cannot be used together.
135
         *
136
         * Based on that, the below modifier keyword order checks are sufficient (for now).
137
         */
138

139
        if ($propertyInfo['scope_specified'] === true && $propertyInfo['is_static'] === true) {
3✔
140
            $scopePtr  = $phpcsFile->findPrevious(Tokens::$scopeModifiers, ($stackPtr - 1));
3✔
141
            $staticPtr = $phpcsFile->findPrevious(T_STATIC, ($stackPtr - 1));
3✔
142
            if ($scopePtr > $staticPtr) {
3✔
143
                $error = 'The static declaration must come after the visibility declaration';
3✔
144
                $fix   = $phpcsFile->addFixableError($error, $stackPtr, 'StaticBeforeVisibility');
3✔
145
                if ($fix === true) {
3✔
146
                    $phpcsFile->fixer->beginChangeset();
3✔
147

148
                    for ($i = ($scopePtr + 1); $scopePtr < $stackPtr; $i++) {
3✔
149
                        if ($tokens[$i]['code'] !== T_WHITESPACE) {
3✔
150
                            break;
3✔
151
                        }
152

153
                        $phpcsFile->fixer->replaceToken($i, '');
3✔
154
                    }
155

156
                    $phpcsFile->fixer->replaceToken($scopePtr, '');
3✔
157
                    $phpcsFile->fixer->addContentBefore($staticPtr, $propertyInfo['scope'].' ');
3✔
158

159
                    $phpcsFile->fixer->endChangeset();
3✔
160
                }
161
            }
162
        }//end if
163

164
        if ($propertyInfo['scope_specified'] === true && $propertyInfo['is_readonly'] === true) {
3✔
165
            $scopePtr    = $phpcsFile->findPrevious(Tokens::$scopeModifiers, ($stackPtr - 1));
3✔
166
            $readonlyPtr = $phpcsFile->findPrevious(T_READONLY, ($stackPtr - 1));
3✔
167
            if ($scopePtr > $readonlyPtr) {
3✔
168
                $error = 'The readonly declaration must come after the visibility declaration';
3✔
169
                $fix   = $phpcsFile->addFixableError($error, $stackPtr, 'ReadonlyBeforeVisibility');
3✔
170
                if ($fix === true) {
3✔
171
                    $phpcsFile->fixer->beginChangeset();
3✔
172

173
                    for ($i = ($scopePtr + 1); $scopePtr < $stackPtr; $i++) {
3✔
174
                        if ($tokens[$i]['code'] !== T_WHITESPACE) {
3✔
175
                            break;
3✔
176
                        }
177

178
                        $phpcsFile->fixer->replaceToken($i, '');
3✔
179
                    }
180

181
                    $phpcsFile->fixer->replaceToken($scopePtr, '');
3✔
182
                    $phpcsFile->fixer->addContentBefore($readonlyPtr, $propertyInfo['scope'].' ');
3✔
183

184
                    $phpcsFile->fixer->endChangeset();
3✔
185
                }
186
            }
187
        }//end if
188

189
    }//end processMemberVar()
1✔
190

191

192
    /**
193
     * Processes normal variables.
194
     *
195
     * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
196
     * @param int                         $stackPtr  The position where the token was found.
197
     *
198
     * @return void
199
     */
200
    protected function processVariable(File $phpcsFile, $stackPtr)
×
201
    {
202
        /*
203
            We don't care about normal variables.
204
        */
205

206
    }//end processVariable()
×
207

208

209
    /**
210
     * Processes variables in double quoted strings.
211
     *
212
     * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
213
     * @param int                         $stackPtr  The position where the token was found.
214
     *
215
     * @return void
216
     */
217
    protected function processVariableInString(File $phpcsFile, $stackPtr)
×
218
    {
219
        /*
220
            We don't care about normal variables.
221
        */
222

223
    }//end processVariableInString()
×
224

225

226
}//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

© 2026 Coveralls, Inc