Skip to content

File tree

3 files changed

+399
-0
lines changed

3 files changed

+399
-0
lines changed
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Cdn77\Sniffs\NamingConventions;
6+
7+
use PHP_CodeSniffer\Files\File;
8+
use PHP_CodeSniffer\Sniffs\Sniff;
9+
use const T_COMMA;
10+
use const T_OPEN_PARENTHESIS;
11+
use const T_OPEN_SHORT_ARRAY;
12+
use const T_WHITESPACE;
13+
14+
use PHP_CodeSniffer\Sniffs\AbstractVariableSniff;
15+
use PHP_CodeSniffer\Util\Common;
16+
use PHP_CodeSniffer\Util\Tokens;
17+
18+
class ValidVariableNameSniff extends AbstractVariableSniff
19+
{
20+
21+
22+
/**
23+
* Processes this test, when one of its tokens is encountered.
24+
*
25+
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
26+
* @param int $stackPtr The position of the current token in the
27+
* stack passed in $tokens.
28+
*
29+
* @return void
30+
*/
31+
protected function processVariable(File $phpcsFile, $stackPtr)
32+
{
33+
$tokens = $phpcsFile->getTokens();
34+
$varName = ltrim($tokens[$stackPtr]['content'], '$');
35+
36+
// If it's a php reserved var, then its ok.
37+
if (isset($this->phpReservedVars[$varName]) === true) {
38+
return;
39+
}
40+
41+
$objOperator = $phpcsFile->findNext([T_WHITESPACE], ($stackPtr + 1), null, true);
42+
if ($tokens[$objOperator]['code'] === T_OBJECT_OPERATOR
43+
|| $tokens[$objOperator]['code'] === T_NULLSAFE_OBJECT_OPERATOR
44+
) {
45+
// Check to see if we are using a variable from an object.
46+
$var = $phpcsFile->findNext([T_WHITESPACE], ($objOperator + 1), null, true);
47+
if ($tokens[$var]['code'] === T_STRING) {
48+
$bracket = $phpcsFile->findNext([T_WHITESPACE], ($var + 1), null, true);
49+
if ($tokens[$bracket]['code'] !== T_OPEN_PARENTHESIS) {
50+
$objVarName = $tokens[$var]['content'];
51+
52+
// There is no way for us to know if the var is public or
53+
// private, so we have to ignore a leading underscore if there is
54+
// one and just check the main part of the variable name.
55+
$originalVarName = $objVarName;
56+
if (substr($objVarName, 0, 1) === '_') {
57+
$objVarName = substr($objVarName, 1);
58+
}
59+
60+
if (Common::isCamelCaps($objVarName, false, true, false) === false) {
61+
$error = 'Member variable "%s" is not in valid camel caps format';
62+
$data = [$originalVarName];
63+
$phpcsFile->addError($error, $var, 'MemberNotCamelCaps', $data);
64+
}
65+
}//end if
66+
}//end if
67+
}//end if
68+
69+
$objOperator = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
70+
if ($tokens[$objOperator]['code'] === T_DOUBLE_COLON) {
71+
// The variable lives within a class, and is referenced like
72+
// this: MyClass::$_variable, so we don't know its scope.
73+
$objVarName = $varName;
74+
if (substr($objVarName, 0, 1) === '_') {
75+
$objVarName = substr($objVarName, 1);
76+
}
77+
78+
if (Common::isCamelCaps($objVarName, false, true, false) === false) {
79+
$error = 'Member variable "%s" is not in valid camel caps format';
80+
$data = [$tokens[$stackPtr]['content']];
81+
$phpcsFile->addError($error, $stackPtr, 'MemberNotCamelCaps', $data);
82+
}
83+
84+
return;
85+
}
86+
87+
// There is no way for us to know if the var is public or private,
88+
// so we have to ignore a leading underscore if there is one and just
89+
// check the main part of the variable name.
90+
$originalVarName = $varName;
91+
if (substr($varName, 0, 1) === '_') {
92+
$inClass = $phpcsFile->hasCondition($stackPtr, Tokens::$ooScopeTokens);
93+
if ($inClass === true) {
94+
$varName = substr($varName, 1);
95+
}
96+
}
97+
98+
if (Common::isCamelCaps($varName, false, true, false) === false) {
99+
$error = 'Variable "%s" is not in valid camel caps format';
100+
$data = [$originalVarName];
101+
$phpcsFile->addError($error, $stackPtr, 'NotCamelCaps', $data);
102+
}
103+
104+
}//end processVariable()
105+
106+
107+
/**
108+
* Processes class member variables.
109+
*
110+
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
111+
* @param int $stackPtr The position of the current token in the
112+
* stack passed in $tokens.
113+
*
114+
* @return void
115+
*/
116+
protected function processMemberVar(File $phpcsFile, $stackPtr)
117+
{
118+
$tokens = $phpcsFile->getTokens();
119+
120+
$varName = ltrim($tokens[$stackPtr]['content'], '$');
121+
$memberProps = $phpcsFile->getMemberProperties($stackPtr);
122+
if (empty($memberProps) === true) {
123+
// Couldn't get any info about this variable, which
124+
// generally means it is invalid or possibly has a parse
125+
// error. Any errors will be reported by the core, so
126+
// we can ignore it.
127+
return;
128+
}
129+
130+
$public = ($memberProps['scope'] !== 'private');
131+
$errorData = [$varName];
132+
133+
if ($public === true) {
134+
if (substr($varName, 0, 1) === '_') {
135+
$error = '%s member variable "%s" must not contain a leading underscore';
136+
$data = [
137+
ucfirst($memberProps['scope']),
138+
$errorData[0],
139+
];
140+
$phpcsFile->addError($error, $stackPtr, 'PublicHasUnderscore', $data);
141+
}
142+
} else {
143+
if (substr($varName, 0, 1) !== '_') {
144+
$error = 'Private member variable "%s" must contain a leading underscore';
145+
$phpcsFile->addError($error, $stackPtr, 'PrivateNoUnderscore', $errorData);
146+
}
147+
}
148+
149+
// Remove a potential underscore prefix for testing CamelCaps.
150+
$varName = ltrim($varName, '_');
151+
152+
if (Common::isCamelCaps($varName, false, true, false) === false) {
153+
$error = 'Member variable "%s" is not in valid camel caps format';
154+
$phpcsFile->addError($error, $stackPtr, 'MemberNotCamelCaps', $errorData);
155+
}
156+
157+
}//end processMemberVar()
158+
159+
160+
/**
161+
* Processes the variable found within a double quoted string.
162+
*
163+
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
164+
* @param int $stackPtr The position of the double quoted
165+
* string.
166+
*
167+
* @return void
168+
*/
169+
protected function processVariableInString(File $phpcsFile, $stackPtr)
170+
{
171+
$tokens = $phpcsFile->getTokens();
172+
173+
if (preg_match_all('|[^\\\]\${?([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)|', $tokens[$stackPtr]['content'], $matches) !== 0) {
174+
foreach ($matches[1] as $varName) {
175+
// If it's a php reserved var, then its ok.
176+
if (isset($this->phpReservedVars[$varName]) === true) {
177+
continue;
178+
}
179+
180+
if (Common::isCamelCaps($varName, false, true, false) === false) {
181+
$error = 'Variable "%s" is not in valid camel caps format';
182+
$data = [$varName];
183+
$phpcsFile->addError($error, $stackPtr, 'StringNotCamelCaps', $data);
184+
}
185+
}
186+
}
187+
188+
}//end processVariableInString()
189+
190+
191+
}//end class
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Cdn77\Sniffs\NamingConventions;
6+
7+
use Cdn77\TestCase;
8+
9+
class ValidVariableNameSniffTest extends TestCase
10+
{
11+
public const CODE_NOT_CAMEL_CAPS = 'NotCamelCaps';
12+
public const CODE_MEMBER_NOT_CAMEL_CAPS = 'MemberNotCamelCaps';
13+
public const CODE_PUBLIC_HAS_UNDERSCORE = 'PublicHasUnderscore';
14+
public const CODE_PRIVATE_NO_UNDERSCORE = 'PrivateNoUnderscore';
15+
public const CODE_STRING_NOT_CAMEL_CAPS = 'StringNotCamelCaps';
16+
17+
public function testErrors(): void
18+
{
19+
$file = self::checkFile(__DIR__ . '/data/ValidVariableNameSniffTest.inc');
20+
21+
self::assertSame(36, $file->getErrorCount());
22+
23+
self::assertSniffError($file, 3, self::CODE_NOT_CAMEL_CAPS);
24+
self::assertSniffError($file, 5, self::CODE_NOT_CAMEL_CAPS);
25+
self::assertSniffError($file, 10, self::CODE_MEMBER_NOT_CAMEL_CAPS);
26+
self::assertSniffError($file, 12, self::CODE_PUBLIC_HAS_UNDERSCORE);
27+
self::assertSniffError($file, 15, self::CODE_MEMBER_NOT_CAMEL_CAPS);
28+
self::assertSniffError($file, 17, self::CODE_PUBLIC_HAS_UNDERSCORE);
29+
self::assertSniffError($file, 20, self::CODE_MEMBER_NOT_CAMEL_CAPS);
30+
self::assertSniffError($file, 22, self::CODE_PUBLIC_HAS_UNDERSCORE);
31+
self::assertSniffError($file, 25, self::CODE_MEMBER_NOT_CAMEL_CAPS);
32+
self::assertSniffError($file, 27, self::CODE_PRIVATE_NO_UNDERSCORE);
33+
self::assertSniffError($file, 31, self::CODE_NOT_CAMEL_CAPS);
34+
self::assertSniffError($file, 33, self::CODE_NOT_CAMEL_CAPS);
35+
self::assertSniffError($file, 36, self::CODE_STRING_NOT_CAMEL_CAPS);
36+
self::assertSniffError($file, 37, self::CODE_STRING_NOT_CAMEL_CAPS);
37+
self::assertSniffError($file, 39, self::CODE_STRING_NOT_CAMEL_CAPS);
38+
self::assertSniffError($file, 42, self::CODE_NOT_CAMEL_CAPS);
39+
self::assertSniffError($file, 44, self::CODE_NOT_CAMEL_CAPS);
40+
self::assertSniffError($file, 53, self::CODE_MEMBER_NOT_CAMEL_CAPS);
41+
self::assertSniffError($file, 58, self::CODE_MEMBER_NOT_CAMEL_CAPS);
42+
self::assertSniffError($file, 62, self::CODE_MEMBER_NOT_CAMEL_CAPS);
43+
self::assertSniffError($file, 63, self::CODE_NOT_CAMEL_CAPS);
44+
self::assertSniffError($file, 64, self::CODE_NOT_CAMEL_CAPS);
45+
self::assertSniffError($file, 67, self::CODE_NOT_CAMEL_CAPS);
46+
self::assertSniffError($file, 81, self::CODE_STRING_NOT_CAMEL_CAPS);
47+
self::assertSniffError($file, 106, self::CODE_PUBLIC_HAS_UNDERSCORE);
48+
self::assertSniffError($file, 107, self::CODE_PUBLIC_HAS_UNDERSCORE);
49+
self::assertSniffError($file, 107, self::CODE_MEMBER_NOT_CAMEL_CAPS);
50+
self::assertSniffError($file, 108, self::CODE_PUBLIC_HAS_UNDERSCORE);
51+
self::assertSniffError($file, 111, self::CODE_PRIVATE_NO_UNDERSCORE);
52+
self::assertSniffError($file, 112, self::CODE_PRIVATE_NO_UNDERSCORE);
53+
self::assertSniffError($file, 113, self::CODE_PRIVATE_NO_UNDERSCORE);
54+
self::assertSniffError($file, 114, self::CODE_PRIVATE_NO_UNDERSCORE);
55+
self::assertSniffError($file, 123, self::CODE_PUBLIC_HAS_UNDERSCORE);
56+
self::assertSniffError($file, 138, self::CODE_NOT_CAMEL_CAPS);
57+
self::assertSniffError($file, 141, self::CODE_NOT_CAMEL_CAPS);
58+
self::assertSniffError($file, 146, self::CODE_MEMBER_NOT_CAMEL_CAPS);
59+
}
60+
}

0 commit comments

Comments
 (0)