Completed
Push — in-portal ( b7a6d3...d7c658 )
by Alexander
02:38
created

ValidVariableNameSniff::isSnakeCaps()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
ccs 0
cts 4
cp 0
crap 2
1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 36 and the first side effect is on line 18.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
/**
3
 * CodingStandard_Sniffs_NamingConventions_ValidVariableNameSniff.
4
 *
5
 * PHP version 5
6
 *
7
 * @category PHP
8
 * @package  PHP_CodeSniffer
9
 * @author   Greg Sherwood <[email protected]>
10
 * @author   Marc McIntyre <[email protected]>
11
 * @author   Alexander Obuhovich <[email protected]>
12
 * @license  https://github.com/aik099/CodingStandard/blob/master/LICENSE BSD 3-Clause
13
 * @link     https://github.com/aik099/CodingStandard
14
 */
15
16
// @codeCoverageIgnoreStart
17
if (class_exists('PHP_CodeSniffer_Standards_AbstractVariableSniff', true) === false) {
18
    $error = 'Class PHP_CodeSniffer_Standards_AbstractVariableSniff not found';
19
    throw new PHP_CodeSniffer_Exception($error);
20
}
21
// @codeCoverageIgnoreEnd
22
23
/**
24
 * CodingStandard_Sniffs_NamingConventions_ValidVariableNameSniff.
25
 *
26
 * Checks the naming of variables and member variables.
27
 *
28
 * @category PHP
29
 * @package  PHP_CodeSniffer
30
 * @author   Greg Sherwood <[email protected]>
31
 * @author   Marc McIntyre <[email protected]>
32
 * @author   Alexander Obuhovich <[email protected]>
33
 * @license  https://github.com/aik099/CodingStandard/blob/master/LICENSE BSD 3-Clause
34
 * @link     https://github.com/aik099/CodingStandard
35
 */
36
class CodingStandard_Sniffs_NamingConventions_ValidVariableNameSniff extends
0 ignored issues
show
Coding Style introduced by
This class is not in CamelCase format.

Classes in PHP are usually named in CamelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. The whole name starts with a capital letter as well.

Thus the name database provider becomes DatabaseProvider.

Loading history...
37
 PHP_CodeSniffer_Standards_AbstractVariableSniff
