├── CHANGELOG.md
├── LICENSE
├── Modernize
├── Docs
│ └── FunctionCalls
│ │ └── DirnameStandard.xml
├── Sniffs
│ └── FunctionCalls
│ │ └── DirnameSniff.php
└── ruleset.xml
├── NormalizedArrays
├── Docs
│ └── Arrays
│ │ ├── ArrayBraceSpacingStandard.xml
│ │ └── CommaAfterLastStandard.xml
├── Sniffs
│ └── Arrays
│ │ ├── ArrayBraceSpacingSniff.php
│ │ └── CommaAfterLastSniff.php
└── ruleset.xml
├── README.md
├── Universal
├── Docs
│ ├── Arrays
│ │ ├── DisallowShortArraySyntaxStandard.xml
│ │ ├── DuplicateArrayKeyStandard.xml
│ │ ├── MixedArrayKeyTypesStandard.xml
│ │ └── MixedKeyedUnkeyedArrayStandard.xml
│ ├── Classes
│ │ ├── DisallowAnonClassParenthesesStandard.xml
│ │ ├── DisallowFinalClassStandard.xml
│ │ ├── ModifierKeywordOrderStandard.xml
│ │ ├── RequireAnonClassParenthesesStandard.xml
│ │ └── RequireFinalClassStandard.xml
│ ├── CodeAnalysis
│ │ ├── ConstructorDestructorReturnStandard.xml
│ │ ├── ForeachUniqueAssignmentStandard.xml
│ │ ├── NoDoubleNegativeStandard.xml
│ │ ├── NoEchoSprintfStandard.xml
│ │ └── StaticInFinalClassStandard.xml
│ ├── Constants
│ │ ├── LowercaseClassResolutionKeywordStandard.xml
│ │ ├── ModifierKeywordOrderStandard.xml
│ │ └── UppercaseMagicConstantsStandard.xml
│ ├── ControlStructures
│ │ ├── DisallowAlternativeSyntaxStandard.xml
│ │ ├── DisallowLonelyIfStandard.xml
│ │ └── IfElseDeclarationStandard.xml
│ ├── Files
│ │ └── SeparateFunctionsFromOOStandard.xml
│ ├── FunctionDeclarations
│ │ ├── NoLongClosuresStandard.xml
│ │ └── RequireFinalMethodsInTraitsStandard.xml
│ ├── Lists
│ │ ├── DisallowLongListSyntaxStandard.xml
│ │ └── DisallowShortListSyntaxStandard.xml
│ ├── Namespaces
│ │ ├── DisallowCurlyBraceSyntaxStandard.xml
│ │ ├── DisallowDeclarationWithoutNameStandard.xml
│ │ ├── EnforceCurlyBraceSyntaxStandard.xml
│ │ └── OneDeclarationPerFileStandard.xml
│ ├── NamingConventions
│ │ └── NoReservedKeywordParameterNamesStandard.xml
│ ├── OOStructures
│ │ └── AlphabeticExtendsImplementsStandard.xml
│ ├── Operators
│ │ ├── ConcatPositionStandard.xml
│ │ ├── DisallowLogicalAndOrStandard.xml
│ │ ├── DisallowShortTernaryStandard.xml
│ │ ├── DisallowStandalonePostIncrementDecrementStandard.xml
│ │ ├── StrictComparisonsStandard.xml
│ │ └── TypeSeparatorSpacingStandard.xml
│ ├── PHP
│ │ ├── LowercasePHPTagStandard.xml
│ │ ├── NoFQNTrueFalseNullStandard.xml
│ │ └── OneStatementInShortEchoTagStandard.xml
│ ├── UseStatements
│ │ ├── DisallowMixedGroupUseStandard.xml
│ │ ├── DisallowUseClassStandard.xml
│ │ ├── DisallowUseConstStandard.xml
│ │ ├── DisallowUseFunctionStandard.xml
│ │ ├── KeywordSpacingStandard.xml
│ │ ├── LowercaseFunctionConstStandard.xml
│ │ ├── NoLeadingBackslashStandard.xml
│ │ └── NoUselessAliasesStandard.xml
│ └── WhiteSpace
│ │ ├── AnonClassKeywordSpacingStandard.xml
│ │ ├── CommaSpacingStandard.xml
│ │ ├── DisallowInlineTabsStandard.xml
│ │ └── PrecisionAlignmentStandard.xml
├── Helpers
│ └── DummyTokenizer.php
├── Sniffs
│ ├── Arrays
│ │ ├── DisallowShortArraySyntaxSniff.php
│ │ ├── DuplicateArrayKeySniff.php
│ │ ├── MixedArrayKeyTypesSniff.php
│ │ └── MixedKeyedUnkeyedArraySniff.php
│ ├── Classes
│ │ ├── DisallowAnonClassParenthesesSniff.php
│ │ ├── DisallowFinalClassSniff.php
│ │ ├── ModifierKeywordOrderSniff.php
│ │ ├── RequireAnonClassParenthesesSniff.php
│ │ └── RequireFinalClassSniff.php
│ ├── CodeAnalysis
│ │ ├── ConstructorDestructorReturnSniff.php
│ │ ├── ForeachUniqueAssignmentSniff.php
│ │ ├── NoDoubleNegativeSniff.php
│ │ ├── NoEchoSprintfSniff.php
│ │ └── StaticInFinalClassSniff.php
│ ├── Constants
│ │ ├── LowercaseClassResolutionKeywordSniff.php
│ │ ├── ModifierKeywordOrderSniff.php
│ │ └── UppercaseMagicConstantsSniff.php
│ ├── ControlStructures
│ │ ├── DisallowAlternativeSyntaxSniff.php
│ │ ├── DisallowLonelyIfSniff.php
│ │ └── IfElseDeclarationSniff.php
│ ├── Files
│ │ └── SeparateFunctionsFromOOSniff.php
│ ├── FunctionDeclarations
│ │ ├── NoLongClosuresSniff.php
│ │ └── RequireFinalMethodsInTraitsSniff.php
│ ├── Lists
│ │ ├── DisallowLongListSyntaxSniff.php
│ │ └── DisallowShortListSyntaxSniff.php
│ ├── Namespaces
│ │ ├── DisallowCurlyBraceSyntaxSniff.php
│ │ ├── DisallowDeclarationWithoutNameSniff.php
│ │ ├── EnforceCurlyBraceSyntaxSniff.php
│ │ └── OneDeclarationPerFileSniff.php
│ ├── NamingConventions
│ │ └── NoReservedKeywordParameterNamesSniff.php
│ ├── OOStructures
│ │ └── AlphabeticExtendsImplementsSniff.php
│ ├── Operators
│ │ ├── ConcatPositionSniff.php
│ │ ├── DisallowLogicalAndOrSniff.php
│ │ ├── DisallowShortTernarySniff.php
│ │ ├── DisallowStandalonePostIncrementDecrementSniff.php
│ │ ├── StrictComparisonsSniff.php
│ │ └── TypeSeparatorSpacingSniff.php
│ ├── PHP
│ │ ├── LowercasePHPTagSniff.php
│ │ ├── NoFQNTrueFalseNullSniff.php
│ │ └── OneStatementInShortEchoTagSniff.php
│ ├── UseStatements
│ │ ├── DisallowMixedGroupUseSniff.php
│ │ ├── DisallowUseClassSniff.php
│ │ ├── DisallowUseConstSniff.php
│ │ ├── DisallowUseFunctionSniff.php
│ │ ├── KeywordSpacingSniff.php
│ │ ├── LowercaseFunctionConstSniff.php
│ │ ├── NoLeadingBackslashSniff.php
│ │ └── NoUselessAliasesSniff.php
│ └── WhiteSpace
│ │ ├── AnonClassKeywordSpacingSniff.php
│ │ ├── CommaSpacingSniff.php
│ │ ├── DisallowInlineTabsSniff.php
│ │ └── PrecisionAlignmentSniff.php
└── ruleset.xml
└── composer.json
/Modernize/Docs/FunctionCalls/DirnameStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 | = 5.3: Usage of dirname(__FILE__) can be replaced with __DIR__.
9 | ]]>
10 |
11 |
12 |
13 | __DIR__;
15 | ]]>
16 |
17 |
18 | dirname(__FILE__);
20 | ]]>
21 |
22 |
23 |
24 | = 7.0: Nested calls to dirname() can be replaced by using dirname() with the $levels parameter.
26 | ]]>
27 |
28 |
29 |
30 | dirname($file, 3);
32 | ]]>
33 |
34 |
35 | dirname(dirname(dirname($file)));
37 | ]]>
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/Modernize/ruleset.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | A collection of sniffs to detect code modernization opportunities.
5 |
6 |
--------------------------------------------------------------------------------
/NormalizedArrays/Docs/Arrays/ArrayBraceSpacingStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
12 |
13 |
16 |
17 |
18 | (1, 2);
20 | ]]>
21 |
22 |
23 |
24 |
27 |
28 |
29 |
30 |
35 |
36 |
37 | );
39 |
40 | $args = [ ];
41 | ]]>
42 |
43 |
44 |
45 |
48 |
49 |
50 |
51 |
56 |
57 |
58 | 1, 2 );
60 |
61 | $args = [ 1, 2 ];
62 | ]]>
63 |
64 |
65 |
66 |
69 |
70 |
71 |
72 |
74 | 1,
75 | 2
76 | );
77 |
78 | $args = [
79 | 1,
80 | 2
81 | ];
82 | ]]>
83 |
84 |
85 |
92 |
93 |
94 |
95 |
--------------------------------------------------------------------------------
/NormalizedArrays/Docs/Arrays/CommaAfterLastStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 | no comma after the last array item.
9 |
10 | However, for multi-line arrays, there should be a comma after the last array item.
11 | ]]>
12 |
13 |
14 |
15 |
18 |
19 |
20 | , );
22 | ]]>
23 |
24 |
25 |
26 |
27 | 'foo',
30 | 2 => 'bar',
31 | ];
32 | ]]>
33 |
34 |
35 | 'foo',
38 | 2 => 'bar'
39 | ];
40 | ]]>
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/NormalizedArrays/ruleset.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | A ruleset for PHP_CodeSniffer to check arrays for normalized format.
5 |
6 |
--------------------------------------------------------------------------------
/Universal/Docs/Arrays/DisallowShortArraySyntaxStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
12 |
13 | array(
15 | 'foo' => 'bar',
16 | );
17 | ]]>
18 |
19 |
20 | [
22 | 'foo' => 'bar',
23 | ];
24 | ]]>
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/Universal/Docs/Arrays/DuplicateArrayKeyStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
13 |
14 | 'foo' => 22,
17 | 'bar' => 25,
18 | 'baz' => 28,
19 | );
20 |
21 | $args = array(
22 | 22,
23 | 25,
24 | 2 => 28,
25 | );
26 | ]]>
27 |
28 |
29 | 'foo' => 22,
32 | 'bar' => 25,
33 | 'bar' => 28,
34 | );
35 |
36 | $args = array(
37 | 22,
38 | 25,
39 | 1 => 28,
40 | );
41 | ]]>
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/Universal/Docs/Arrays/MixedArrayKeyTypesStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
12 |
13 | 'foo' => 22,
16 | 'bar' => 25,
17 | );
18 |
19 | $args = array(
20 | 0 => 22,
21 | 1 => 25,
22 | );
23 | ]]>
24 |
25 |
26 | 22,
29 | 25,
30 | );
31 |
32 | $args = array(
33 | 'foo' => 22,
34 | 12 => 25,
35 | );
36 |
37 | ]]>
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/Universal/Docs/Arrays/MixedKeyedUnkeyedArrayStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
12 |
13 | 'foo' => 22,
16 | 'bar' => 25,
17 | );
18 |
19 | $args = array(22, 25);
20 | ]]>
21 |
22 |
23 | 22,
26 | 25,
27 | );
28 | ]]>
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/Universal/Docs/Classes/DisallowAnonClassParenthesesStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
12 |
13 | {};
15 | $anon = new class($param) {};
16 | ]]>
17 |
18 |
19 | () {};
21 | ]]>
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Universal/Docs/Classes/DisallowFinalClassStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
12 |
13 | class Foo {}
15 | abstract class Bar implements MyInterface {}
16 | ]]>
17 |
18 |
19 | final class Foo {}
21 | final class Bar extends MyAbstract {}
22 | ]]>
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/Universal/Docs/Classes/ModifierKeywordOrderStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
14 |
15 | final readonly class Foo {}
17 | abstract readonly class Bar {}
18 | ]]>
19 |
20 |
21 | readonly final class Foo {}
23 | readonly abstract class Bar {}
24 | ]]>
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/Universal/Docs/Classes/RequireAnonClassParenthesesStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
12 |
13 | () {};
15 | ]]>
16 |
17 |
18 | {};
20 | ]]>
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/Universal/Docs/Classes/RequireFinalClassStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
12 |
13 | final class Foo {}
15 | final class Bar extends MyAbstract {}
16 | ]]>
17 |
18 |
19 | class Foo {}
21 | abstract class Bar implements MyInterface {}
22 | ]]>
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/Universal/Docs/CodeAnalysis/ConstructorDestructorReturnStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
12 |
13 |
18 |
19 |
20 | : int {}
23 | }
24 | ]]>
25 |
26 |
27 |
28 |
29 |
32 |
33 |
34 |
35 |
47 |
48 |
49 | $this;
54 | }
55 |
56 | public function __destruct() {
57 | // Do something.
58 | return false;
59 | }
60 | }
61 | ]]>
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/Universal/Docs/CodeAnalysis/ForeachUniqueAssignmentStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
13 |
14 |
15 |
16 | $v ) {}
18 | ]]>
19 |
20 |
21 | $k ) {}
23 | ]]>
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Universal/Docs/CodeAnalysis/NoDoubleNegativeStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
12 |
13 | ! $b;
15 |
16 | if((bool) callMe($a)) {}
17 | ]]>
18 |
19 |
20 | ! ! $b;
22 |
23 | if(! ! ! callMe($a)) {}
24 | ]]>
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/Universal/Docs/CodeAnalysis/NoEchoSprintfStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
12 |
13 | printf('text %s text', $var);
15 | echo callMe('text %s text', $var);
16 | ]]>
17 |
18 |
19 | echo sprintf('text %s text', $var);
21 | echo vsprintf('text %s text', [$var]);
22 | ]]>
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/Universal/Docs/CodeAnalysis/StaticInFinalClassStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
15 |
16 |
17 |
18 | self
22 | {
23 | $var = self::functionCall();
24 | $var = $obj instanceof self;
25 | $var = new self;
26 | }
27 | }
28 | ]]>
29 |
30 |
31 | static|false {
35 | $var = static::$prop;
36 | $var = $obj instanceof static;
37 | $var = new static();
38 | }
39 | };
40 | ]]>
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/Universal/Docs/Constants/LowercaseClassResolutionKeywordStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
12 |
13 |
16 |
17 |
18 | MyClass::CLASS;
20 | ]]>
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/Universal/Docs/Constants/ModifierKeywordOrderStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
14 |
15 | final public const FOO = 'foo';
18 | }
19 | ]]>
20 |
21 |
22 | protected final const BAR = 'foo';
26 | }
27 | ]]>
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/Universal/Docs/Constants/UppercaseMagicConstantsStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
12 |
13 | __LINE__;
15 | include __DIR__ . '/file.php';
16 | ]]>
17 |
18 |
19 | __NameSpace__;
21 | include dirname(__file__) . '/file.php';
22 | ]]>
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/Universal/Docs/ControlStructures/DisallowAlternativeSyntaxStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
12 |
13 | {
15 | $var = 1;
16 | }
17 |
18 | while (++$i < 10) {
19 | echo $i;
20 | }
21 | ]]>
22 |
23 |
24 | :
26 | $var = 1;
27 | endif;
28 |
29 | while (++$i < 10):
30 | echo $i;
31 | endwhile;
32 | ]]>
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/Universal/Docs/ControlStructures/DisallowLonelyIfStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
14 |
15 | elseif ($bar) {
19 | // ...
20 | }
21 |
22 | if ($foo) {
23 | // ...
24 | } else {
25 | if ($bar) {
26 | // ...
27 | }
28 |
29 | doSomethingElse();
30 |
31 | }
32 |
33 | ]]>
34 |
35 |
36 | if ($bar) {
41 | // ...
42 | } else {
43 | // ...
44 | }
45 | }
46 | ]]>
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/Universal/Docs/ControlStructures/IfElseDeclarationStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
12 |
13 |
17 | elseif ($bar) {
18 | $var = 2;
19 | }
20 | else {
21 | $var = 3;
22 | }
23 | ]]>
24 |
25 |
26 | elseif ($bar) {
30 | $var = 2;
31 | } else {
32 | $var = 3;
33 | }
34 | ]]>
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/Universal/Docs/Files/SeparateFunctionsFromOOStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
13 |
14 |
15 |
16 |
29 |
30 |
31 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/Universal/Docs/FunctionDeclarations/NoLongClosuresStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
14 |
15 |
16 |
17 |
24 |
25 |
26 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/Universal/Docs/FunctionDeclarations/RequireFinalMethodsInTraitsStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
12 |
13 | final public function bar() {}
16 | final public static function baz() {}
17 |
18 | // Also valid (out of scope):
19 | protected abstract function overload() {}
20 | private function okay() {}
21 | }
22 | ]]>
23 |
24 |
25 | public function bar() {}
28 | protected static function baz() {}
29 | }
30 | ]]>
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/Universal/Docs/Lists/DisallowLongListSyntaxStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
12 |
13 | [$a, $b] = $array;
15 | ]]>
16 |
17 |
18 | list($a, $b) = $array;
20 | ]]>
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/Universal/Docs/Lists/DisallowShortListSyntaxStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
12 |
13 | list($a, $b) = $array;
15 | ]]>
16 |
17 |
18 | [$a, $b] = $array;
20 | ]]>
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/Universal/Docs/Namespaces/DisallowCurlyBraceSyntaxStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
12 |
13 | ;
15 |
16 | // Code
17 | ]]>
18 |
19 |
20 | {
22 | // Code.
23 | }
24 | ]]>
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/Universal/Docs/Namespaces/DisallowDeclarationWithoutNameStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
12 |
13 | Vendor\Name {
15 | }
16 | ]]>
17 |
18 |
19 | {
21 | }
22 | ]]>
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/Universal/Docs/Namespaces/EnforceCurlyBraceSyntaxStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
12 |
13 | {
15 | // Code.
16 | }
17 | ]]>
18 |
19 |
20 | ;
22 |
23 | // Code
24 | ]]>
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/Universal/Docs/Namespaces/OneDeclarationPerFileStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
12 |
13 |
16 |
17 |
18 | namespace Vendor\Project\Sub\B {
23 | }
24 | ]]>
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/Universal/Docs/NamingConventions/NoReservedKeywordParameterNamesStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
12 |
13 |
16 |
17 |
18 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/Universal/Docs/OOStructures/AlphabeticExtendsImplementsStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
12 |
13 | Bar, Foo
15 | {
16 | }
17 | ]]>
18 |
19 |
20 | Foo, Bar
22 | {
23 | }
24 | ]]>
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/Universal/Docs/Operators/ConcatPositionStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
14 |
15 |
16 |
17 | . $b . 'text'
20 | . $c;
21 | ]]>
22 |
23 |
24 | .
26 | $b . 'text'
27 | . $c;
28 | ]]>
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/Universal/Docs/Operators/DisallowLogicalAndOrStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
13 |
14 |
15 |
16 | && $var > 10) {}
18 |
19 | if (empty($var) || $var < 0) {}
20 | ]]>
21 |
22 |
23 | and $var > 10) {}
25 |
26 | if (empty($var) or $var < 0) {}
27 | ]]>
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/Universal/Docs/Operators/DisallowShortTernaryStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
14 |
15 | ? $a : 'default';
17 | ]]>
18 |
19 |
20 | ?: 'default';
22 | echo $a ? : 'default';
23 | ]]>
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Universal/Docs/Operators/DisallowStandalonePostIncrementDecrementStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
14 |
15 | ++$i;
17 | --$j;
18 | ]]>
19 |
20 |
21 | ++;
23 | $j--;
24 | ]]>
25 |
26 |
27 |
28 |
31 |
32 |
33 |
34 | ++$i;
36 | ]]>
37 |
38 |
39 | --$i++++;
41 | ]]>
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/Universal/Docs/Operators/StrictComparisonsStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
14 |
15 | === 'text') {}
17 |
18 | if ($var !== true) {}
19 | ]]>
20 |
21 |
22 | == 'text') {}
24 |
25 | if ($var != true) {}
26 | ]]>
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/Universal/Docs/Operators/TypeSeparatorSpacingStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
16 |
17 |
18 |
19 | |string $paramA,
22 | TypeA&TypeB $paramB,
23 | (TypeA&TypeB)|null $paramC
24 | ): int|false {}
25 | ]]>
26 |
27 |
28 | | string $paramA,
31 | TypeA & TypeB $paramB,
32 | ( TypeA&TypeB ) |null $paramC
33 | ): int
34 | |
35 | false {}
36 | ]]>
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/Universal/Docs/PHP/LowercasePHPTagStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
12 |
13 |
17 |
18 |
19 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/Universal/Docs/PHP/NoFQNTrueFalseNullStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
12 |
13 | null;
15 |
16 | if ($something === false) {}
17 | ]]>
18 |
19 |
20 | \null;
22 |
23 | if ($something === \false) {}
24 | ]]>
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/Universal/Docs/PHP/OneStatementInShortEchoTagStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
13 |
14 | = $text ?>
16 | ]]>
17 |
18 |
19 | = $text; echo $moreText; ?>
21 | ]]>
22 |
23 |
24 |
25 |
26 |
28 | echo $text;
29 | echo $moreText;
30 | ?>
31 | ]]>
32 |
33 |
34 | = $text;
36 | echo $moreText;
37 | ?>
38 | ]]>
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/Universal/Docs/UseStatements/DisallowMixedGroupUseStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
12 |
13 |
26 |
27 |
28 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/Universal/Docs/UseStatements/DisallowUseClassStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
12 |
13 |
17 |
18 |
19 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/Universal/Docs/UseStatements/DisallowUseConstStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
12 |
13 |
17 |
18 |
19 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/Universal/Docs/UseStatements/DisallowUseFunctionStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
12 |
13 |
17 |
18 |
19 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/Universal/Docs/UseStatements/KeywordSpacingStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
12 |
13 | function strpos;
15 | use const PHP_EOL as MY_EOL;
16 | ]]>
17 |
18 |
19 | function strpos;
21 | use
22 | const
23 | PHP_EOL
24 | as
25 | MY_EOL;
26 | ]]>
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/Universal/Docs/UseStatements/LowercaseFunctionConstStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
12 |
13 | function strpos;
15 | use const PHP_EOL;
16 | ]]>
17 |
18 |
19 | Function strpos;
21 | use CONST PHP_EOL;
22 | ]]>
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/Universal/Docs/UseStatements/NoLeadingBackslashStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
12 |
13 | use Package\ClassName;
15 | ]]>
16 |
17 |
18 | \Package\ClassName;
20 | ]]>
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/Universal/Docs/UseStatements/NoUselessAliasesStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
13 |
14 |
15 |
16 |
21 |
22 |
23 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/Universal/Docs/WhiteSpace/AnonClassKeywordSpacingStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
14 |
15 | class($param)
17 | {
18 | public function __construct($p) {}
19 | };
20 | ]]>
21 |
22 |
23 | class ($param)
25 | {
26 | public function __construct($p) {}
27 | };
28 | ]]>
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/Universal/Docs/WhiteSpace/CommaSpacingStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
19 |
20 |
21 |
22 | , $param2, $param3);
24 |
25 | function_call(
26 | $param1,
27 | $param2,
28 | $param3
29 | );
30 |
31 | $array = array($item1, $item2, $item3);
32 | $array = [
33 | $item1,
34 | $item2,
35 | ];
36 |
37 | list(, $a, $b,,) = $array;
38 | list(
39 | ,
40 | $a,
41 | $b,
42 | ) = $array;
43 | ]]>
44 |
45 |
46 | , $param2,$param3);
48 |
49 | function_call(
50 | $a
51 | ,$b
52 | ,$c
53 | );
54 |
55 | $array = array($item1,$item2 , $item3);
56 | $array = [
57 | $item1,
58 | $item2 ,
59 | ];
60 |
61 | list( ,$a, $b ,,) = $array;
62 | list(
63 | ,
64 | $a,
65 | $b ,
66 | ) = $array;
67 | ]]>
68 |
69 |
70 |
71 |
74 |
75 |
76 |
77 | , // Comment.
80 | $param2, /* Comment. */
81 | );
82 | ]]>
83 |
84 |
85 | ,
89 | $param2 /* Comment. */,
90 | );
91 | ]]>
92 |
93 |
94 |
95 |
--------------------------------------------------------------------------------
/Universal/Docs/WhiteSpace/DisallowInlineTabsStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
12 |
13 | [space]= 'text';
15 | $text[space][space]= 'more text';
16 | ]]>
17 |
18 |
19 | [tab]= 'text';
21 | $text[tab]= 'more text';
22 | ]]>
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/Universal/Docs/WhiteSpace/PrecisionAlignmentStandard.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
12 |
13 | [space][space][space][space]$foo = 'bar';
16 |
17 | [tab]$foo = 'bar';
18 | ]]>
19 |
20 |
21 | [space][space]$foo = 'bar';
24 |
25 | [tab][space]$foo = 'bar';
26 | ]]>
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/Universal/Helpers/DummyTokenizer.php:
--------------------------------------------------------------------------------
1 | eolChar = $eolChar;
37 | $this->config = $config;
38 | }
39 |
40 | /**
41 | * Creates an array of tokens when given some content.
42 | *
43 | * @param string $string The string to tokenize.
44 | *
45 | * @return array>
46 | */
47 | protected function tokenize($string)
48 | {
49 | return [];
50 | }
51 |
52 | /**
53 | * Performs additional processing after main tokenizing.
54 | *
55 | * @return void
56 | */
57 | protected function processAdditional()
58 | {
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/Universal/Sniffs/Arrays/DisallowShortArraySyntaxSniff.php:
--------------------------------------------------------------------------------
1 |
46 | */
47 | public function register()
48 | {
49 | return Collections::arrayOpenTokensBC();
50 | }
51 |
52 | /**
53 | * Processes this test, when one of its tokens is encountered.
54 | *
55 | * @since 1.0.0
56 | *
57 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
58 | * @param int $stackPtr The position of the current token
59 | * in the stack passed in $tokens.
60 | *
61 | * @return void
62 | */
63 | public function process(File $phpcsFile, $stackPtr)
64 | {
65 | $tokens = $phpcsFile->getTokens();
66 |
67 | if ($tokens[$stackPtr]['code'] === \T_ARRAY) {
68 | $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'no');
69 | return;
70 | }
71 |
72 | if (Arrays::isShortArray($phpcsFile, $stackPtr) === false) {
73 | // Square brackets, but not a short array. Probably short list or real square brackets.
74 | return;
75 | }
76 |
77 | $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'yes');
78 |
79 | $error = 'Short array syntax is not allowed';
80 | $fix = $phpcsFile->addFixableError($error, $stackPtr, 'Found');
81 |
82 | if ($fix === true) {
83 | $phpcsFile->fixer->beginChangeset();
84 | $phpcsFile->fixer->replaceToken($tokens[$stackPtr]['bracket_opener'], 'array(');
85 | $phpcsFile->fixer->replaceToken($tokens[$stackPtr]['bracket_closer'], ')');
86 | $phpcsFile->fixer->endChangeset();
87 | }
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/Universal/Sniffs/Arrays/MixedArrayKeyTypesSniff.php:
--------------------------------------------------------------------------------
1 | seenStringKey = false;
53 | $this->seenNumericKey = false;
54 |
55 | parent::processArray($phpcsFile);
56 | }
57 |
58 | /**
59 | * Process the tokens in an array key.
60 | *
61 | * @since 1.0.0
62 | *
63 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where the
64 | * token was found.
65 | * @param int $startPtr The stack pointer to the first token in the "key" part of
66 | * an array item.
67 | * @param int $endPtr The stack pointer to the last token in the "key" part of
68 | * an array item.
69 | * @param int $itemNr Which item in the array is being handled.
70 | *
71 | * @return true|void Returning `TRUE` will short-circuit the array item loop and stop processing.
72 | * In effect, this means that the sniff will not examine the double arrow, the array
73 | * value or comma for this array item and will not process any array items after this one.
74 | */
75 | public function processKey(File $phpcsFile, $startPtr, $endPtr, $itemNr)
76 | {
77 | $key = $this->getActualArrayKey($phpcsFile, $startPtr, $endPtr);
78 | if (isset($key) === false) {
79 | // Key could not be determined.
80 | return;
81 | }
82 |
83 | $integerKey = \is_int($key);
84 |
85 | // Handle integer key.
86 | if ($integerKey === true) {
87 | if ($this->seenStringKey === false) {
88 | if ($this->seenNumericKey !== false) {
89 | // Already seen a numeric key before.
90 | return;
91 | }
92 |
93 | $this->seenNumericKey = true;
94 | return;
95 | }
96 |
97 | // Ok, so we've seen a string key before and now see an explicit numeric key.
98 | $firstNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, $startPtr, null, true);
99 | $phpcsFile->addError(
100 | 'Arrays should have either numeric keys or string keys. Explicit numeric key detected,'
101 | . ' while all previous keys in this array were string keys.',
102 | $firstNonEmpty,
103 | 'ExplicitNumericKey'
104 | );
105 |
106 | // Stop the loop.
107 | return true;
108 | }
109 |
110 | // Handle string key.
111 | if ($this->seenNumericKey === false) {
112 | if ($this->seenStringKey !== false) {
113 | // Already seen a string key before.
114 | return;
115 | }
116 |
117 | $this->seenStringKey = true;
118 | return;
119 | }
120 |
121 | // Ok, so we've seen a numeric key before and now see a string key.
122 | $firstNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, $startPtr, null, true);
123 | $phpcsFile->addError(
124 | 'Arrays should have either numeric keys or string keys. String key detected,'
125 | . ' while all previous keys in this array were integer based keys.',
126 | $firstNonEmpty,
127 | 'StringKey'
128 | );
129 |
130 | // Stop the loop.
131 | return true;
132 | }
133 |
134 | /**
135 | * Process an array item without an array key.
136 | *
137 | * @since 1.0.0
138 | *
139 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where the
140 | * token was found.
141 | * @param int $startPtr The stack pointer to the first token in the array item,
142 | * which in this case will be the first token of the array
143 | * value part of the array item.
144 | * @param int $itemNr Which item in the array is being handled.
145 | *
146 | * @return true|void Returning `TRUE` will short-circuit the array item loop and stop processing.
147 | * In effect, this means that the sniff will not examine the array value or
148 | * comma for this array item and will not process any array items after this one.
149 | */
150 | public function processNoKey(File $phpcsFile, $startPtr, $itemNr)
151 | {
152 | if ($this->seenStringKey === false) {
153 | if ($this->seenNumericKey !== false) {
154 | // Already seen a numeric key before.
155 | return;
156 | }
157 |
158 | $this->seenNumericKey = true;
159 | return;
160 | }
161 |
162 | // Ok, so we've seen a string key before and now see an implicit numeric key.
163 | $firstNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, $startPtr, null, true);
164 | $phpcsFile->addError(
165 | 'Arrays should have either numeric keys or string keys. Implicit numeric key detected,'
166 | . ' while all previous keys in this array were string keys.',
167 | $firstNonEmpty,
168 | 'ImplicitNumericKey'
169 | );
170 |
171 | // Stop the loop.
172 | return true;
173 | }
174 | }
175 |
--------------------------------------------------------------------------------
/Universal/Sniffs/Arrays/MixedKeyedUnkeyedArraySniff.php:
--------------------------------------------------------------------------------
1 | Key is the item number; value the stack point to the first non empty token in the item.
40 | */
41 | private $itemsWithoutKey = [];
42 |
43 | /**
44 | * Process the array declaration.
45 | *
46 | * @since 1.0.0
47 | *
48 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where the
49 | * token was found.
50 | *
51 | * @return void
52 | */
53 | public function processArray(File $phpcsFile)
54 | {
55 | // Reset properties before processing this array.
56 | $this->hasKeys = false;
57 | $this->itemsWithoutKey = [];
58 |
59 | parent::processArray($phpcsFile);
60 | }
61 |
62 | /**
63 | * Process the tokens in an array key.
64 | *
65 | * @since 1.0.0
66 | *
67 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where the
68 | * token was found.
69 | * @param int $startPtr The stack pointer to the first token in the "key" part of
70 | * an array item.
71 | * @param int $endPtr The stack pointer to the last token in the "key" part of
72 | * an array item.
73 | * @param int $itemNr Which item in the array is being handled.
74 | *
75 | * @return void
76 | */
77 | public function processKey(File $phpcsFile, $startPtr, $endPtr, $itemNr)
78 | {
79 | $this->hasKeys = true;
80 |
81 | // Process any previously encountered items without keys.
82 | if (empty($this->itemsWithoutKey) === false) {
83 | foreach ($this->itemsWithoutKey as $itemNr => $stackPtr) {
84 | $phpcsFile->addError(
85 | 'Inconsistent array detected. A mix of keyed and unkeyed array items is not allowed.'
86 | . ' The array item in position %d does not have an array key.',
87 | $stackPtr,
88 | 'Found',
89 | [$itemNr]
90 | );
91 | }
92 |
93 | // No need to do this again.
94 | $this->itemsWithoutKey = [];
95 | }
96 | }
97 |
98 | /**
99 | * Process an array item without an array key.
100 | *
101 | * @since 1.0.0
102 | *
103 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where the
104 | * token was found.
105 | * @param int $startPtr The stack pointer to the first token in the array item,
106 | * which in this case will be the first token of the array
107 | * value part of the array item.
108 | * @param int $itemNr Which item in the array is being handled.
109 | *
110 | * @return void
111 | */
112 | public function processNoKey(File $phpcsFile, $startPtr, $itemNr)
113 | {
114 | $firstNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, $startPtr, null, true);
115 | if ($firstNonEmpty === false || $this->tokens[$firstNonEmpty]['code'] === \T_COMMA) {
116 | // Shouldn't really be possible, but this must be a parse error (empty array item).
117 | return;
118 | }
119 |
120 | // If we already know there are keys in the array, throw an error message straight away.
121 | if ($this->hasKeys === true) {
122 | $phpcsFile->addError(
123 | 'Inconsistent array detected. A mix of keyed and unkeyed array items is not allowed.'
124 | . ' The array item in position %d does not have an array key.',
125 | $firstNonEmpty,
126 | 'Found',
127 | [$itemNr]
128 | );
129 | } else {
130 | // Save the array item info for later in case we do encounter an array key later on in the array.
131 | $this->itemsWithoutKey[$itemNr] = $firstNonEmpty;
132 | }
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/Universal/Sniffs/Classes/DisallowAnonClassParenthesesSniff.php:
--------------------------------------------------------------------------------
1 |
41 | */
42 | public function register()
43 | {
44 | return [\T_ANON_CLASS];
45 | }
46 |
47 | /**
48 | * Processes this test, when one of its tokens is encountered.
49 | *
50 | * @since 1.0.0
51 | *
52 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
53 | * @param int $stackPtr The position of the current token
54 | * in the stack passed in $tokens.
55 | *
56 | * @return void
57 | */
58 | public function process(File $phpcsFile, $stackPtr)
59 | {
60 | $tokens = $phpcsFile->getTokens();
61 | $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
62 |
63 | // Note: no need to check for `false` as PHPCS won't retokenize `class` to `T_ANON_CLASS` in that case.
64 | if ($tokens[$nextNonEmpty]['code'] !== \T_OPEN_PARENTHESIS) {
65 | // No parentheses found.
66 | $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'no');
67 | return;
68 | }
69 |
70 | if (isset($tokens[$nextNonEmpty]['parenthesis_closer']) === false) {
71 | /*
72 | * Incomplete set of parentheses. Ignore.
73 | * Shouldn't be possible as PHPCS won't retokenize `class` to `T_ANON_CLASS` in that case.
74 | */
75 | // @codeCoverageIgnoreStart
76 | $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'yes');
77 | return;
78 | // @codeCoverageIgnoreEnd
79 | }
80 |
81 | $opener = $nextNonEmpty;
82 | $closer = $tokens[$opener]['parenthesis_closer'];
83 | $hasParams = $phpcsFile->findNext(Tokens::$emptyTokens, ($opener + 1), $closer, true);
84 | if ($hasParams !== false) {
85 | // There is something between the parentheses. Ignore.
86 | $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'yes, with parameter(s)');
87 | return;
88 | }
89 |
90 | $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'yes');
91 |
92 | $fix = $phpcsFile->addFixableError(
93 | 'Parenthesis not allowed when creating a new anonymous class without passing parameters',
94 | $stackPtr,
95 | 'Found'
96 | );
97 |
98 | if ($fix === true) {
99 | $phpcsFile->fixer->beginChangeset();
100 |
101 | for ($i = $opener; $i <= $closer; $i++) {
102 | if (isset(Tokens::$commentTokens[$tokens[$i]['code']]) === true) {
103 | continue;
104 | }
105 |
106 | $phpcsFile->fixer->replaceToken($i, '');
107 | }
108 |
109 | $phpcsFile->fixer->endChangeset();
110 | }
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/Universal/Sniffs/Classes/DisallowFinalClassSniff.php:
--------------------------------------------------------------------------------
1 |
42 | */
43 | public function register()
44 | {
45 | return [\T_CLASS];
46 | }
47 |
48 | /**
49 | * Processes this test, when one of its tokens is encountered.
50 | *
51 | * @since 1.0.0
52 | *
53 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
54 | * @param int $stackPtr The position of the current token
55 | * in the stack passed in $tokens.
56 | *
57 | * @return void
58 | */
59 | public function process(File $phpcsFile, $stackPtr)
60 | {
61 | $classProp = ObjectDeclarations::getClassProperties($phpcsFile, $stackPtr);
62 | if ($classProp['is_final'] === false) {
63 | if ($classProp['is_abstract'] === true) {
64 | $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'abstract');
65 | return;
66 | }
67 |
68 | $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'not abstract, not final');
69 | return;
70 | }
71 |
72 | $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'final');
73 |
74 | $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
75 | if ($nextNonEmpty === false) {
76 | // Live coding or parse error.
77 | return;
78 | }
79 |
80 | // No extra safeguards needed, we know the keyword will exist based on the check above.
81 | $finalKeyword = $phpcsFile->findPrevious(\T_FINAL, ($stackPtr - 1));
82 | $snippetEnd = $nextNonEmpty;
83 | $classCloser = '';
84 |
85 | $tokens = $phpcsFile->getTokens();
86 | if (isset($tokens[$stackPtr]['scope_opener']) === true) {
87 | $snippetEnd = $tokens[$stackPtr]['scope_opener'];
88 | $classCloser = '}';
89 | }
90 |
91 | $snippet = GetTokensAsString::compact($phpcsFile, $finalKeyword, $snippetEnd, true);
92 | $fix = $phpcsFile->addFixableError(
93 | 'Declaring a class as final is not allowed. Found: %s%s',
94 | $finalKeyword,
95 | 'FinalClassFound',
96 | [$snippet, $classCloser]
97 | );
98 |
99 | if ($fix === true) {
100 | $phpcsFile->fixer->beginChangeset();
101 | $phpcsFile->fixer->replaceToken($finalKeyword, '');
102 |
103 | // Remove redundant whitespace.
104 | for ($i = ($finalKeyword + 1); $i < $stackPtr; $i++) {
105 | if ($tokens[$i]['code'] === \T_WHITESPACE) {
106 | $phpcsFile->fixer->replaceToken($i, '');
107 | continue;
108 | }
109 |
110 | break;
111 | }
112 |
113 | $phpcsFile->fixer->endChangeset();
114 | }
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/Universal/Sniffs/Classes/ModifierKeywordOrderSniff.php:
--------------------------------------------------------------------------------
1 |
73 | */
74 | public function register()
75 | {
76 | return [\T_CLASS];
77 | }
78 |
79 | /**
80 | * Processes this test, when one of its tokens is encountered.
81 | *
82 | * @since 1.0.0
83 | *
84 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
85 | * @param int $stackPtr The position of the current token
86 | * in the stack passed in $tokens.
87 | *
88 | * @return void
89 | */
90 | public function process(File $phpcsFile, $stackPtr)
91 | {
92 | $classProp = ObjectDeclarations::getClassProperties($phpcsFile, $stackPtr);
93 |
94 | if ($classProp['readonly_token'] === false
95 | || ($classProp['final_token'] === false && $classProp['abstract_token'] === false)
96 | ) {
97 | /*
98 | * Either no modifier keywords found at all; or only one type of modifier
99 | * keyword (abstract/final or readonly) declared, but not both. No ordering needed.
100 | */
101 | return;
102 | }
103 |
104 | if ($classProp['final_token'] !== false && $classProp['abstract_token'] !== false) {
105 | // Parse error. Ignore.
106 | return;
107 | }
108 |
109 | $readonly = $classProp['readonly_token'];
110 |
111 | if ($classProp['final_token'] !== false) {
112 | $extendability = $classProp['final_token'];
113 | } else {
114 | $extendability = $classProp['abstract_token'];
115 | }
116 |
117 | if ($readonly < $extendability) {
118 | $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, self::READONLY_EXTEND);
119 | } else {
120 | $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, self::EXTEND_READONLY);
121 | }
122 |
123 | $message = 'Class modifier keywords are not in the correct order. Expected: "%s", found: "%s"';
124 |
125 | switch ($this->order) {
126 | case self::READONLY_EXTEND:
127 | if ($readonly < $extendability) {
128 | // Order is correct. Nothing to do.
129 | return;
130 | }
131 |
132 | $this->handleError($phpcsFile, $extendability, $readonly);
133 | break;
134 |
135 | case self::EXTEND_READONLY:
136 | default:
137 | if ($extendability < $readonly) {
138 | // Order is correct. Nothing to do.
139 | return;
140 | }
141 |
142 | $this->handleError($phpcsFile, $readonly, $extendability);
143 | break;
144 | }
145 | }
146 |
147 | /**
148 | * Throw the error and potentially fix it.
149 | *
150 | * @since 1.0.0
151 | *
152 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
153 | * @param int $firstKeyword The position of the first keyword found.
154 | * @param int $secondKeyword The position of the second keyword token.
155 | *
156 | * @return void
157 | */
158 | private function handleError(File $phpcsFile, $firstKeyword, $secondKeyword)
159 | {
160 | $tokens = $phpcsFile->getTokens();
161 |
162 | $message = 'Class modifier keywords are not in the correct order. Expected: "%s", found: "%s"';
163 | $data = [
164 | $tokens[$secondKeyword]['content'] . ' ' . $tokens[$firstKeyword]['content'],
165 | $tokens[$firstKeyword]['content'] . ' ' . $tokens[$secondKeyword]['content'],
166 | ];
167 |
168 | $fix = $phpcsFile->addFixableError($message, $firstKeyword, 'Incorrect', $data);
169 |
170 | if ($fix === true) {
171 | $phpcsFile->fixer->beginChangeset();
172 |
173 | $phpcsFile->fixer->replaceToken($secondKeyword, '');
174 |
175 | // Prevent leaving behind trailing whitespace.
176 | $i = ($secondKeyword + 1);
177 | while ($tokens[$i]['code'] === \T_WHITESPACE) {
178 | $phpcsFile->fixer->replaceToken($i, '');
179 | ++$i;
180 | }
181 |
182 | // Use the original token content as the case used for keywords is not the concern of this sniff.
183 | $phpcsFile->fixer->addContentBefore($firstKeyword, $tokens[$secondKeyword]['content'] . ' ');
184 |
185 | $phpcsFile->fixer->endChangeset();
186 | }
187 | }
188 | }
189 |
--------------------------------------------------------------------------------
/Universal/Sniffs/Classes/RequireAnonClassParenthesesSniff.php:
--------------------------------------------------------------------------------
1 |
40 | */
41 | public function register()
42 | {
43 | return [\T_ANON_CLASS];
44 | }
45 |
46 | /**
47 | * Processes this test, when one of its tokens is encountered.
48 | *
49 | * @since 1.0.0
50 | *
51 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
52 | * @param int $stackPtr The position of the current token
53 | * in the stack passed in $tokens.
54 | *
55 | * @return void
56 | */
57 | public function process(File $phpcsFile, $stackPtr)
58 | {
59 | $tokens = $phpcsFile->getTokens();
60 | $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
61 |
62 | // Note: no need to check for `false` as PHPCS won't retokenize `class` to `T_ANON_CLASS` in that case.
63 | if ($tokens[$nextNonEmpty]['code'] === \T_OPEN_PARENTHESIS) {
64 | // Parentheses found.
65 | $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'yes');
66 | return;
67 | }
68 |
69 | $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'no');
70 |
71 | $fix = $phpcsFile->addFixableError(
72 | 'Parenthesis required when creating a new anonymous class.',
73 | $stackPtr,
74 | 'Missing'
75 | );
76 |
77 | if ($fix === true) {
78 | $phpcsFile->fixer->addContent($stackPtr, '()');
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/Universal/Sniffs/Classes/RequireFinalClassSniff.php:
--------------------------------------------------------------------------------
1 |
42 | */
43 | public function register()
44 | {
45 | return [\T_CLASS];
46 | }
47 |
48 | /**
49 | * Processes this test, when one of its tokens is encountered.
50 | *
51 | * @since 1.0.0
52 | *
53 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
54 | * @param int $stackPtr The position of the current token
55 | * in the stack passed in $tokens.
56 | *
57 | * @return void
58 | */
59 | public function process(File $phpcsFile, $stackPtr)
60 | {
61 | $classProp = ObjectDeclarations::getClassProperties($phpcsFile, $stackPtr);
62 | if ($classProp['is_final'] === true) {
63 | $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'final');
64 | return;
65 | }
66 |
67 | if ($classProp['is_abstract'] === true) {
68 | // Abstract classes can't be final.
69 | $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'abstract');
70 | return;
71 | }
72 |
73 | $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'not abstract, not final');
74 |
75 | $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
76 | if ($nextNonEmpty === false) {
77 | // Live coding or parse error.
78 | return;
79 | }
80 |
81 | $snippetEnd = $nextNonEmpty;
82 | $classCloser = '';
83 |
84 | $tokens = $phpcsFile->getTokens();
85 | if (isset($tokens[$stackPtr]['scope_opener']) === true) {
86 | $snippetEnd = $tokens[$stackPtr]['scope_opener'];
87 | $classCloser = '}';
88 | }
89 |
90 | $snippet = GetTokensAsString::compact($phpcsFile, $stackPtr, $snippetEnd, true);
91 | $fix = $phpcsFile->addFixableError(
92 | 'A non-abstract class should be declared as final. Found: %s%s',
93 | $stackPtr,
94 | 'NonFinalClassFound',
95 | [$snippet, $classCloser]
96 | );
97 |
98 | if ($fix === true) {
99 | $phpcsFile->fixer->addContentBefore($stackPtr, 'final ');
100 | }
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/Universal/Sniffs/CodeAnalysis/ForeachUniqueAssignmentSniff.php:
--------------------------------------------------------------------------------
1 |
37 | */
38 | public function register()
39 | {
40 | return [\T_FOREACH];
41 | }
42 |
43 | /**
44 | * Processes this test, when one of its tokens is encountered.
45 | *
46 | * @since 1.0.0
47 | *
48 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
49 | * @param int $stackPtr The position of the current token
50 | * in the stack passed in $tokens.
51 | *
52 | * @return void
53 | */
54 | public function process(File $phpcsFile, $stackPtr)
55 | {
56 | $tokens = $phpcsFile->getTokens();
57 |
58 | if (isset($tokens[$stackPtr]['parenthesis_opener'], $tokens[$stackPtr]['parenthesis_closer']) === false) {
59 | // Parse error or live coding, not our concern.
60 | return;
61 | }
62 |
63 | $opener = $tokens[$stackPtr]['parenthesis_opener'];
64 | $closer = $tokens[$stackPtr]['parenthesis_closer'];
65 |
66 | $asPtr = $phpcsFile->findNext(\T_AS, ($opener + 1), $closer);
67 | if ($asPtr === false) {
68 | // Parse error or live coding, not our concern.
69 | return;
70 | }
71 |
72 | // Real target.
73 | $find = [\T_DOUBLE_ARROW];
74 | // Prevent matching on double arrows within a list assignment.
75 | $find += Collections::listTokens();
76 |
77 | $doubleArrowPtr = $phpcsFile->findNext($find, ($asPtr + 1), $closer);
78 | if ($doubleArrowPtr === false
79 | || $tokens[$doubleArrowPtr]['code'] !== \T_DOUBLE_ARROW
80 | ) {
81 | // No key assignment.
82 | return;
83 | }
84 |
85 | $isListAssignment = $phpcsFile->findNext(Tokens::$emptyTokens, ($doubleArrowPtr + 1), $closer, true);
86 | if ($isListAssignment === false) {
87 | // Parse error or live coding, not our concern.
88 | }
89 |
90 | $keyAsString = \ltrim(GetTokensAsString::noEmpties($phpcsFile, ($asPtr + 1), ($doubleArrowPtr - 1)), '&');
91 | $valueAssignments = [];
92 | if (isset(Collections::listTokens()[$tokens[$isListAssignment]['code']]) === false) {
93 | // Single value assignment.
94 | $valueAssignments[] = GetTokensAsString::noEmpties($phpcsFile, ($doubleArrowPtr + 1), ($closer - 1));
95 | } else {
96 | // List assignment.
97 | $assignments = Lists::getAssignments($phpcsFile, $isListAssignment);
98 | foreach ($assignments as $listItem) {
99 | if ($listItem['assignment'] === '') {
100 | // Ignore empty list assignments.
101 | continue;
102 | }
103 |
104 | // Note: this doesn't take nested lists into account (yet).
105 | $valueAssignments[] = $listItem['assignment'];
106 | }
107 | }
108 |
109 | if (empty($valueAssignments)) {
110 | // No assignments found.
111 | return;
112 | }
113 |
114 | foreach ($valueAssignments as $valueAsString) {
115 | $valueAsString = \ltrim($valueAsString, '&');
116 |
117 | if ($keyAsString !== $valueAsString) {
118 | // Key and value not the same.
119 | continue;
120 | }
121 |
122 | $error = 'The variables used for the key and the value in a foreach assignment should be unique.';
123 | $error .= 'Both the key and the value will currently be assigned to: "%s"';
124 |
125 | $fix = $phpcsFile->addFixableError($error, $doubleArrowPtr, 'NotUnique', [$valueAsString]);
126 | if ($fix === true) {
127 | $phpcsFile->fixer->beginChangeset();
128 |
129 | // Remove the key.
130 | for ($i = ($asPtr + 1); $i < ($doubleArrowPtr + 1); $i++) {
131 | if ($tokens[$i]['code'] === \T_WHITESPACE
132 | && isset(Tokens::$commentTokens[$tokens[($i + 1)]['code']])
133 | ) {
134 | // Don't remove whitespace when followed directly by a comment.
135 | continue;
136 | }
137 |
138 | if (isset(Tokens::$commentTokens[$tokens[$i]['code']])) {
139 | // Don't remove comments.
140 | continue;
141 | }
142 |
143 | // Remove everything else.
144 | $phpcsFile->fixer->replaceToken($i, '');
145 | }
146 |
147 | $phpcsFile->fixer->endChangeset();
148 | }
149 |
150 | break;
151 | }
152 | }
153 | }
154 |
--------------------------------------------------------------------------------
/Universal/Sniffs/CodeAnalysis/NoEchoSprintfSniff.php:
--------------------------------------------------------------------------------
1 |
36 | */
37 | private $targetFunctions = [
38 | 'sprintf' => 'printf',
39 | 'vsprintf' => 'vprintf',
40 | ];
41 |
42 | /**
43 | * Returns an array of tokens this test wants to listen for.
44 | *
45 | * @since 1.1.0
46 | *
47 | * @return array
48 | */
49 | public function register()
50 | {
51 | return [\T_ECHO];
52 | }
53 |
54 | /**
55 | * Processes this test, when one of its tokens is encountered.
56 | *
57 | * @since 1.1.0
58 | *
59 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
60 | * @param int $stackPtr The position of the current token
61 | * in the stack passed in $tokens.
62 | *
63 | * @return void
64 | */
65 | public function process(File $phpcsFile, $stackPtr)
66 | {
67 | $tokens = $phpcsFile->getTokens();
68 |
69 | $skip = Tokens::$emptyTokens;
70 | $skip[] = \T_NS_SEPARATOR;
71 |
72 | $next = $phpcsFile->findNext($skip, ($stackPtr + 1), null, true);
73 | if ($next === false
74 | || $tokens[$next]['code'] !== \T_STRING
75 | || isset($this->targetFunctions[\strtolower($tokens[$next]['content'])]) === false
76 | ) {
77 | // Not our target.
78 | return;
79 | }
80 |
81 | $detectedFunction = \strtolower($tokens[$next]['content']);
82 |
83 | $openParens = $phpcsFile->findNext(Tokens::$emptyTokens, ($next + 1), null, true);
84 | if ($openParens === false
85 | || $tokens[$openParens]['code'] !== \T_OPEN_PARENTHESIS
86 | || isset($tokens[$openParens]['parenthesis_closer']) === false
87 | ) {
88 | // Live coding/parse error.
89 | return;
90 | }
91 |
92 | $closeParens = $tokens[$openParens]['parenthesis_closer'];
93 | $afterFunctionCall = $phpcsFile->findNext(Tokens::$emptyTokens, ($closeParens + 1), null, true);
94 | if ($afterFunctionCall === false
95 | || ($tokens[$afterFunctionCall]['code'] !== \T_SEMICOLON
96 | && $tokens[$afterFunctionCall]['code'] !== \T_CLOSE_TAG)
97 | ) {
98 | // Live coding/parse error or compound echo statement.
99 | return;
100 | }
101 |
102 | $fix = $phpcsFile->addFixableError(
103 | 'Unnecessary "echo %s(...)" found. Use "%s(...)" instead.',
104 | $next,
105 | 'Found',
106 | [
107 | $tokens[$next]['content'],
108 | $this->targetFunctions[$detectedFunction],
109 | ]
110 | );
111 |
112 | if ($fix === true) {
113 | $phpcsFile->fixer->beginChangeset();
114 |
115 | // Remove echo and whitespace.
116 | $phpcsFile->fixer->replaceToken($stackPtr, '');
117 |
118 | for ($i = ($stackPtr + 1); $i < $next; $i++) {
119 | if ($tokens[$i]['code'] !== \T_WHITESPACE) {
120 | break;
121 | }
122 |
123 | $phpcsFile->fixer->replaceToken($i, '');
124 | }
125 |
126 | $phpcsFile->fixer->replaceToken($next, $this->targetFunctions[$detectedFunction]);
127 |
128 | $phpcsFile->fixer->endChangeset();
129 | }
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/Universal/Sniffs/Constants/LowercaseClassResolutionKeywordSniff.php:
--------------------------------------------------------------------------------
1 |
43 | */
44 | public function register()
45 | {
46 | return [\T_STRING];
47 | }
48 |
49 | /**
50 | * Processes this test, when one of its tokens is encountered.
51 | *
52 | * @since 1.0.0
53 | *
54 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
55 | * @param int $stackPtr The position of the current token
56 | * in the stack passed in $tokens.
57 | *
58 | * @return void
59 | */
60 | public function process(File $phpcsFile, $stackPtr)
61 | {
62 | $tokens = $phpcsFile->getTokens();
63 | $content = $tokens[$stackPtr]['content'];
64 | $contentLC = \strtolower($content);
65 |
66 | if ($contentLC !== 'class') {
67 | return;
68 | }
69 |
70 | $nextToken = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
71 | if ($nextToken !== false && $tokens[$nextToken]['code'] === \T_OPEN_PARENTHESIS) {
72 | // Function call or declaration for a function called "class".
73 | return;
74 | }
75 |
76 | $prevToken = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
77 | if ($prevToken === false || $tokens[$prevToken]['code'] !== \T_DOUBLE_COLON) {
78 | return;
79 | }
80 |
81 | if ($contentLC === $content) {
82 | $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'lowercase');
83 | return;
84 | }
85 |
86 | $error = "The ::class keyword for class name resolution must be in lowercase. Expected: '::%s'; found: '::%s'";
87 | $data = [
88 | $contentLC,
89 | $content,
90 | ];
91 |
92 | $errorCode = '';
93 | if (\strtoupper($content) === $content) {
94 | $errorCode = 'Uppercase';
95 | $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'uppercase');
96 | } else {
97 | $errorCode = 'Mixedcase';
98 | $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'mixed case');
99 | }
100 |
101 | $fix = $phpcsFile->addFixableError($error, $stackPtr, $errorCode, $data);
102 | if ($fix === true) {
103 | $phpcsFile->fixer->replaceToken($stackPtr, $contentLC);
104 | }
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/Universal/Sniffs/Constants/ModifierKeywordOrderSniff.php:
--------------------------------------------------------------------------------
1 |
75 | */
76 | public function register()
77 | {
78 | return [\T_CONST];
79 | }
80 |
81 | /**
82 | * Processes this test, when one of its tokens is encountered.
83 | *
84 | * @since 1.0.0
85 | *
86 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
87 | * @param int $stackPtr The position of the current token
88 | * in the stack passed in $tokens.
89 | *
90 | * @return void
91 | */
92 | public function process(File $phpcsFile, $stackPtr)
93 | {
94 | if (Scopes::isOOConstant($phpcsFile, $stackPtr) === false) {
95 | return;
96 | }
97 |
98 | $tokens = $phpcsFile->getTokens();
99 | $valid = Collections::constantModifierKeywords() + Tokens::$emptyTokens;
100 |
101 | $finalPtr = false;
102 | $visibilityPtr = false;
103 |
104 | for ($i = ($stackPtr - 1); $i > 0; $i--) {
105 | if (isset($valid[$tokens[$i]['code']]) === false) {
106 | break;
107 | }
108 |
109 | if (isset(Tokens::$emptyTokens[$tokens[$i]['code']]) === true) {
110 | continue;
111 | }
112 |
113 | if ($tokens[$i]['code'] === \T_FINAL) {
114 | $finalPtr = $i;
115 | } else {
116 | $visibilityPtr = $i;
117 | }
118 | }
119 |
120 | if ($finalPtr === false || $visibilityPtr === false) {
121 | /*
122 | * Either no modifier keywords found at all; or only one type of modifier
123 | * keyword (final or visibility) declared, but not both. No ordering needed.
124 | */
125 | return;
126 | }
127 |
128 | if ($visibilityPtr < $finalPtr) {
129 | $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, self::VISIBILITY_FINAL);
130 | } else {
131 | $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, self::FINAL_VISIBILITY);
132 | }
133 |
134 | $message = 'OO constant modifier keywords are not in the correct order. Expected: "%s", found: "%s"';
135 |
136 | switch ($this->order) {
137 | case self::VISIBILITY_FINAL:
138 | if ($visibilityPtr < $finalPtr) {
139 | // Order is correct. Nothing to do.
140 | return;
141 | }
142 |
143 | $this->handleError($phpcsFile, $finalPtr, $visibilityPtr);
144 | break;
145 |
146 | case self::FINAL_VISIBILITY:
147 | default:
148 | if ($finalPtr < $visibilityPtr) {
149 | // Order is correct. Nothing to do.
150 | return;
151 | }
152 |
153 | $this->handleError($phpcsFile, $visibilityPtr, $finalPtr);
154 | break;
155 | }
156 | }
157 |
158 | /**
159 | * Throw the error and potentially fix it.
160 | *
161 | * @since 1.0.0
162 | *
163 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
164 | * @param int $firstKeyword The position of the first keyword found.
165 | * @param int $secondKeyword The position of the second keyword token.
166 | *
167 | * @return void
168 | */
169 | private function handleError(File $phpcsFile, $firstKeyword, $secondKeyword)
170 | {
171 | $tokens = $phpcsFile->getTokens();
172 |
173 | $message = 'Constant modifier keywords are not in the correct order. Expected: "%s", found: "%s"';
174 | $data = [
175 | $tokens[$secondKeyword]['content'] . ' ' . $tokens[$firstKeyword]['content'],
176 | $tokens[$firstKeyword]['content'] . ' ' . $tokens[$secondKeyword]['content'],
177 | ];
178 |
179 | $fix = $phpcsFile->addFixableError($message, $firstKeyword, 'Incorrect', $data);
180 |
181 | if ($fix === true) {
182 | $phpcsFile->fixer->beginChangeset();
183 |
184 | $phpcsFile->fixer->replaceToken($secondKeyword, '');
185 |
186 | // Prevent leaving behind trailing whitespace.
187 | $i = ($secondKeyword + 1);
188 | while ($tokens[$i]['code'] === \T_WHITESPACE) {
189 | $phpcsFile->fixer->replaceToken($i, '');
190 | ++$i;
191 | }
192 |
193 | // Use the original token content as the case used for keywords is not the concern of this sniff.
194 | $phpcsFile->fixer->addContentBefore($firstKeyword, $tokens[$secondKeyword]['content'] . ' ');
195 |
196 | $phpcsFile->fixer->endChangeset();
197 | }
198 | }
199 | }
200 |
--------------------------------------------------------------------------------
/Universal/Sniffs/Constants/UppercaseMagicConstantsSniff.php:
--------------------------------------------------------------------------------
1 |
42 | */
43 | public function register()
44 | {
45 | return Tokens::$magicConstants;
46 | }
47 |
48 | /**
49 | * Processes this test, when one of its tokens is encountered.
50 | *
51 | * @since 1.0.0
52 | *
53 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
54 | * @param int $stackPtr The position of the current token
55 | * in the stack passed in $tokens.
56 | *
57 | * @return void
58 | */
59 | public function process(File $phpcsFile, $stackPtr)
60 | {
61 | $tokens = $phpcsFile->getTokens();
62 | $content = $tokens[$stackPtr]['content'];
63 | $contentUC = \strtoupper($content);
64 | if ($contentUC === $content) {
65 | $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'uppercase');
66 | return;
67 | }
68 |
69 | $error = 'Magic constants should be in uppercase. Expected: %s; found: %s';
70 | $errorCode = '';
71 | $data = [
72 | $contentUC,
73 | $content,
74 | ];
75 |
76 | if (\strtolower($content) === $content) {
77 | $errorCode = 'Lowercase';
78 | $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'lowercase');
79 | } else {
80 | $errorCode = 'Mixedcase';
81 | $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'mixed case');
82 | }
83 |
84 | $fix = $phpcsFile->addFixableError($error, $stackPtr, $errorCode, $data);
85 | if ($fix === true) {
86 | $phpcsFile->fixer->replaceToken($stackPtr, $contentUC);
87 | }
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/Universal/Sniffs/ControlStructures/IfElseDeclarationSniff.php:
--------------------------------------------------------------------------------
1 |
49 | */
50 | public function register()
51 | {
52 | return [
53 | \T_ELSE,
54 | \T_ELSEIF,
55 | ];
56 | }
57 |
58 | /**
59 | * Processes this test, when one of its tokens is encountered.
60 | *
61 | * @since 1.0.0
62 | *
63 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
64 | * @param int $stackPtr The position of the current token
65 | * in the stack passed in $tokens.
66 | *
67 | * @return void
68 | */
69 | public function process(File $phpcsFile, $stackPtr)
70 | {
71 | $tokens = $phpcsFile->getTokens();
72 |
73 | /*
74 | * Check for control structures without braces and alternative syntax.
75 | */
76 | $scopePtr = $stackPtr;
77 | if (isset($tokens[$stackPtr]['scope_opener']) === false) {
78 | // Deal with "else if".
79 | $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
80 | if ($tokens[$next]['code'] === \T_IF) {
81 | $scopePtr = $next;
82 | }
83 | }
84 |
85 | if (isset($tokens[$scopePtr]['scope_opener']) === false
86 | || $tokens[$tokens[$scopePtr]['scope_opener']]['code'] === \T_COLON
87 | ) {
88 | // No scope opener found or alternative syntax (not our concern).
89 | return;
90 | }
91 |
92 | /*
93 | * Check whether the else(if) is on a new line.
94 | */
95 | $prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
96 | if ($prevNonEmpty === false || $tokens[$prevNonEmpty]['code'] !== \T_CLOSE_CURLY_BRACKET) {
97 | // Parse error or mixing braced and non-braced. Not our concern.
98 | return;
99 | }
100 |
101 | if ($tokens[$prevNonEmpty]['line'] !== $tokens[$stackPtr]['line']) {
102 | $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'yes');
103 | return;
104 | }
105 |
106 | $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'no');
107 |
108 | $errorBase = \strtoupper($tokens[$stackPtr]['content']);
109 | $error = $errorBase . ' statement must be on a new line.';
110 |
111 | $prevNonWhitespace = $phpcsFile->findPrevious(\T_WHITESPACE, ($stackPtr - 1), null, true);
112 |
113 | if ($prevNonWhitespace !== $prevNonEmpty) {
114 | // Comment found between previous scope closer and the keyword.
115 | $fix = $phpcsFile->addError($error, $stackPtr, 'NoNewLine');
116 | return;
117 | }
118 |
119 | $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NoNewLine');
120 | if ($fix === false) {
121 | return;
122 | }
123 |
124 | /*
125 | * Fix it.
126 | */
127 |
128 | // Figure out the indentation for the else(if).
129 | $indentBase = $prevNonEmpty;
130 | if (isset($tokens[$prevNonEmpty]['scope_condition']) === true
131 | && ($tokens[$tokens[$prevNonEmpty]['scope_condition']]['column'] === 1
132 | || ($tokens[($tokens[$prevNonEmpty]['scope_condition'] - 1)]['code'] === \T_WHITESPACE
133 | && $tokens[($tokens[$prevNonEmpty]['scope_condition'] - 1)]['column'] === 1))
134 | ) {
135 | // Base the indentation off the previous if/elseif if on a line by itself.
136 | $indentBase = $tokens[$prevNonEmpty]['scope_condition'];
137 | }
138 |
139 | $indent = '';
140 | $firstOnIndentLine = $indentBase;
141 | if ($tokens[$firstOnIndentLine]['column'] !== 1) {
142 | while (isset($tokens[($firstOnIndentLine - 1)]) && $tokens[--$firstOnIndentLine]['column'] !== 1);
143 |
144 | if ($tokens[$firstOnIndentLine]['code'] === \T_WHITESPACE) {
145 | $indent = $tokens[$firstOnIndentLine]['content'];
146 |
147 | // If tabs were replaced, use the original content.
148 | if (isset($tokens[$firstOnIndentLine]['orig_content']) === true) {
149 | $indent = $tokens[$firstOnIndentLine]['orig_content'];
150 | }
151 | }
152 | }
153 |
154 | $phpcsFile->fixer->beginChangeset();
155 |
156 | // Remove any whitespace between the previous scope closer and the else(if).
157 | for ($i = ($prevNonEmpty + 1); $i < $stackPtr; $i++) {
158 | $phpcsFile->fixer->replaceToken($i, '');
159 | }
160 |
161 | $phpcsFile->fixer->addContent($prevNonEmpty, $phpcsFile->eolChar . $indent);
162 | $phpcsFile->fixer->endChangeset();
163 | }
164 | }
165 |
--------------------------------------------------------------------------------
/Universal/Sniffs/Files/SeparateFunctionsFromOOSniff.php:
--------------------------------------------------------------------------------
1 |
53 | */
54 | private $search = [
55 | // Some tokens to help skip over structures we're not interested in.
56 | \T_START_HEREDOC => \T_START_HEREDOC,
57 | \T_START_NOWDOC => \T_START_NOWDOC,
58 | ];
59 |
60 | /**
61 | * Returns an array of tokens this test wants to listen for.
62 | *
63 | * @since 1.0.0
64 | *
65 | * @return array
66 | */
67 | public function register()
68 | {
69 | $this->search += Tokens::$ooScopeTokens;
70 | $this->search += Collections::functionDeclarationTokens();
71 |
72 | return Collections::phpOpenTags();
73 | }
74 |
75 | /**
76 | * Processes this test, when one of its tokens is encountered.
77 | *
78 | * @since 1.0.0
79 | *
80 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
81 | * @param int $stackPtr The position of the current token
82 | * in the stack passed in $tokens.
83 | *
84 | * @return int Integer stack pointer to skip forward.
85 | */
86 | public function process(File $phpcsFile, $stackPtr)
87 | {
88 | $tokens = $phpcsFile->getTokens();
89 |
90 | $firstOO = null;
91 | $firstFunction = null;
92 | $functionCount = 0;
93 | $OOCount = 0;
94 |
95 | for ($i = 0; $i < $phpcsFile->numTokens; $i++) {
96 | // Ignore anything within square brackets.
97 | if ($tokens[$i]['code'] !== \T_OPEN_CURLY_BRACKET
98 | && isset($tokens[$i]['bracket_opener'], $tokens[$i]['bracket_closer'])
99 | && $i === $tokens[$i]['bracket_opener']
100 | ) {
101 | $i = $tokens[$i]['bracket_closer'];
102 | continue;
103 | }
104 |
105 | // Skip past nested arrays, function calls and arbitrary groupings.
106 | if ($tokens[$i]['code'] === \T_OPEN_PARENTHESIS
107 | && isset($tokens[$i]['parenthesis_closer'])
108 | ) {
109 | $i = $tokens[$i]['parenthesis_closer'];
110 | continue;
111 | }
112 |
113 | // Skip over potentially large docblocks.
114 | if ($tokens[$i]['code'] === \T_DOC_COMMENT_OPEN_TAG
115 | && isset($tokens[$i]['comment_closer'])
116 | ) {
117 | $i = $tokens[$i]['comment_closer'];
118 | continue;
119 | }
120 |
121 | // Ignore everything else we're not interested in.
122 | if (isset($this->search[$tokens[$i]['code']]) === false) {
123 | continue;
124 | }
125 |
126 | // Skip over structures which won't contain anything we're interested in.
127 | if (($tokens[$i]['code'] === \T_START_HEREDOC
128 | || $tokens[$i]['code'] === \T_START_NOWDOC
129 | || $tokens[$i]['code'] === \T_ANON_CLASS
130 | || $tokens[$i]['code'] === \T_CLOSURE
131 | || $tokens[$i]['code'] === \T_FN)
132 | && isset($tokens[$i]['scope_condition'], $tokens[$i]['scope_closer'])
133 | && $tokens[$i]['scope_condition'] === $i
134 | ) {
135 | $i = $tokens[$i]['scope_closer'];
136 | continue;
137 | }
138 |
139 | // This will be either a function declaration or an OO declaration token.
140 | if ($tokens[$i]['code'] === \T_FUNCTION) {
141 | if (isset($firstFunction) === false) {
142 | $firstFunction = $i;
143 | }
144 |
145 | ++$functionCount;
146 | } else {
147 | if (isset($firstOO) === false) {
148 | $firstOO = $i;
149 | }
150 |
151 | ++$OOCount;
152 | }
153 |
154 | if (isset($tokens[$i]['scope_closer']) === true) {
155 | $i = $tokens[$i]['scope_closer'];
156 | }
157 | }
158 |
159 | if ($functionCount > 0 && $OOCount > 0) {
160 | $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'Both function and OO declarations');
161 |
162 | $reportToken = \max($firstFunction, $firstOO);
163 |
164 | $phpcsFile->addError(
165 | 'A file should either contain function declarations or OO structure declarations, but not both.'
166 | . ' Found %d function declaration(s) and %d OO structure declaration(s).'
167 | . ' The first function declaration was found on line %d;'
168 | . ' the first OO declaration was found on line %d',
169 | $reportToken,
170 | 'Mixed',
171 | [
172 | $functionCount,
173 | $OOCount,
174 | $tokens[$firstFunction]['line'],
175 | $tokens[$firstOO]['line'],
176 | ]
177 | );
178 | } elseif ($functionCount > 0) {
179 | $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'Only function(s)');
180 | } elseif ($OOCount > 0) {
181 | $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'Only OO structure(s)');
182 | } else {
183 | $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'Neither');
184 | }
185 |
186 | // Ignore the rest of the file.
187 | return $phpcsFile->numTokens;
188 | }
189 | }
190 |
--------------------------------------------------------------------------------
/Universal/Sniffs/FunctionDeclarations/RequireFinalMethodsInTraitsSniff.php:
--------------------------------------------------------------------------------
1 |
42 | */
43 | public function register()
44 | {
45 | return [\T_FUNCTION];
46 | }
47 |
48 | /**
49 | * Processes this test, when one of its tokens is encountered.
50 | *
51 | * @since 1.1.0
52 | *
53 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
54 | * @param int $stackPtr The position of the current token
55 | * in the stack passed in $tokens.
56 | *
57 | * @return void
58 | */
59 | public function process(File $phpcsFile, $stackPtr)
60 | {
61 | $tokens = $phpcsFile->getTokens();
62 | if (isset($tokens[$stackPtr]['parenthesis_opener']) === false) {
63 | // Parse error/live coding.
64 | return;
65 | }
66 |
67 | $scopePtr = Scopes::validDirectScope($phpcsFile, $stackPtr, \T_TRAIT);
68 | if ($scopePtr === false) {
69 | // Not a trait method.
70 | return;
71 | }
72 |
73 | $methodProps = FunctionDeclarations::getProperties($phpcsFile, $stackPtr);
74 | if ($methodProps['scope'] === 'private') {
75 | // Private methods can't be final.
76 | return;
77 | }
78 |
79 | if ($methodProps['is_final'] === true) {
80 | // Already final, nothing to do.
81 | $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'final');
82 | return;
83 | }
84 |
85 | if ($methodProps['is_abstract'] === true) {
86 | // Abstract classes can't be final.
87 | $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'abstract');
88 | return;
89 | }
90 |
91 | $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'not abstract, not final');
92 |
93 | $methodName = FunctionDeclarations::getName($phpcsFile, $stackPtr);
94 | $magic = '';
95 | $code = 'NonFinalMethodFound';
96 | if (FunctionDeclarations::isMagicMethodName($methodName) === true) {
97 | // Use separate error code for magic methods.
98 | $magic = 'magic ';
99 | $code = 'NonFinalMagicMethodFound';
100 | }
101 |
102 | $data = [
103 | $methodProps['scope'],
104 | $magic,
105 | $methodName,
106 | ObjectDeclarations::getName($phpcsFile, $scopePtr),
107 | ];
108 |
109 | $fix = $phpcsFile->addFixableError(
110 | 'The non-abstract, %s %smethod "%s()" in trait %s should be declared as final.',
111 | $stackPtr,
112 | $code,
113 | $data
114 | );
115 |
116 | if ($fix === true) {
117 | $phpcsFile->fixer->addContentBefore($stackPtr, 'final ');
118 | }
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/Universal/Sniffs/Lists/DisallowLongListSyntaxSniff.php:
--------------------------------------------------------------------------------
1 |
31 | */
32 | public function register()
33 | {
34 | return [\T_LIST];
35 | }
36 |
37 | /**
38 | * Processes this test, when one of its tokens is encountered.
39 | *
40 | * @since 1.0.0
41 | *
42 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
43 | * @param int $stackPtr The position of the current token
44 | * in the stack passed in $tokens.
45 | *
46 | * @return void
47 | */
48 | public function process(File $phpcsFile, $stackPtr)
49 | {
50 | $openClose = Lists::getOpenClose($phpcsFile, $stackPtr);
51 | if ($openClose === false) {
52 | // Live coding or parse error.
53 | return;
54 | }
55 |
56 | $fix = $phpcsFile->addFixableError('Long list syntax is not allowed', $stackPtr, 'Found');
57 |
58 | if ($fix === true) {
59 | $opener = $openClose['opener'];
60 | $closer = $openClose['closer'];
61 |
62 | $phpcsFile->fixer->beginChangeset();
63 |
64 | $phpcsFile->fixer->replaceToken($stackPtr, '');
65 | $phpcsFile->fixer->replaceToken($opener, '[');
66 | $phpcsFile->fixer->replaceToken($closer, ']');
67 |
68 | $phpcsFile->fixer->endChangeset();
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/Universal/Sniffs/Lists/DisallowShortListSyntaxSniff.php:
--------------------------------------------------------------------------------
1 |
39 | */
40 | public function register()
41 | {
42 | return Collections::listOpenTokensBC();
43 | }
44 |
45 | /**
46 | * Processes this test, when one of its tokens is encountered.
47 | *
48 | * @since 1.0.0
49 | *
50 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
51 | * @param int $stackPtr The position of the current token
52 | * in the stack passed in $tokens.
53 | *
54 | * @return void
55 | */
56 | public function process(File $phpcsFile, $stackPtr)
57 | {
58 | $tokens = $phpcsFile->getTokens();
59 |
60 | if ($tokens[$stackPtr]['code'] === \T_LIST) {
61 | $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'no');
62 | return;
63 | }
64 |
65 | $openClose = Lists::getOpenClose($phpcsFile, $stackPtr);
66 |
67 | if ($openClose === false) {
68 | // Not a short list, live coding or parse error.
69 | return;
70 | }
71 |
72 | $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'yes');
73 |
74 | $fix = $phpcsFile->addFixableError('Short list syntax is not allowed', $stackPtr, 'Found');
75 |
76 | if ($fix === true) {
77 | $opener = $openClose['opener'];
78 | $closer = $openClose['closer'];
79 |
80 | $phpcsFile->fixer->beginChangeset();
81 | $phpcsFile->fixer->replaceToken($opener, 'list(');
82 | $phpcsFile->fixer->replaceToken($closer, ')');
83 | $phpcsFile->fixer->endChangeset();
84 | }
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/Universal/Sniffs/Namespaces/DisallowCurlyBraceSyntaxSniff.php:
--------------------------------------------------------------------------------
1 |
40 | */
41 | public function register()
42 | {
43 | return [\T_NAMESPACE];
44 | }
45 |
46 | /**
47 | * Processes this test, when one of its tokens is encountered.
48 | *
49 | * @since 1.0.0
50 | *
51 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
52 | * @param int $stackPtr The position of the current token
53 | * in the stack passed in $tokens.
54 | *
55 | * @return void
56 | */
57 | public function process(File $phpcsFile, $stackPtr)
58 | {
59 | if (Namespaces::isDeclaration($phpcsFile, $stackPtr) === false) {
60 | // Namespace operator, not a declaration; or live coding/parse error.
61 | return;
62 | }
63 |
64 | $tokens = $phpcsFile->getTokens();
65 |
66 | if (isset($tokens[$stackPtr]['scope_condition']) === false
67 | || $tokens[$stackPtr]['scope_condition'] !== $stackPtr
68 | ) {
69 | $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'no');
70 | return;
71 | }
72 |
73 | $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'yes');
74 |
75 | $phpcsFile->addError(
76 | 'Namespace declarations using the curly brace syntax are not allowed.',
77 | $stackPtr,
78 | 'Forbidden'
79 | );
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/Universal/Sniffs/Namespaces/DisallowDeclarationWithoutNameSniff.php:
--------------------------------------------------------------------------------
1 |
40 | */
41 | public function register()
42 | {
43 | return [\T_NAMESPACE];
44 | }
45 |
46 | /**
47 | * Processes this test, when one of its tokens is encountered.
48 | *
49 | * @since 1.0.0
50 | *
51 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
52 | * @param int $stackPtr The position of the current token
53 | * in the stack passed in $tokens.
54 | *
55 | * @return void
56 | */
57 | public function process(File $phpcsFile, $stackPtr)
58 | {
59 | $name = Namespaces::getDeclaredName($phpcsFile, $stackPtr);
60 | if ($name === false) {
61 | // Use of the namespace keyword as an operator or live coding/parse error.
62 | return;
63 | }
64 |
65 | if ($name !== '') {
66 | // Named namespace.
67 | $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'yes');
68 | return;
69 | }
70 |
71 | $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'no');
72 |
73 | // Namespace declaration without namespace name (= global namespace).
74 | $phpcsFile->addError(
75 | 'Namespace declarations without a namespace name are not allowed.',
76 | $stackPtr,
77 | 'Forbidden'
78 | );
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/Universal/Sniffs/Namespaces/EnforceCurlyBraceSyntaxSniff.php:
--------------------------------------------------------------------------------
1 |
40 | */
41 | public function register()
42 | {
43 | return [\T_NAMESPACE];
44 | }
45 |
46 | /**
47 | * Processes this test, when one of its tokens is encountered.
48 | *
49 | * @since 1.0.0
50 | *
51 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
52 | * @param int $stackPtr The position of the current token
53 | * in the stack passed in $tokens.
54 | *
55 | * @return void
56 | */
57 | public function process(File $phpcsFile, $stackPtr)
58 | {
59 | if (Namespaces::isDeclaration($phpcsFile, $stackPtr) === false) {
60 | // Namespace operator, not a declaration; or live coding/parse error.
61 | return;
62 | }
63 |
64 | $tokens = $phpcsFile->getTokens();
65 |
66 | if (isset($tokens[$stackPtr]['scope_condition']) === true
67 | && $tokens[$stackPtr]['scope_condition'] === $stackPtr
68 | ) {
69 | $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'yes');
70 | return;
71 | }
72 |
73 | $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'no');
74 |
75 | $phpcsFile->addError(
76 | 'Namespace declarations without curly braces are not allowed.',
77 | $stackPtr,
78 | 'Forbidden'
79 | );
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/Universal/Sniffs/Namespaces/OneDeclarationPerFileSniff.php:
--------------------------------------------------------------------------------
1 |
49 | */
50 | public function register()
51 | {
52 | return [\T_NAMESPACE];
53 | }
54 |
55 | /**
56 | * Processes this test, when one of its tokens is encountered.
57 | *
58 | * @since 1.0.0
59 | *
60 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
61 | * @param int $stackPtr The position of the current token
62 | * in the stack passed in $tokens.
63 | *
64 | * @return void
65 | */
66 | public function process(File $phpcsFile, $stackPtr)
67 | {
68 | $fileName = $phpcsFile->getFilename();
69 | if ($this->currentFile !== $fileName) {
70 | // Reset the properties for each new file.
71 | $this->currentFile = $fileName;
72 | $this->declarationSeen = false;
73 | }
74 |
75 | if (Namespaces::isDeclaration($phpcsFile, $stackPtr) === false) {
76 | // Namespace operator, not a declaration; or live coding/parse error.
77 | return;
78 | }
79 |
80 | if ($this->declarationSeen === false) {
81 | // This is the first namespace declaration in the file.
82 | $this->declarationSeen = $stackPtr;
83 | return;
84 | }
85 |
86 | $tokens = $phpcsFile->getTokens();
87 |
88 | // OK, so this is a file with multiple namespace declarations.
89 | $phpcsFile->addError(
90 | 'There should be only one namespace declaration per file. The first declaration was found on line %d',
91 | $stackPtr,
92 | 'MultipleFound',
93 | [$tokens[$this->declarationSeen]['line']]
94 | );
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/Universal/Sniffs/Operators/DisallowLogicalAndOrSniff.php:
--------------------------------------------------------------------------------
1 |
43 | */
44 | private $metricType = [
45 | \T_LOGICAL_AND => 'logical (and/or)',
46 | \T_LOGICAL_OR => 'logical (and/or)',
47 | \T_BOOLEAN_AND => 'boolean (&&/||)',
48 | \T_BOOLEAN_OR => 'boolean (&&/||)',
49 | ];
50 |
51 | /**
52 | * The tokens this sniff targets with error code and replacements.
53 | *
54 | * @since 1.0.0
55 | *
56 | * @var array>
57 | */
58 | private $targetTokenInfo = [
59 | \T_LOGICAL_AND => [
60 | 'error_code' => 'LogicalAnd',
61 | 'replacement' => '&&',
62 | ],
63 | \T_LOGICAL_OR => [
64 | 'error_code' => 'LogicalOr',
65 | 'replacement' => '||',
66 | ],
67 | ];
68 |
69 | /**
70 | * Returns an array of tokens this test wants to listen for.
71 | *
72 | * @since 1.0.0
73 | *
74 | * @return array
75 | */
76 | public function register()
77 | {
78 | return \array_keys($this->metricType);
79 | }
80 |
81 | /**
82 | * Processes this test, when one of its tokens is encountered.
83 | *
84 | * @since 1.0.0
85 | *
86 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
87 | * @param int $stackPtr The position of the current token
88 | * in the stack passed in $tokens.
89 | *
90 | * @return void
91 | */
92 | public function process(File $phpcsFile, $stackPtr)
93 | {
94 | $tokens = $phpcsFile->getTokens();
95 | $tokenCode = $tokens[$stackPtr]['code'];
96 |
97 | $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, $this->metricType[$tokenCode]);
98 |
99 | if (isset($this->targetTokenInfo[$tokenCode]) === false) {
100 | // Already using boolean operator.
101 | return;
102 | }
103 |
104 | $error = 'Using logical operators is not allowed. Expected: "%s"; Found: "%s"';
105 | $data = [
106 | $this->targetTokenInfo[$tokenCode]['replacement'],
107 | $tokens[ $stackPtr ]['content'],
108 | ];
109 |
110 | $phpcsFile->addError($error, $stackPtr, $this->targetTokenInfo[$tokenCode]['error_code'], $data);
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/Universal/Sniffs/Operators/DisallowShortTernarySniff.php:
--------------------------------------------------------------------------------
1 |
44 | */
45 | public function register()
46 | {
47 | return [\T_INLINE_THEN];
48 | }
49 |
50 | /**
51 | * Processes this test, when one of its tokens is encountered.
52 | *
53 | * @since 1.0.0
54 | *
55 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
56 | * @param int $stackPtr The position of the current token
57 | * in the stack passed in $tokens.
58 | *
59 | * @return void
60 | */
61 | public function process(File $phpcsFile, $stackPtr)
62 | {
63 | if (Operators::isShortTernary($phpcsFile, $stackPtr) === false) {
64 | $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'long');
65 | return;
66 | }
67 |
68 | $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'short');
69 |
70 | $phpcsFile->addError(
71 | 'Using short ternaries is not allowed as they are rarely used correctly',
72 | $stackPtr,
73 | 'Found'
74 | );
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/Universal/Sniffs/Operators/StrictComparisonsSniff.php:
--------------------------------------------------------------------------------
1 |
42 | */
43 | private $metricType = [
44 | \T_IS_EQUAL => 'loose',
45 | \T_IS_NOT_EQUAL => 'loose',
46 | \T_IS_IDENTICAL => 'strict',
47 | \T_IS_NOT_IDENTICAL => 'strict',
48 | ];
49 |
50 | /**
51 | * The tokens this sniff targets with error code and replacements.
52 | *
53 | * @since 1.0.0
54 | *
55 | * @var array>
56 | */
57 | private $targetTokenInfo = [
58 | \T_IS_EQUAL => [
59 | 'error_code' => 'LooseEqual',
60 | 'replacement' => '===',
61 | ],
62 | \T_IS_NOT_EQUAL => [
63 | 'error_code' => 'LooseNotEqual',
64 | 'replacement' => '!==',
65 | ],
66 | ];
67 |
68 | /**
69 | * Returns an array of tokens this test wants to listen for.
70 | *
71 | * @since 1.0.0
72 | *
73 | * @return array
74 | */
75 | public function register()
76 | {
77 | return \array_keys($this->metricType);
78 | }
79 |
80 | /**
81 | * Processes this test, when one of its tokens is encountered.
82 | *
83 | * @since 1.0.0
84 | *
85 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
86 | * @param int $stackPtr The position of the current token
87 | * in the stack passed in $tokens.
88 | *
89 | * @return void
90 | */
91 | public function process(File $phpcsFile, $stackPtr)
92 | {
93 | $tokens = $phpcsFile->getTokens();
94 | $tokenCode = $tokens[$stackPtr]['code'];
95 |
96 | $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, $this->metricType[$tokenCode]);
97 |
98 | if (isset($this->targetTokenInfo[$tokenCode]) === false) {
99 | // Already using strict comparison operator.
100 | return;
101 | }
102 |
103 | $error = 'Loose comparisons are not allowed. Expected: "%s"; Found: "%s"';
104 | $data = [
105 | $this->targetTokenInfo[$tokenCode]['replacement'],
106 | $tokens[ $stackPtr ]['content'],
107 | ];
108 |
109 | $fix = $phpcsFile->addFixableError($error, $stackPtr, $this->targetTokenInfo[$tokenCode]['error_code'], $data);
110 | if ($fix === false) {
111 | return;
112 | }
113 |
114 | $phpcsFile->fixer->replaceToken($stackPtr, $this->targetTokenInfo[$tokenCode]['replacement']);
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/Universal/Sniffs/Operators/TypeSeparatorSpacingSniff.php:
--------------------------------------------------------------------------------
1 |
34 | */
35 | private $targetTokens = [
36 | \T_TYPE_UNION => \T_TYPE_UNION,
37 | \T_TYPE_INTERSECTION => \T_TYPE_INTERSECTION,
38 | \T_TYPE_OPEN_PARENTHESIS => \T_TYPE_OPEN_PARENTHESIS,
39 | \T_TYPE_CLOSE_PARENTHESIS => \T_TYPE_CLOSE_PARENTHESIS,
40 | ];
41 |
42 | /**
43 | * Returns an array of tokens this test wants to listen for.
44 | *
45 | * @since 1.0.0
46 | *
47 | * @return array
48 | */
49 | public function register()
50 | {
51 | return $this->targetTokens;
52 | }
53 |
54 | /**
55 | * Processes this test, when one of its tokens is encountered.
56 | *
57 | * @since 1.0.0
58 | *
59 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
60 | * @param int $stackPtr The position of the current token
61 | * in the stack passed in $tokens.
62 | *
63 | * @return void
64 | */
65 | public function process(File $phpcsFile, $stackPtr)
66 | {
67 | $tokens = $phpcsFile->getTokens();
68 |
69 | $type = 'union';
70 | $code = 'UnionType';
71 | if ($tokens[$stackPtr]['code'] === \T_TYPE_INTERSECTION) {
72 | $type = 'intersection';
73 | $code = 'IntersectionType';
74 | } elseif ($tokens[$stackPtr]['code'] === \T_TYPE_OPEN_PARENTHESIS) {
75 | $type = 'DNF parenthesis open';
76 | $code = 'DNFOpen';
77 | } elseif ($tokens[$stackPtr]['code'] === \T_TYPE_CLOSE_PARENTHESIS) {
78 | $type = 'DNF parenthesis close';
79 | $code = 'DNFClose';
80 | }
81 |
82 | $expectedSpaces = 0;
83 | $prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
84 | if ($tokens[$stackPtr]['code'] === \T_TYPE_OPEN_PARENTHESIS) {
85 | if ($tokens[$prevNonEmpty]['code'] === \T_COLON
86 | || $tokens[$prevNonEmpty]['code'] === \T_CONST
87 | || isset(Collections::propertyModifierKeywords()[$tokens[$prevNonEmpty]['code']]) === true
88 | ) {
89 | // Start of return type or property/const type. Always demand 1 space.
90 | $expectedSpaces = 1;
91 | }
92 |
93 | if ($tokens[$prevNonEmpty]['code'] === \T_OPEN_PARENTHESIS
94 | || $tokens[$prevNonEmpty]['code'] === \T_COMMA
95 | ) {
96 | // Start of parameter type. Allow new line/indent before.
97 | if ($tokens[$prevNonEmpty]['line'] === $tokens[$stackPtr]['line']) {
98 | $expectedSpaces = 1;
99 | } else {
100 | $expectedSpaces = 'skip';
101 | }
102 | }
103 | }
104 |
105 | if (isset($this->targetTokens[$tokens[$prevNonEmpty]['code']]) === true) {
106 | // Prevent duplicate errors when there are two adjacent operators.
107 | $expectedSpaces = 'skip';
108 | }
109 |
110 | if ($expectedSpaces !== 'skip') {
111 | SpacesFixer::checkAndFix(
112 | $phpcsFile,
113 | $stackPtr,
114 | $prevNonEmpty,
115 | $expectedSpaces,
116 | 'Expected %s before the ' . $type . ' type separator. Found: %s',
117 | $code . 'SpacesBefore',
118 | 'error',
119 | 0, // Severity.
120 | 'Space before ' . $type . ' type separator'
121 | );
122 | }
123 |
124 | $expectedSpaces = 0;
125 | $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
126 | if ($tokens[$stackPtr]['code'] === \T_TYPE_CLOSE_PARENTHESIS) {
127 | if ($tokens[$nextNonEmpty]['code'] === \T_OPEN_CURLY_BRACKET
128 | || $tokens[$nextNonEmpty]['code'] === \T_VARIABLE
129 | || $tokens[$nextNonEmpty]['code'] === \T_STRING
130 | ) {
131 | // End of return type, parameter or property/const type. Always demand 1 space.
132 | $expectedSpaces = 1;
133 | }
134 | }
135 |
136 | SpacesFixer::checkAndFix(
137 | $phpcsFile,
138 | $stackPtr,
139 | $nextNonEmpty,
140 | $expectedSpaces,
141 | 'Expected %s after the ' . $type . ' type separator. Found: %s',
142 | $code . 'SpacesAfter',
143 | 'error',
144 | 0, // Severity.
145 | 'Space after ' . $type . ' type separator'
146 | );
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/Universal/Sniffs/PHP/LowercasePHPTagSniff.php:
--------------------------------------------------------------------------------
1 |
39 | */
40 | public function register()
41 | {
42 | return [\T_OPEN_TAG];
43 | }
44 |
45 | /**
46 | * Processes this test, when one of its tokens is encountered.
47 | *
48 | * @since 1.2.0
49 | *
50 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
51 | * @param int $stackPtr The position of the current token
52 | * in the stack passed in $tokens.
53 | *
54 | * @return void
55 | */
56 | public function process(File $phpcsFile, $stackPtr)
57 | {
58 | $tokens = $phpcsFile->getTokens();
59 | $content = $tokens[$stackPtr]['content'];
60 | $contentLC = \strtolower($content);
61 |
62 | if ($contentLC === $content) {
63 | $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'lowercase');
64 | return;
65 | }
66 |
67 | $errorCode = '';
68 | if (\strtoupper($content) === $content) {
69 | $errorCode = 'Uppercase';
70 | $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'uppercase');
71 | } else {
72 | $errorCode = 'Mixedcase';
73 | $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'mixed case');
74 | }
75 |
76 | $fix = $phpcsFile->addFixableError(
77 | 'The php open tag should be in lowercase. Found: %s',
78 | $stackPtr,
79 | $errorCode,
80 | [\trim($content)]
81 | );
82 |
83 | if ($fix === true) {
84 | $phpcsFile->fixer->replaceToken($stackPtr, $contentLC);
85 | }
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/Universal/Sniffs/PHP/NoFQNTrueFalseNullSniff.php:
--------------------------------------------------------------------------------
1 |
31 | */
32 | public function register()
33 | {
34 | return [
35 | // PHP < 8.0.
36 | \T_TRUE,
37 | \T_FALSE,
38 | \T_NULL,
39 |
40 | // PHP >= 8.0.
41 | \T_STRING,
42 | ];
43 | }
44 |
45 | /**
46 | * Processes this test, when one of its tokens is encountered.
47 | *
48 | * @since 1.3.0
49 | *
50 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
51 | * @param int $stackPtr The position of the current token
52 | * in the stack passed in $tokens.
53 | *
54 | * @return void
55 | */
56 | public function process(File $phpcsFile, $stackPtr)
57 | {
58 | $tokens = $phpcsFile->getTokens();
59 | $content = $tokens[$stackPtr]['content'];
60 | $contentLC = \strtolower($content);
61 |
62 | if ($contentLC !== 'true' && $contentLC !== 'false' && $contentLC !== 'null') {
63 | return;
64 | }
65 |
66 | $prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
67 | if ($tokens[$prev]['code'] !== \T_NS_SEPARATOR) {
68 | return;
69 | }
70 |
71 | $prevPrev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($prev - 1), null, true);
72 | if ($tokens[$prevPrev]['code'] === \T_STRING || $tokens[$prevPrev]['code'] === \T_NAMESPACE) {
73 | return;
74 | }
75 |
76 | $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
77 | if ($tokens[$next]['code'] === \T_NS_SEPARATOR) {
78 | return;
79 | }
80 |
81 | $fix = $phpcsFile->addFixableError(
82 | 'The special PHP constant "%s" should not be fully qualified.',
83 | $prev,
84 | 'Found',
85 | [$contentLC]
86 | );
87 |
88 | if ($fix === true) {
89 | $phpcsFile->fixer->replaceToken($prev, '');
90 | }
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/Universal/Sniffs/PHP/OneStatementInShortEchoTagSniff.php:
--------------------------------------------------------------------------------
1 |
36 | */
37 | public function register()
38 | {
39 | return [\T_OPEN_TAG_WITH_ECHO];
40 | }
41 |
42 | /**
43 | * Processes this test, when one of its tokens is encountered.
44 | *
45 | * @since 1.0.0
46 | *
47 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
48 | * @param int $stackPtr The position of the current token
49 | * in the stack passed in $tokens.
50 | *
51 | * @return void
52 | */
53 | public function process(File $phpcsFile, $stackPtr)
54 | {
55 | $tokens = $phpcsFile->getTokens();
56 |
57 | for ($endOfStatement = ($stackPtr + 1); $endOfStatement < $phpcsFile->numTokens; $endOfStatement++) {
58 | if ($tokens[$endOfStatement]['code'] === \T_CLOSE_TAG
59 | || $tokens[$endOfStatement]['code'] === \T_SEMICOLON
60 | ) {
61 | break;
62 | }
63 |
64 | // Skip over anything within parenthesis.
65 | if ($tokens[$endOfStatement]['code'] === \T_OPEN_PARENTHESIS
66 | && isset($tokens[$endOfStatement]['parenthesis_closer'])
67 | ) {
68 | $endOfStatement = $tokens[$endOfStatement]['parenthesis_closer'];
69 | }
70 | }
71 |
72 | if ($endOfStatement === $phpcsFile->numTokens
73 | || $tokens[$endOfStatement]['code'] === \T_CLOSE_TAG
74 | ) {
75 | return;
76 | }
77 |
78 | // Semicolon, so check for any code between it and the close tag.
79 | $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($endOfStatement + 1), null, true);
80 | if ($nextNonEmpty === false
81 | || $tokens[$nextNonEmpty]['code'] === \T_CLOSE_TAG
82 | ) {
83 | return;
84 | }
85 |
86 | $fix = $phpcsFile->addFixableError(
87 | 'Only one statement is allowed when using short open echo PHP tags.'
88 | . ' Use the "fixer->replaceToken($stackPtr, 'fixer->replaceToken($stackPtr, '
44 | */
45 | protected $keywords = [
46 | 'const' => true,
47 | 'function' => true,
48 | ];
49 |
50 | /**
51 | * Returns an array of tokens this test wants to listen for.
52 | *
53 | * @since 1.0.0
54 | *
55 | * @return array
56 | */
57 | public function register()
58 | {
59 | return [\T_USE];
60 | }
61 |
62 | /**
63 | * Processes this test, when one of its tokens is encountered.
64 | *
65 | * @since 1.0.0
66 | *
67 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
68 | * @param int $stackPtr The position of the current token in the
69 | * stack passed in $tokens.
70 | *
71 | * @return void
72 | */
73 | public function process(File $phpcsFile, $stackPtr)
74 | {
75 | if (UseStatements::isImportUse($phpcsFile, $stackPtr) === false) {
76 | // Trait or closure use statement.
77 | return;
78 | }
79 |
80 | $tokens = $phpcsFile->getTokens();
81 | $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
82 | if ($nextNonEmpty === false) {
83 | // Live coding or parse error.
84 | return;
85 | }
86 |
87 | if (isset($this->keywords[\strtolower($tokens[$nextNonEmpty]['content'])]) === true) {
88 | // Keyword found at start of statement, applies to whole statement.
89 | $this->processKeyword($phpcsFile, $nextNonEmpty, $tokens[$nextNonEmpty]['content']);
90 | return;
91 | }
92 |
93 | // This may still be a group use statement with function/const substatements.
94 | $openGroup = $phpcsFile->findNext([\T_SEMICOLON, \T_CLOSE_TAG, \T_OPEN_USE_GROUP], ($stackPtr + 1));
95 | if ($openGroup === false || $tokens[$openGroup]['code'] !== \T_OPEN_USE_GROUP) {
96 | // Not a group use statement.
97 | return;
98 | }
99 |
100 | $closeGroup = $phpcsFile->findNext([\T_SEMICOLON, \T_CLOSE_TAG, \T_CLOSE_USE_GROUP], ($openGroup + 1));
101 | if ($closeGroup === false || $tokens[$closeGroup]['code'] !== \T_CLOSE_USE_GROUP) {
102 | // Live coding or parse error.
103 | return;
104 | }
105 |
106 | $current = $openGroup;
107 | do {
108 | $current = $phpcsFile->findNext(Tokens::$emptyTokens, ($current + 1), $closeGroup, true);
109 | if ($current === false) {
110 | return;
111 | }
112 |
113 | if (isset($this->keywords[\strtolower($tokens[$current]['content'])]) === true) {
114 | $this->processKeyword($phpcsFile, $current, $tokens[$current]['content']);
115 | }
116 |
117 | // We're within the use group, so find the next comma.
118 | $current = $phpcsFile->findNext(\T_COMMA, ($current + 1), $closeGroup);
119 | } while ($current !== false);
120 | }
121 |
122 | /**
123 | * Processes a found keyword.
124 | *
125 | * @since 1.0.0
126 | *
127 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
128 | * @param int $stackPtr The position of the keyword in the token stack.
129 | * @param string $content The keyword as found.
130 | *
131 | * @return void
132 | */
133 | public function processKeyword(File $phpcsFile, $stackPtr, $content)
134 | {
135 | $contentLC = \strtolower($content);
136 | $metricName = \sprintf(self::METRIC_NAME, $contentLC);
137 | if ($contentLC === $content) {
138 | // Already lowercase. Bow out.
139 | $phpcsFile->recordMetric($stackPtr, $metricName, 'lowercase');
140 | return;
141 | }
142 |
143 | if (\strtoupper($content) === $content) {
144 | $phpcsFile->recordMetric($stackPtr, $metricName, 'uppercase');
145 | } else {
146 | $phpcsFile->recordMetric($stackPtr, $metricName, 'mixed case');
147 | }
148 |
149 | $error = 'The "%s" keyword when used in an import use statements must be lowercase.';
150 | $fix = $phpcsFile->addFixableError($error, $stackPtr, 'NotLowercase', [$contentLC]);
151 |
152 | if ($fix === true) {
153 | $phpcsFile->fixer->replaceToken($stackPtr, $contentLC);
154 | }
155 | }
156 | }
157 |
--------------------------------------------------------------------------------
/Universal/Sniffs/UseStatements/NoLeadingBackslashSniff.php:
--------------------------------------------------------------------------------
1 |
45 | */
46 | public function register()
47 | {
48 | return [\T_USE];
49 | }
50 |
51 | /**
52 | * Processes this test, when one of its tokens is encountered.
53 | *
54 | * @since 1.0.0
55 | *
56 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
57 | * @param int $stackPtr The position of the current token in the
58 | * stack passed in $tokens.
59 | *
60 | * @return void
61 | */
62 | public function process(File $phpcsFile, $stackPtr)
63 | {
64 | if (UseStatements::isImportUse($phpcsFile, $stackPtr) === false) {
65 | // Trait or closure use statement.
66 | return;
67 | }
68 |
69 | $endOfStatement = $phpcsFile->findNext([\T_SEMICOLON, \T_CLOSE_TAG, \T_OPEN_USE_GROUP], ($stackPtr + 1));
70 | if ($endOfStatement === false) {
71 | // Live coding or parse error.
72 | return;
73 | }
74 |
75 | $tokens = $phpcsFile->getTokens();
76 | $current = $stackPtr;
77 |
78 | do {
79 | $continue = $this->processImport($phpcsFile, $current, $endOfStatement);
80 | if ($continue === false) {
81 | break;
82 | }
83 |
84 | // Move the stackPtr forward to the next part of the use statement, if any.
85 | $current = $phpcsFile->findNext(\T_COMMA, ($current + 1), $endOfStatement);
86 | } while ($current !== false);
87 |
88 | if ($tokens[$endOfStatement]['code'] !== \T_OPEN_USE_GROUP) {
89 | // Finished the statement.
90 | return;
91 | }
92 |
93 | $current = $endOfStatement; // Group open brace.
94 | $endOfStatement = $phpcsFile->findNext([\T_CLOSE_USE_GROUP], ($endOfStatement + 1));
95 | if ($endOfStatement === false) {
96 | // Live coding or parse error.
97 | return;
98 | }
99 |
100 | do {
101 | $continue = $this->processImport($phpcsFile, $current, $endOfStatement, true);
102 | if ($continue === false) {
103 | break;
104 | }
105 |
106 | // Move the stackPtr forward to the next part of the use statement, if any.
107 | $current = $phpcsFile->findNext(\T_COMMA, ($current + 1), $endOfStatement);
108 | } while ($current !== false);
109 | }
110 |
111 | /**
112 | * Examine an individual import statement.
113 | *
114 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
115 | * @param int $stackPtr The position of the current token.
116 | * @param int $endOfStatement End token for the current import statement.
117 | * @param bool $groupUse Whether the current statement is a partial one
118 | * within a group use statement.
119 | *
120 | * @return bool Whether or not to continue examining this import use statement.
121 | */
122 | private function processImport(File $phpcsFile, $stackPtr, $endOfStatement, $groupUse = false)
123 | {
124 | $tokens = $phpcsFile->getTokens();
125 |
126 | $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), $endOfStatement, true);
127 | if ($nextNonEmpty === false) {
128 | // Reached the end of the statement.
129 | return false;
130 | }
131 |
132 | // Skip past 'function'/'const' keyword.
133 | $contentLC = \strtolower($tokens[$nextNonEmpty]['content']);
134 | if ($tokens[$nextNonEmpty]['code'] === \T_STRING
135 | && ($contentLC === 'function' || $contentLC === 'const')
136 | ) {
137 | $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($nextNonEmpty + 1), $endOfStatement, true);
138 | if ($nextNonEmpty === false) {
139 | // Reached the end of the statement.
140 | return false;
141 | }
142 | }
143 |
144 | if ($tokens[$nextNonEmpty]['code'] === \T_NS_SEPARATOR) {
145 | $phpcsFile->recordMetric($nextNonEmpty, self::METRIC_NAME, 'yes');
146 |
147 | $error = 'An import use statement should never start with a leading backslash';
148 | $code = 'LeadingBackslashFound';
149 |
150 | if ($groupUse === true) {
151 | $error = 'Parse error: partial import use statement in a use group starting with a leading backslash';
152 | $code = 'LeadingBackslashFoundInGroup';
153 | }
154 |
155 | $fix = $phpcsFile->addFixableError($error, $nextNonEmpty, $code);
156 |
157 | if ($fix === true) {
158 | if ($tokens[$nextNonEmpty - 1]['code'] !== \T_WHITESPACE) {
159 | $phpcsFile->fixer->replaceToken($nextNonEmpty, ' ');
160 | } else {
161 | $phpcsFile->fixer->replaceToken($nextNonEmpty, '');
162 | }
163 | }
164 | } else {
165 | $phpcsFile->recordMetric($nextNonEmpty, self::METRIC_NAME, 'no');
166 | }
167 |
168 | return true;
169 | }
170 | }
171 |
--------------------------------------------------------------------------------
/Universal/Sniffs/UseStatements/NoUselessAliasesSniff.php:
--------------------------------------------------------------------------------
1 |
37 | */
38 | public function register()
39 | {
40 | return [\T_USE];
41 | }
42 |
43 | /**
44 | * Processes this test, when one of its tokens is encountered.
45 | *
46 | * @since 1.1.0
47 | *
48 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
49 | * @param int $stackPtr The position of the current token
50 | * in the stack passed in $tokens.
51 | *
52 | * @return void
53 | */
54 | public function process(File $phpcsFile, $stackPtr)
55 | {
56 | if (UseStatements::isImportUse($phpcsFile, $stackPtr) === false) {
57 | // Closure or trait use statement. Bow out.
58 | return;
59 | }
60 |
61 | $endOfStatement = $phpcsFile->findNext([\T_SEMICOLON, \T_CLOSE_TAG], ($stackPtr + 1));
62 | if ($endOfStatement === false) {
63 | // Parse error or live coding.
64 | return;
65 | }
66 |
67 | $hasAliases = $phpcsFile->findNext(\T_AS, ($stackPtr + 1), $endOfStatement);
68 | if ($hasAliases === false) {
69 | // This use import statement does not alias anything, bow out.
70 | return;
71 | }
72 |
73 | $useStatements = UseStatements::splitImportUseStatement($phpcsFile, $stackPtr);
74 | if (\count($useStatements, \COUNT_RECURSIVE) <= 3) {
75 | // No statements found. Shouldn't be possible, but still. Bow out.
76 | return;
77 | }
78 |
79 | $tokens = $phpcsFile->getTokens();
80 |
81 | // Collect all places where aliases are used in this use statement.
82 | $aliasPtrs = [];
83 | $currentAs = $hasAliases;
84 | do {
85 | $aliasPtr = $phpcsFile->findNext(Tokens::$emptyTokens, ($currentAs + 1), null, true);
86 | if ($aliasPtr !== false && $tokens[$aliasPtr]['code'] === \T_STRING) {
87 | $aliasPtrs[$currentAs] = $aliasPtr;
88 | }
89 |
90 | $currentAs = $phpcsFile->findNext(\T_AS, ($currentAs + 1), $endOfStatement);
91 | } while ($currentAs !== false);
92 |
93 | // Now check the names in each use statement for useless aliases.
94 | foreach ($useStatements as $type => $statements) {
95 | foreach ($statements as $alias => $fqName) {
96 | $unqualifiedName = \ltrim(\substr($fqName, \strrpos($fqName, '\\')), '\\');
97 |
98 | $uselessAlias = false;
99 | if ($type === 'const') {
100 | // Do a case-sensitive comparison for constants.
101 | if ($unqualifiedName === $alias) {
102 | $uselessAlias = true;
103 | }
104 | } elseif (NamingConventions::isEqual($unqualifiedName, $alias)) {
105 | $uselessAlias = true;
106 | }
107 |
108 | if ($uselessAlias === false) {
109 | continue;
110 | }
111 |
112 | // Now check if this is actually used as an alias or just the actual name.
113 | foreach ($aliasPtrs as $asPtr => $aliasPtr) {
114 | if ($tokens[$aliasPtr]['content'] !== $alias) {
115 | continue;
116 | }
117 |
118 | // Make sure this is really the right one.
119 | $prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($asPtr - 1), null, true);
120 | if ($tokens[$prev]['code'] !== \T_STRING
121 | || $tokens[$prev]['content'] !== $unqualifiedName
122 | ) {
123 | continue;
124 | }
125 |
126 | $error = 'Useless alias "%s" found for import of "%s"';
127 | $code = 'Found';
128 | $data = [$alias, $fqName];
129 |
130 | // Okay, so this is the one which should be flagged.
131 | $hasComments = $phpcsFile->findNext(Tokens::$commentTokens, ($prev + 1), $aliasPtr);
132 | if ($hasComments !== false) {
133 | // Don't auto-fix if there are comments.
134 | $phpcsFile->addError($error, $aliasPtr, $code, $data);
135 | break;
136 | }
137 |
138 | $fix = $phpcsFile->addFixableError($error, $aliasPtr, $code, $data);
139 |
140 | if ($fix === true) {
141 | $phpcsFile->fixer->beginChangeset();
142 |
143 | for ($i = ($prev + 1); $i <= $aliasPtr; $i++) {
144 | $phpcsFile->fixer->replaceToken($i, '');
145 | }
146 |
147 | $phpcsFile->fixer->endChangeset();
148 | }
149 |
150 | break;
151 | }
152 | }
153 | }
154 | }
155 | }
156 |
--------------------------------------------------------------------------------
/Universal/Sniffs/WhiteSpace/AnonClassKeywordSpacingSniff.php:
--------------------------------------------------------------------------------
1 |
41 | */
42 | public function register()
43 | {
44 | return [\T_ANON_CLASS];
45 | }
46 |
47 | /**
48 | * Processes this test, when one of its tokens is encountered.
49 | *
50 | * @since 1.0.0
51 | *
52 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
53 | * @param int $stackPtr The position of the current token
54 | * in the stack passed in $tokens.
55 | *
56 | * @return void
57 | */
58 | public function process(File $phpcsFile, $stackPtr)
59 | {
60 | $tokens = $phpcsFile->getTokens();
61 | $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
62 | if ($nextNonEmpty === false || $tokens[$nextNonEmpty]['code'] !== \T_OPEN_PARENTHESIS) {
63 | // No parentheses, nothing to do.
64 | return;
65 | }
66 |
67 | SpacesFixer::checkAndFix(
68 | $phpcsFile,
69 | $stackPtr,
70 | $nextNonEmpty,
71 | (int) $this->spacing,
72 | 'There must be %1$s between the class keyword and the open parenthesis for an anonymous class. Found: %2$s',
73 | 'Incorrect',
74 | 'error',
75 | 0,
76 | 'Anon class: space between keyword and open parenthesis'
77 | );
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/Universal/Sniffs/WhiteSpace/DisallowInlineTabsSniff.php:
--------------------------------------------------------------------------------
1 |
32 | *
33 | *
34 | *
35 | * The PHPCS native `Generic.Whitespace.DisallowTabIndent` sniff oversteps its reach and silently
36 | * does mid-line tab to space replacements as well.
37 | * However, the sister-sniff `Generic.Whitespace.DisallowSpaceIndent` leaves mid-line tabs/spaces alone.
38 | * This sniff fills that gap.
39 | *
40 | * @since 1.0.0
41 | */
42 | final class DisallowInlineTabsSniff implements Sniff
43 | {
44 |
45 | /**
46 | * The --tab-width CLI value that is being used.
47 | *
48 | * @since 1.0.0
49 | *
50 | * @var int
51 | */
52 | private $tabWidth;
53 |
54 | /**
55 | * Tokens to check for mid-line tabs.
56 | *
57 | * @since 1.0.0
58 | *
59 | * @var array
60 | */
61 | private $find = [
62 | \T_WHITESPACE => true,
63 | \T_DOC_COMMENT_WHITESPACE => true,
64 | \T_DOC_COMMENT_STRING => true,
65 | \T_COMMENT => true,
66 | \T_START_HEREDOC => true,
67 | \T_START_NOWDOC => true,
68 | \T_YIELD_FROM => true,
69 | ];
70 |
71 | /**
72 | * Registers the tokens that this sniff wants to listen for.
73 | *
74 | * @since 1.0.0
75 | *
76 | * @return array
77 | */
78 | public function register()
79 | {
80 | return Collections::phpOpenTags();
81 | }
82 |
83 | /**
84 | * Processes this test, when one of its tokens is encountered.
85 | *
86 | * @since 1.0.0
87 | *
88 | * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
89 | * @param int $stackPtr The position of the current token
90 | * in the stack passed in $tokens.
91 | *
92 | * @return int Integer stack pointer to skip the rest of the file.
93 | */
94 | public function process(File $phpcsFile, $stackPtr)
95 | {
96 | if (isset($this->tabWidth) === false) {
97 | $this->tabWidth = (int) Helper::getTabWidth($phpcsFile);
98 | }
99 |
100 | if (\defined('PHP_CODESNIFFER_IN_TESTS')) {
101 | $this->tabWidth = (int) Helper::getCommandLineData($phpcsFile, 'tabWidth');
102 | }
103 |
104 | $tokens = $phpcsFile->getTokens();
105 | $dummy = new DummyTokenizer('', $phpcsFile->config);
106 |
107 | for ($i = 0; $i < $phpcsFile->numTokens; $i++) {
108 | // Skip all non-target tokens and skip whitespace at the start of a new line.
109 | if (isset($this->find[$tokens[$i]['code']]) === false
110 | || (($tokens[$i]['code'] === \T_WHITESPACE
111 | || $tokens[$i]['code'] === \T_DOC_COMMENT_WHITESPACE)
112 | && $tokens[$i]['column'] === 1)
113 | ) {
114 | continue;
115 | }
116 |
117 | // If tabs haven't been converted to spaces by the tokenizer, do so now.
118 | $token = $tokens[$i];
119 | if (isset($token['orig_content']) === false) {
120 | if ($token['content'] === '' || \strpos($token['content'], "\t") === false) {
121 | // If there are no tabs, we can continue, no matter what.
122 | continue;
123 | }
124 |
125 | $dummy->replaceTabsInToken($token);
126 | }
127 |
128 | /*
129 | * Tokens only have the 'orig_content' key if they contain tabs,
130 | * so from here on out, we **know** there will be tabs in the content.
131 | */
132 | $origContent = $token['orig_content'];
133 | $commentOnly = '';
134 |
135 | $multiLineComment = false;
136 | if (($tokens[$i]['code'] === \T_COMMENT
137 | || isset(Tokens::$phpcsCommentTokens[$tokens[$i]['code']]))
138 | && $tokens[$i]['column'] === 1
139 | && ($tokens[($i - 1)]['code'] === \T_COMMENT
140 | || isset(Tokens::$phpcsCommentTokens[$tokens[($i - 1)]['code']]))
141 | ) {
142 | $multiLineComment = true;
143 | }
144 |
145 | if ($multiLineComment === true) {
146 | // This is the subsequent line of a multi-line comment. Account for indentation.
147 | $commentOnly = \ltrim($origContent);
148 | if ($commentOnly === '' || \strpos($commentOnly, "\t") === false) {
149 | continue;
150 | }
151 | }
152 |
153 | $fix = $phpcsFile->addFixableError(
154 | 'Spaces must be used for mid-line alignment; tabs are not allowed',
155 | $i,
156 | 'NonIndentTabsUsed'
157 | );
158 |
159 | if ($fix === false) {
160 | continue;
161 | }
162 |
163 | $indent = '';
164 | if ($multiLineComment === true) {
165 | // Take the original indent (tabs/spaces) and combine with the tab-replaced comment content.
166 | $indent = \str_replace($commentOnly, '', $origContent);
167 | $token['content'] = \ltrim($token['content']);
168 | }
169 |
170 | $phpcsFile->fixer->replaceToken($i, $indent . $token['content']);
171 | }
172 |
173 | // Scanned the whole file in one go. Don't scan this file again.
174 | return $phpcsFile->numTokens;
175 | }
176 | }
177 |
--------------------------------------------------------------------------------
/Universal/ruleset.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | A collection of universal sniffs. This standard is not designed to be used to check code. Include individual sniffs from this standard in a custom ruleset instead.
5 |
6 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name" : "phpcsstandards/phpcsextra",
3 | "description" : "A collection of sniffs and standards for use with PHP_CodeSniffer.",
4 | "type" : "phpcodesniffer-standard",
5 | "keywords" : [ "phpcs", "phpcbf", "standards", "static analysis", "php_codesniffer", "phpcodesniffer-standard" ],
6 | "license" : "LGPL-3.0-or-later",
7 | "authors" : [
8 | {
9 | "name" : "Juliette Reinders Folmer",
10 | "role" : "lead",
11 | "homepage" : "https://github.com/jrfnl"
12 | },
13 | {
14 | "name" : "Contributors",
15 | "homepage" : "https://github.com/PHPCSStandards/PHPCSExtra/graphs/contributors"
16 | }
17 | ],
18 | "support" : {
19 | "issues" : "https://github.com/PHPCSStandards/PHPCSExtra/issues",
20 | "source" : "https://github.com/PHPCSStandards/PHPCSExtra",
21 | "security": "https://github.com/PHPCSStandards/PHPCSExtra/security/policy"
22 | },
23 | "require" : {
24 | "php" : ">=5.4",
25 | "squizlabs/php_codesniffer" : "^3.12.1",
26 | "phpcsstandards/phpcsutils" : "^1.0.9"
27 | },
28 | "require-dev" : {
29 | "php-parallel-lint/php-parallel-lint": "^1.4.0",
30 | "php-parallel-lint/php-console-highlighter": "^1.0",
31 | "phpcsstandards/phpcsdevcs": "^1.1.6",
32 | "phpcsstandards/phpcsdevtools": "^1.2.1",
33 | "phpunit/phpunit": "^4.5 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0"
34 | },
35 | "extra": {
36 | "branch-alias": {
37 | "dev-stable": "1.x-dev",
38 | "dev-develop": "1.x-dev"
39 | }
40 | },
41 | "scripts" : {
42 | "lint": [
43 | "@php ./vendor/php-parallel-lint/php-parallel-lint/parallel-lint . --show-deprecated -e php --exclude vendor --exclude .git"
44 | ],
45 | "checkcs": [
46 | "@php ./vendor/squizlabs/php_codesniffer/bin/phpcs"
47 | ],
48 | "fixcs": [
49 | "@php ./vendor/squizlabs/php_codesniffer/bin/phpcbf"
50 | ],
51 | "check-complete": [
52 | "@php ./vendor/phpcsstandards/phpcsdevtools/bin/phpcs-check-feature-completeness ./Modernize ./NormalizedArrays ./Universal"
53 | ],
54 | "test": [
55 | "@php ./vendor/phpunit/phpunit/phpunit --filter PHPCSExtra --no-coverage ./vendor/squizlabs/php_codesniffer/tests/AllTests.php"
56 | ],
57 | "coverage": [
58 | "@php ./vendor/phpunit/phpunit/phpunit --filter PHPCSExtra ./vendor/squizlabs/php_codesniffer/tests/AllTests.php"
59 | ],
60 | "coverage-local": [
61 | "@php ./vendor/phpunit/phpunit/phpunit --filter PHPCSExtra ./vendor/squizlabs/php_codesniffer/tests/AllTests.php --coverage-html ./build/coverage-html"
62 | ]
63 | },
64 | "config": {
65 | "allow-plugins": {
66 | "dealerdirect/phpcodesniffer-composer-installer": true
67 | },
68 | "lock": false
69 | }
70 | }
71 |
--------------------------------------------------------------------------------