Skip to content

Commit 7055739

Browse files
authored
Merge pull request #1020 from PHPCSStandards/phpcs-4.0/feature/3041-tokenizer-php-namespaced-name-tokenization
Tokenizer/PHP: namespaced names as single token, mirroring PHP 8.0+
2 parents f78bb64 + 121c58c commit 7055739

File tree

109 files changed

+1073
-1106
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

109 files changed

+1073
-1106
lines changed

src/Files/File.php

Lines changed: 14 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1415,7 +1415,10 @@ public function getMethodParameters($stackPtr)
14151415
}
14161416
break;
14171417
case T_STRING:
1418-
// This is a string, so it may be a type hint, but it could
1418+
case T_NAME_QUALIFIED:
1419+
case T_NAME_FULLY_QUALIFIED:
1420+
case T_NAME_RELATIVE:
1421+
// This is an identifier name, so it may be a type declaration, but it could
14191422
// also be a constant used as a default value.
14201423
$prevComma = false;
14211424
for ($t = $i; $t >= $opener; $t--) {
@@ -1673,17 +1676,15 @@ public function getMethodProperties($stackPtr)
16731676
$scopeOpener = $this->tokens[$stackPtr]['scope_opener'];
16741677
}
16751678

1676-
$valid = [
1677-
T_STRING => T_STRING,
1679+
$valid = Tokens::$nameTokens;
1680+
$valid += [
16781681
T_CALLABLE => T_CALLABLE,
16791682
T_SELF => T_SELF,
16801683
T_PARENT => T_PARENT,
16811684
T_STATIC => T_STATIC,
16821685
T_FALSE => T_FALSE,
16831686
T_TRUE => T_TRUE,
16841687
T_NULL => T_NULL,
1685-
T_NAMESPACE => T_NAMESPACE,
1686-
T_NS_SEPARATOR => T_NS_SEPARATOR,
16871688
T_TYPE_UNION => T_TYPE_UNION,
16881689
T_TYPE_INTERSECTION => T_TYPE_INTERSECTION,
16891690
T_TYPE_OPEN_PARENTHESIS => T_TYPE_OPEN_PARENTHESIS,
@@ -1876,16 +1877,14 @@ public function getMemberProperties($stackPtr)
18761877

18771878
if ($i < $stackPtr) {
18781879
// We've found a type.
1879-
$valid = [
1880-
T_STRING => T_STRING,
1880+
$valid = Tokens::$nameTokens;
1881+
$valid += [
18811882
T_CALLABLE => T_CALLABLE,
18821883
T_SELF => T_SELF,
18831884
T_PARENT => T_PARENT,
18841885
T_FALSE => T_FALSE,
18851886
T_TRUE => T_TRUE,
18861887
T_NULL => T_NULL,
1887-
T_NAMESPACE => T_NAMESPACE,
1888-
T_NS_SEPARATOR => T_NS_SEPARATOR,
18891888
T_TYPE_UNION => T_TYPE_UNION,
18901889
T_TYPE_INTERSECTION => T_TYPE_INTERSECTION,
18911890
T_TYPE_OPEN_PARENTHESIS => T_TYPE_OPEN_PARENTHESIS,
@@ -2086,12 +2085,10 @@ public function isReference($stackPtr)
20862085
return true;
20872086
} else {
20882087
$skip = Tokens::$emptyTokens;
2089-
$skip[] = T_NS_SEPARATOR;
2088+
$skip += Tokens::$nameTokens;
20902089
$skip[] = T_SELF;
20912090
$skip[] = T_PARENT;
20922091
$skip[] = T_STATIC;
2093-
$skip[] = T_STRING;
2094-
$skip[] = T_NAMESPACE;
20952092
$skip[] = T_DOUBLE_COLON;
20962093

20972094
$nextSignificantAfter = $this->findNext(
@@ -2781,11 +2778,8 @@ public function findExtendedClassName($stackPtr)
27812778
return false;
27822779
}
27832780

2784-
$find = [
2785-
T_NS_SEPARATOR,
2786-
T_STRING,
2787-
T_WHITESPACE,
2788-
];
2781+
$find = Tokens::$nameTokens;
2782+
$find[] = T_WHITESPACE;
27892783

27902784
$end = $this->findNext($find, ($extendsIndex + 1), ($classOpenerIndex + 1), true);
27912785
$name = $this->getTokensAsString(($extendsIndex + 1), ($end - $extendsIndex - 1));
@@ -2833,12 +2827,9 @@ public function findImplementedInterfaceNames($stackPtr)
28332827
return false;
28342828
}
28352829

2836-
$find = [
2837-
T_NS_SEPARATOR,
2838-
T_STRING,
2839-
T_WHITESPACE,
2840-
T_COMMA,
2841-
];
2830+
$find = Tokens::$nameTokens;
2831+
$find[] = T_WHITESPACE;
2832+
$find[] = T_COMMA;
28422833

28432834
$end = $this->findNext($find, ($implementsIndex + 1), ($classOpenerIndex + 1), true);
28442835
$name = $this->getTokensAsString(($implementsIndex + 1), ($end - $implementsIndex - 1));

src/Standards/Generic/Sniffs/Classes/DuplicateClassNameSniff.php

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -63,24 +63,16 @@ public function process(File $phpcsFile, $stackPtr)
6363
// Keep track of what namespace we are in.
6464
if ($tokens[$stackPtr]['code'] === T_NAMESPACE) {
6565
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
66-
if ($nextNonEmpty !== false
67-
// Ignore namespace keyword used as operator.
68-
&& $tokens[$nextNonEmpty]['code'] !== T_NS_SEPARATOR
69-
) {
70-
$namespace = '';
71-
for ($i = $nextNonEmpty; $i < $phpcsFile->numTokens; $i++) {
72-
if (isset(Tokens::$emptyTokens[$tokens[$i]['code']]) === true) {
73-
continue;
74-
}
75-
76-
if ($tokens[$i]['code'] !== T_STRING && $tokens[$i]['code'] !== T_NS_SEPARATOR) {
77-
break;
78-
}
79-
80-
$namespace .= $tokens[$i]['content'];
66+
if ($nextNonEmpty !== false) {
67+
if ($tokens[$nextNonEmpty]['code'] === T_STRING
68+
|| $tokens[$nextNonEmpty]['code'] === T_NAME_QUALIFIED
69+
) {
70+
$namespace = $tokens[$nextNonEmpty]['content'];
71+
} else if ($tokens[$nextNonEmpty]['code'] === T_OPEN_CURLY_BRACKET) {
72+
$namespace = '';
8173
}
8274

83-
$stackPtr = $i;
75+
$stackPtr = $nextNonEmpty;
8476
}
8577
} else {
8678
$name = $phpcsFile->getDeclarationName($stackPtr);

src/Standards/Generic/Sniffs/CodeAnalysis/ForLoopWithTestFunctionCallSniff.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ public function process(File $phpcsFile, $stackPtr)
8080
continue;
8181
} else if ($position > 1) {
8282
break;
83-
} else if ($code !== T_VARIABLE && $code !== T_STRING) {
83+
} else if ($code !== T_VARIABLE && isset(Tokens::$nameTokens[$code]) === false) {
8484
continue;
8585
}
8686

src/Standards/Generic/Sniffs/ControlStructures/DisallowYodaConditionsSniff.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ public function process(File $phpcsFile, $stackPtr)
8888
);
8989

9090
if ($beforeOpeningParenthesisIndex === false || $tokens[$beforeOpeningParenthesisIndex]['code'] !== T_ARRAY) {
91-
if ($tokens[$beforeOpeningParenthesisIndex]['code'] === T_STRING) {
91+
if (isset(Tokens::$nameTokens[$tokens[$beforeOpeningParenthesisIndex]['code']]) === true) {
9292
return;
9393
}
9494

src/Standards/Generic/Sniffs/Functions/FunctionCallArgumentSpacingSniff.php

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,17 @@ class FunctionCallArgumentSpacingSniff implements Sniff
2424
*/
2525
public function register()
2626
{
27-
return[
28-
T_STRING,
29-
T_ISSET,
30-
T_UNSET,
31-
T_SELF,
32-
T_STATIC,
33-
T_PARENT,
34-
T_VARIABLE,
35-
T_CLOSE_CURLY_BRACKET,
36-
T_CLOSE_PARENTHESIS,
37-
];
27+
$targets = Tokens::$nameTokens;
28+
$targets[] = T_ISSET;
29+
$targets[] = T_UNSET;
30+
$targets[] = T_SELF;
31+
$targets[] = T_STATIC;
32+
$targets[] = T_PARENT;
33+
$targets[] = T_VARIABLE;
34+
$targets[] = T_CLOSE_CURLY_BRACKET;
35+
$targets[] = T_CLOSE_PARENTHESIS;
36+
37+
return $targets;
3838

3939
}//end register()
4040

src/Standards/Generic/Sniffs/NamingConventions/UpperCaseConstantNameSniff.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ public function register()
2626
{
2727
return [
2828
T_STRING,
29+
T_NAME_FULLY_QUALIFIED,
2930
T_CONST,
3031
];
3132

@@ -83,7 +84,7 @@ public function process(File $phpcsFile, $stackPtr)
8384
}//end if
8485

8586
// Only interested in define statements now.
86-
if (strtolower($tokens[$stackPtr]['content']) !== 'define') {
87+
if (strtolower(ltrim($tokens[$stackPtr]['content'], '\\')) !== 'define') {
8788
return;
8889
}
8990

src/Standards/Generic/Sniffs/PHP/ForbiddenFunctionsSniff.php

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,10 @@ public function register()
7070
$this->forbiddenFunctionNames[$i] = '/'.$name.'/i';
7171
}
7272

73-
return [T_STRING];
73+
return [
74+
T_STRING,
75+
T_NAME_FULLY_QUALIFIED,
76+
];
7477
}
7578

7679
// If we are not pattern matching, we need to work out what
@@ -102,7 +105,10 @@ public function register()
102105
$this->forbiddenFunctionNames = array_map('strtolower', $this->forbiddenFunctionNames);
103106
$this->forbiddenFunctions = array_combine($this->forbiddenFunctionNames, $this->forbiddenFunctions);
104107

105-
return array_unique($register);
108+
$targets = array_unique($register);
109+
$targets[] = T_NAME_FULLY_QUALIFIED;
110+
111+
return $targets;
106112

107113
}//end register()
108114

@@ -132,22 +138,11 @@ public function process(File $phpcsFile, $stackPtr)
132138
T_AS => true,
133139
T_NEW => true,
134140
T_INSTEADOF => true,
135-
T_NS_SEPARATOR => true,
136141
T_IMPLEMENTS => true,
137142
];
138143

139144
$prevToken = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
140145

141-
// If function call is directly preceded by a NS_SEPARATOR it points to the
142-
// global namespace, so we should still catch it.
143-
if ($tokens[$prevToken]['code'] === T_NS_SEPARATOR) {
144-
$prevToken = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($prevToken - 1), null, true);
145-
if ($tokens[$prevToken]['code'] === T_STRING) {
146-
// Not in the global namespace.
147-
return;
148-
}
149-
}
150-
151146
if (isset($ignore[$tokens[$prevToken]['code']]) === true) {
152147
// Not a call to a PHP function.
153148
return;
@@ -159,7 +154,9 @@ public function process(File $phpcsFile, $stackPtr)
159154
return;
160155
}
161156

162-
if ($tokens[$stackPtr]['code'] === T_STRING && $tokens[$nextToken]['code'] !== T_OPEN_PARENTHESIS) {
157+
if (($tokens[$stackPtr]['code'] === T_STRING || $tokens[$stackPtr]['code'] === T_NAME_FULLY_QUALIFIED)
158+
&& $tokens[$nextToken]['code'] !== T_OPEN_PARENTHESIS
159+
) {
163160
// Not a call to a PHP function.
164161
return;
165162
}
@@ -170,8 +167,11 @@ public function process(File $phpcsFile, $stackPtr)
170167
}
171168

172169
$function = strtolower($tokens[$stackPtr]['content']);
173-
$pattern = null;
170+
if ($tokens[$stackPtr]['code'] === T_NAME_FULLY_QUALIFIED) {
171+
$function = ltrim($function, '\\');
172+
}
174173

174+
$pattern = null;
175175
if ($this->patternMatch === true) {
176176
$count = 0;
177177
$pattern = preg_replace(
@@ -194,7 +194,7 @@ public function process(File $phpcsFile, $stackPtr)
194194
}
195195
}//end if
196196

197-
$this->addError($phpcsFile, $stackPtr, $tokens[$stackPtr]['content'], $pattern);
197+
$this->addError($phpcsFile, $stackPtr, $function, $pattern);
198198

199199
}//end process()
200200

src/Standards/Generic/Sniffs/PHP/SAPIUsageSniff.php

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,10 @@ class SAPIUsageSniff implements Sniff
2323
*/
2424
public function register()
2525
{
26-
return [T_STRING];
26+
return [
27+
T_STRING,
28+
T_NAME_FULLY_QUALIFIED,
29+
];
2730

2831
}//end register()
2932

@@ -41,6 +44,11 @@ public function process(File $phpcsFile, $stackPtr)
4144
{
4245
$tokens = $phpcsFile->getTokens();
4346

47+
$function = strtolower(ltrim($tokens[$stackPtr]['content'], '\\'));
48+
if ($function !== 'php_sapi_name') {
49+
return;
50+
}
51+
4452
$ignore = [
4553
T_DOUBLE_COLON => true,
4654
T_OBJECT_OPERATOR => true,
@@ -55,11 +63,8 @@ public function process(File $phpcsFile, $stackPtr)
5563
return;
5664
}
5765

58-
$function = strtolower($tokens[$stackPtr]['content']);
59-
if ($function === 'php_sapi_name') {
60-
$error = 'Use the PHP_SAPI constant instead of calling php_sapi_name()';
61-
$phpcsFile->addError($error, $stackPtr, 'FunctionFound');
62-
}
66+
$error = 'Use the PHP_SAPI constant instead of calling php_sapi_name()';
67+
$phpcsFile->addError($error, $stackPtr, 'FunctionFound');
6368

6469
}//end process()
6570

src/Standards/Generic/Sniffs/WhiteSpace/IncrementDecrementSpacingSniff.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ public function process(File $phpcsFile, $stackPtr)
5353
// Is this a pre-increment/decrement ?
5454
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
5555
if ($nextNonEmpty !== false
56-
&& ($tokens[$nextNonEmpty]['code'] === T_VARIABLE || $tokens[$nextNonEmpty]['code'] === T_STRING)
56+
&& ($tokens[$nextNonEmpty]['code'] === T_VARIABLE
57+
|| isset(Tokens::$nameTokens[$tokens[$nextNonEmpty]['code']]) === true)
5758
) {
5859
if ($nextNonEmpty === ($stackPtr + 1)) {
5960
$phpcsFile->recordMetric($stackPtr, 'Spacing between in/decrementor and variable', 0);
@@ -106,7 +107,7 @@ public function process(File $phpcsFile, $stackPtr)
106107
$prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
107108
if ($prevNonEmpty !== false
108109
&& ($tokens[$prevNonEmpty]['code'] === T_VARIABLE
109-
|| $tokens[$prevNonEmpty]['code'] === T_STRING
110+
|| isset(Tokens::$nameTokens[$tokens[$prevNonEmpty]['code']]) === true
110111
|| $tokens[$prevNonEmpty]['code'] === T_CLOSE_SQUARE_BRACKET)
111112
) {
112113
if ($prevNonEmpty === ($stackPtr - 1)) {

src/Standards/Generic/Sniffs/WhiteSpace/LanguageConstructSpacingSniff.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,9 @@ public function process(File $phpcsFile, $stackPtr)
7373
$content = $tokens[$stackPtr]['content'];
7474
if ($tokens[$stackPtr]['code'] === T_NAMESPACE) {
7575
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
76-
if ($nextNonEmpty !== false && $tokens[$nextNonEmpty]['code'] === T_NS_SEPARATOR) {
76+
if ($nextNonEmpty !== false && $tokens[$nextNonEmpty]['code'] === T_NAME_FULLY_QUALIFIED) {
7777
// Namespace keyword used as operator, not as the language construct.
78+
// Note: in PHP >= 8 namespaced names no longer allow for whitespace/comments between the parts (parse error).
7879
return;
7980
}
8081
}

0 commit comments

Comments
 (0)