38
{
39
40
    /**
41
     * Variable names, that are reserved in PHP.
42
     *
43
     * @var array
44
     */
45
    protected $phpReservedVars = array(
46
                                  '_SERVER',
47
                                  '_GET',
48
                                  '_POST',
49
                                  '_REQUEST',
50
                                  '_SESSION',
51
                                  '_ENV',
52
                                  '_COOKIE',
53
                                  '_FILES',
54
                                  'GLOBALS',
55
                                  'http_response_header',
56
                                  'HTTP_RAW_POST_DATA',
57
                                  'php_errormsg',
58
                                 );
59
60
    /**
61
     * Member variable names that break the rules, but are allowed.
62
     *
63
     * @var array
64
     */
65
    protected $memberExceptions = array(
66
                                   // From "kBase".
67
                                   'Application',
68
                                   'Conn',
69
70
                                   // From "kEvent".
71
                                   'Name',
72
                                   'MasterEvent',
73
                                   'Prefix',
74
                                   'Special',
75
76
                                   // From "kDBItem".
77
                                   'IDField',
78
                                   'TableName',
79
                                   'IgnoreValidation',
80
                                  );
81
82
83
    /**
84
     * Processes this test, when one of its tokens is encountered.
85
     *
86
     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
87
     * @param int                  $stackPtr  The position of the current token in the
88
     *                                        stack passed in $tokens.
89
     *
90
     * @return void
91
     */
92
    protected function processVariable(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
93
    {
94
        $tokens  = $phpcsFile->getTokens();
95
        $varName = ltrim($tokens[$stackPtr]['content'], '$');
96
97
        // If it's a php reserved var, then its ok.
98
        if (in_array($varName, $this->phpReservedVars) === true) {
99
            return;
100
        }
101
102
        $objOperator = $phpcsFile->findPrevious(array(T_WHITESPACE), ($stackPtr - 1), null, true);
103
        if ($tokens[$objOperator]['code'] === T_DOUBLE_COLON
104
            || $tokens[$objOperator]['code'] === T_OBJECT_OPERATOR
105
        ) {
106
            // Don't validate class/object property usage,
107
            // because their declaration is already validated.
108
            return;
109
        }
110
111
        if ($this->isSnakeCaps($varName) === false) {
112
            $error = 'Variable "%s" is not in valid snake caps format';
113
            $data  = array($varName);
114
            $phpcsFile->addError($error, $stackPtr, 'NotSnakeCaps', $data);
115
        }
116
117
    }//end processVariable()
118
119
120
    /**
121
     * Processes class member variables.
122
     *
123
     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
124
     * @param int                  $stackPtr  The position of the current token in the
125
     *                                        stack passed in $tokens.
126
     *
127
     * @return void
128
     */
129
    protected function processMemberVar(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
130
    {
131
        $tokens = $phpcsFile->getTokens();
132
133
        $varName     = ltrim($tokens[$stackPtr]['content'], '$');
134
        $memberProps = $phpcsFile->getMemberProperties($stackPtr);
135
136
        // @codeCoverageIgnoreStart
137
        if (empty($memberProps) === true) {
138
            // Couldn't get any info about this variable, which
139
            // generally means it is invalid or possibly has a parse
140
            // error. Any errors will be reported by the core, so
141
            // we can ignore it.
142
            return;
143
        }
144
        // @codeCoverageIgnoreEnd
145
146
        $classToken = $phpcsFile->findPrevious(
147
            array(T_CLASS, T_INTERFACE, T_TRAIT),
148
            $stackPtr
149
        );
150
        $className = $phpcsFile->getDeclarationName($classToken);
0 ignored issues
show
Bug introduced by
It seems like $classToken defined by $phpcsFile->findPrevious...E, T_TRAIT), $stackPtr) on line 146 can also be of type boolean; however, PHP_CodeSniffer_File::getDeclarationName() does only seem to accept integer, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
151
152
        $public    = ($memberProps['scope'] !== 'private');
153
        $errorData = array($className.'::'.$varName);
154
155
        if ($public === true) {
156
            if (substr($varName, 0, 1) === '_') {
157
                $error = '%s member variable "%s" must not contain a leading underscore';
158
                $data  = array(
159
                          ucfirst($memberProps['scope']),
160
                          $errorData[0],
161
                         );
162
                $phpcsFile->addError($error, $stackPtr, 'PublicHasUnderscore', $data);
163
                return;
164
            }
165
        } else {
166
            if (substr($varName, 0, 1) !== '_') {
167
                $error = 'Private member variable "%s" must contain a leading underscore';
168
                $phpcsFile->addError($error, $stackPtr, 'PrivateNoUnderscore', $errorData);
169
                return;
170
            }
171
        }
172
173
        if ($this->isCamelCaps($varName, $public) === false) {
174
            $error = '%s member variable "%s" is not in valid camel caps format';
175
            $data  = array(
176
                      ucfirst($memberProps['scope']),
177
                      $errorData[0],
178
                     );
179
            $phpcsFile->addError($error, $stackPtr, 'MemberNotCamelCaps', $data);
180
        }
181
182
    }//end processMemberVar()
183
184
185
    /**
186
     * Processes the variable found within a double quoted string.
187
     *
188
     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
189
     * @param int                  $stackPtr  The position of the double quoted
190
     *                                        string.
191
     *
192
     * @return void
193
     */
194
    protected function processVariableInString(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
195
    {
196
        $tokens         = $phpcsFile->getTokens();
197
        $content        = $tokens[$stackPtr]['content'];
198
        $variablesFound = preg_match_all(
199
            '|[^\\\]\${?([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)|',
200
            $content,
201
            $matches,
202
            PREG_SET_ORDER + PREG_OFFSET_CAPTURE
203
        );
204
205
        if ($variablesFound === 0) {
206
            return;
207
        }
208
209
        foreach ($matches as $match) {
0 ignored issues
show
Bug introduced by
The expression $matches of type null|array<integer,array<integer,string>> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
210
            $varName = $match[1][0];
211
            $offset = $match[1][1];
212
213
            // If it's a php reserved var, then its ok.
214
            if (in_array($varName, $this->phpReservedVars) === true) {
215
                continue;
216
            }
217
218
            // Don't validate class/object property usage in strings,
219
            // because their declaration is already validated.
220
            $variablePrefix = substr($content, $offset - 3, 2);
221
            if ($variablePrefix === '::' || $variablePrefix === '->') {
222
                continue;
223
            }
224
225
            if ($this->isSnakeCaps($varName) === false) {
226
                $error = 'Variable in string "%s" is not in valid snake caps format';
227
                $data  = array($varName);
228
                $phpcsFile->addError($error, $stackPtr, 'StringNotSnakeCaps', $data);
229
            }
230
        }//end foreach
231
232
    }//end processVariableInString()
233
234
235
    /**
236
     * Determines if a variable is in camel caps case.
237
     *
238
     * @param string $string String.
239
     * @param bool   $public If true, the first character in the string
240
     *                       must be an a-z character. If false, the
241
     *                       character must be an underscore. This
242
     *                       argument is only applicable if $classFormat
243
     *                       is false.
244
     *
245
     * @return bool
246
     */
247
    protected function isCamelCaps($string, $public=true)
0 ignored issues
show
Coding Style introduced by
Incorrect spacing between argument "$public" and equals sign; expected 1 but found 0
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$public"; expected 1 but found 0
Loading history...
248
    {
249
        if (in_array($string, $this->memberExceptions) === true) {
250
            return true;
251
        }
252
253
        return PHP_CodeSniffer::isCamelCaps($string, false, $public, false);
254
255
    }//end isCamelCaps()
256
257
258
    /**
259
     * Determines if a variable is in snake caps case.
260
     *
261
     * @param string $string String.
262
     *
263
     * @return bool
264
     */
265
    protected function isSnakeCaps($string)
266
    {
267
        return strtolower($string) === $string;
268
269
    }//end isSnakeCaps()
270
271
272
}//end class
273