Skip to content

Commit 6db6799

Browse files
committed
Added VariablesNameSniff
1 parent 4df873f commit 6db6799

File tree

8 files changed

+462
-1
lines changed

8 files changed

+462
-1
lines changed
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
<?php // phpcs:disable
2+
3+
namespace Inpsyde\Sniffs\CodeQuality;
4+
5+
use Inpsyde\PhpcsHelpers;
6+
use PHP_CodeSniffer\Files\File;
7+
use PHP_CodeSniffer\Sniffs\Sniff;
8+
9+
class VariablesNameSniff implements Sniff
10+
{
11+
const GLOBALS = [
12+
'$_GET',
13+
'$_POST',
14+
'$_REQUEST',
15+
'$_SERVER',
16+
'$_COOKIE',
17+
'$_FILES',
18+
'$_SESSION',
19+
'$_ENV',
20+
'$GLOBALS',
21+
];
22+
23+
const WP_GLOBALS = [
24+
'$is_iphone',
25+
'$is_chrome',
26+
'$is_safari',
27+
'$is_NS4',
28+
'$is_opera',
29+
'$is_macIE',
30+
'$is_winIE',
31+
'$is_gecko',
32+
'$is_lynx',
33+
'$is_IE',
34+
'$is_edge',
35+
'$is_apache',
36+
'$is_IIS',
37+
'$is_iis7',
38+
'$tinymce_version',
39+
'$manifest_version',
40+
'$required_php_version',
41+
'$required_mysql_version',
42+
'$super_admins',
43+
];
44+
45+
/**
46+
* @var bool
47+
*/
48+
public $checkType = 'camelCase';
49+
50+
/**
51+
* @var string[]
52+
*/
53+
public $ignoredNames = [];
54+
55+
/**
56+
* @var bool
57+
*/
58+
public $ignoreLocalVars = false;
59+
60+
/**
61+
* @var bool
62+
*/
63+
public $ignoreProperties = false;
64+
65+
/**
66+
* @inheritdoc
67+
*/
68+
public function register()
69+
{
70+
return [
71+
T_VARIABLE
72+
];
73+
}
74+
75+
/**
76+
* @inheritdoc
77+
*/
78+
public function process(File $phpcsFile, $stackPtr)
79+
{
80+
$ignored = $this->allIgnored();
81+
$name = $phpcsFile->getTokens()[$stackPtr]['content'];
82+
83+
if (in_array($name, $ignored, true) || strpos($name, '$wp_') === 0) {
84+
return;
85+
}
86+
87+
$isCamelCase = $this->checkType() === 'camelCase';
88+
89+
$valid = $isCamelCase ? $this->checkCamelCase($name) : $this->checkSnakeCase($name);
90+
if ($valid) {
91+
return;
92+
}
93+
94+
$isProperty = PhpcsHelpers::variableIsProperty($phpcsFile, $stackPtr);
95+
96+
if (($isProperty && $this->arePropertiesIgnored())
97+
|| (!$isProperty && $this->areVariablesIgnored())
98+
) {
99+
return;
100+
}
101+
102+
$phpcsFile->addWarning(
103+
sprintf(
104+
'"%s" should be used for variable names.',
105+
$isCamelCase ? '$camelCase' : '$snake_case'
106+
),
107+
$stackPtr,
108+
$isCamelCase ? 'SnakeCaseVar' : 'CamelCaseVar'
109+
);
110+
}
111+
112+
/**
113+
* @return string
114+
*/
115+
private function checkType(): string
116+
{
117+
if (!is_string($this->checkType)) {
118+
return 'camelCase';
119+
}
120+
121+
$type = strtolower(trim($this->checkType));
122+
if (in_array($type, ['camelcase', 'snake_case'], true)) {
123+
return $type === 'camelcase' ? 'camelCase' : 'snake_case';
124+
}
125+
126+
return 'camelCase';
127+
}
128+
129+
/**
130+
* @return bool
131+
*/
132+
private function arePropertiesIgnored(): bool
133+
{
134+
return (bool) filter_var($this->ignoreProperties, FILTER_VALIDATE_BOOLEAN);
135+
}
136+
137+
/**
138+
* @return bool
139+
*/
140+
private function areVariablesIgnored(): bool
141+
{
142+
return (bool) filter_var($this->ignoreLocalVars, FILTER_VALIDATE_BOOLEAN);
143+
}
144+
145+
/**
146+
* @param string $name
147+
* @return bool
148+
*/
149+
private function checkCamelCase(string $name): bool
150+
{
151+
return preg_match('~^\$[a-z]+(?:[a-zA-Z0-9]+)?$~', $name)
152+
&& ! preg_match('~[A-Z]{2,}~', $name);
153+
}
154+
155+
/**
156+
* @param string $name
157+
* @return bool
158+
*/
159+
private function checkSnakeCase(string $name): bool
160+
{
161+
return (bool)preg_match('~^\$[a-z]+(?:[a-z0-9_]+)?$~', $name);
162+
}
163+
164+
165+
/**
166+
* @return array
167+
*/
168+
private function allIgnored(): array
169+
{
170+
if (is_string($this->ignoredNames)) {
171+
$this->ignoredNames = explode(',', $this->ignoredNames);
172+
}
173+
174+
if (!is_array($this->ignoredNames)) {
175+
$this->ignoredNames = [];
176+
}
177+
178+
$normalized = [];
179+
foreach ($this->ignoredNames as $name) {
180+
if (is_string($name)) {
181+
$normalized[] = '$' . ltrim(trim($name), '$');
182+
}
183+
}
184+
185+
$this->ignoredNames = $normalized;
186+
187+
return array_merge($normalized, self::GLOBALS, self::WP_GLOBALS);
188+
}
189+
}

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ Some custom rules are also in use. They are:
155155
| `PropertyPerClassLimitSniff`|Discourage usage of more than 10 properties per class.||||
156156
| `Psr4Sniff`|Check PSR-4 compliance||||
157157
| `ReturnTypeDeclarationSniff`|Enforce return type declaration, with few exceptions (e.g. hook callbacks or `ArrayAccess` methods)||||
158+
| `VariablesNameSniff`|Check variable (and properties) names||||
158159

