1
|
|
|
<?php |
|
|
|
|
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 |
|
|
|
|
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); |
|
|
|
|
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) { |
|
|
|
|
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) |
|
|
|
|
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
|
|
|
|
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.