159160
For **notes and configuration** see `/docs/rules-list/inpsyde-rules-configuration.md` file in this repo.
160161

docs/inpsyde-rules-configuration.md

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,4 +248,48 @@ Also note that the warning **is** shown in case:
248248
- the `@return` docbloc declares more than one not-null types, e.g. `@return Foo|Bar|null`
249249
- the `@return` docbloc types contains "mixed", e.g. `@return mixed|null`.
250250

251-
-----
251+
-----
252+
253+
254+
## Inpsyde.CodeQuality.VariablesName
255+
256+
This sniff can be configured to enforce either `$camelCase` (default) or `$snake_case` variable names.
257+
258+
To change the check type, use `checkType` property, and set it to either: `"camelCase"` or `"snake_case"`
259+
(any other value will be ignored and default will be applied).
260+
261+
```xml
262+
<rule ref="Inpsyde.CodeQuality.VariablesName">
263+
<properties>
264+
<property name="checkType" value="snake_case" />
265+
</properties>
266+
</rule>
267+
```
268+
269+
By default, the sniff applies check to both local variables and class properties.
270+
271+
It is possible to ignore either local variables or class properties respectively via `ignoreLocalVars`
272+
and `ignoreProperties` properties.
273+
274+
E.g.:
275+
276+
```xml
277+
<rule ref="Inpsyde.CodeQuality.VariablesName">
278+
<properties>
279+
<property name="ignoreLocalVars" value="true" />
280+
</properties>
281+
</rule>
282+
```
283+
284+
No matter the check type used (`"camelCase"` or `"snake_case"`), PHP super globals variables and
285+
WordPress global variables are always ignored.
286+
287+
It is possible to also ignore some other names via the `ignoredNames` property:
288+
289+
```xml
290+
<rule ref="Inpsyde.CodeQuality.VariablesName">
291+
<properties>
292+
<property name="ignoredNames" type="array" value="ALLOWED,allowed_snake" />
293+
</properties>
294+
</rule>
295+
```

docs/rules-list/custom.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,6 @@
3333
- Inpsyde.CodeQuality.ReturnTypeDeclaration.IncorrectVoidReturn
3434
- Inpsyde.CodeQuality.ReturnTypeDeclaration.NoReturnType
3535
- Inpsyde.CodeQuality.ReturnTypeDeclaration.IncorrectVoidReturnType
36+
- Inpsyde.CodeQuality.VariablesName
37+
- Inpsyde.CodeQuality.VariablesName.CamelCaseVar
38+
- Inpsyde.CodeQuality.VariablesName.SnakeCaseVar
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?php
2+
// @phpcsSniff CodeQuality.VariablesName
3+
4+
// @phpcsSniffPropertiesStart
5+
// $checkType = "camelCase";
6+
// @phpcsSniffPropertiesEnd
7+
8+
namespace VariablesNameTestCamelCase;
9+
10+
// @phpcsWarningOnNextLine
11+
$foo_bar = 'foo_bar';
12+
13+
$foo = 'foo';
14+
15+
$fooBar = 'fooBar';
16+
17+
// @phpcsWarningOnNextLine
18+
$foo_Bar = 'foo_Bar';
19+
20+
// @phpcsWarningOnNextLine
21+
$FooBar = 'foo_Bar';
22+
23+
global $is_edge;
24+
$is_edge = false;
25+
26+
class Foo {
27+
28+
private static $foo = 'foo';
29+
30+
// @phpcsWarningOnNextLine
31+
public static $FooBar = 'FooBar';
32+
33+
// @phpcsWarningOnNextLine
34+
public $foo_bar = 'foo_bar';
35+
36+
protected $fooBar = 'fooBar';
37+
38+
// @phpcsWarningOnNextLine
39+
var $foo_Bar = 'foo_Bar';
40+
}
41+
42+
trait Bar {
43+
44+
private static $foo = 'foo';
45+
46+
// @phpcsWarningOnNextLine
47+
public static $FooBar = 'FooBar';
48+
49+
// @phpcsWarningOnNextLine
50+
public $foo_bar = 'foo_bar';
51+
52+
protected $fooBar = 'fooBar';
53+
54+
// @phpcsWarningOnNextLine
55+
var $foo_Bar = 'foo_Bar';
56+
}

tests/fixtures/var-names-no-local.php

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
// @phpcsSniff CodeQuality.VariablesName
3+
4+
// @phpcsSniffPropertiesStart
5+
// $checkType = 'snake_case';
6+
// $ignoredNames = ['IAMALLOWED', 'anId'];
7+
// $ignoreLocalVars = "true";
8+
// @phpcsSniffPropertiesEnd
9+
10+
namespace VariablesNameTestCamelCase;
11+
12+
$foo_bar = 'foo_bar';
13+
$foo = 'foo';
14+
$fooBar = 'fooBar';
15+
$foo_Bar = 'foo_Bar';
16+
$FooBar = 'foo_Bar';
17+
18+
global $is_NS4;
19+
$is_NS4 = false;
20+
$_GET = [];
21+
$IAMALLOWED = true;
22+
23+
class Foo {
24+
25+
private static $foo = 'foo';
26+
27+
// @phpcsWarningOnNextLine
28+
public static $FooBar = 'FooBar';
29+
30+
public $foo_bar = 'foo_bar';
31+
32+
// @phpcsWarningOnNextLine
33+
protected $fooBar = 'fooBar';
34+
35+
private $anId = 1;
36+
37+
// @phpcsWarningOnNextLine
38+
var $foo_Bar = 'foo_Bar';
39+
}
40+
41+
trait Bar {
42+
43+
private static $foo = 'foo';
44+
45+
// @phpcsWarningOnNextLine
46+
public static $FooBar = 'FooBar';
47+
48+
public $foo_bar = 'foo_bar';
49+
50+
// @phpcsWarningOnNextLine
51+
protected $fooBar = 'fooBar';
52+
53+
// @phpcsWarningOnNextLine
54+
var $foo_Bar = 'foo_Bar';
55+
}

0 commit comments

Comments
 (0)