Skip to content

Commit 4444dc9

Browse files
committed
UseStatements::splitImportUseStatement(): compatibility with the PHP 8 identifier name tokenization
As the PHP 8 identifier name tokens will not always exist in all supported PHP/PHPCS versions, the token code comparison needs to be switched to a token type comparison to prevent "unknown constant" errors. Additionally, the `UseStatements::splitImportUseStatement()` method uses the last part of an imported name as the alias/array index key if no alias is given. For the PHP 5/7 tokenization, this was handled by overwriting the`$alias` while the method was still going through the various parts of the name until it found the end. For the PHP 8 tokenization, however, the various parts of the name are now all squashed together into one token, so we need to extract the last part of the name from the (fully) qualified name to get to the "alias". As an extra issue to deal with, PHPCS re-tokenizes the next token after a `function`/`const` keyword to `T_STRING`. This is primarily intended for function/constant declarations, however, it affects import use statements for functions/constants as well if the `function`/`const` keyword is part of a group `use` statement. That means that, for all PHPCS versions prior to the PHPCS version in which this will be fixed (which is expected to be version 3.5.7 as part of upstream PR 3063), `T_STRING` tokens in a group use statement _may_ in fact be PHP 8.0 identifier name tokens and _may_ also need to be split to extract the last part of the name from the complete name to get the alias. The existing unit test cover all the above situations.
1 parent f4d902b commit 4444dc9

File tree

1 file changed

+44
-16
lines changed

1 file changed

+44
-16
lines changed

PHPCSUtils/Utils/UseStatements.php

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ public static function isTraitUse(File $phpcsFile, $stackPtr)
154154
* Handles single import, multi-import and group-import use statements.
155155
*
156156
* @since 1.0.0
157+
* @since 1.0.0-alpha4 Added support for PHP 8.0 identifier name tokenization.
157158
*
158159
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
159160
* @param int $stackPtr The position in the stack of the `T_USE` token.
@@ -229,7 +230,7 @@ public static function splitImportUseStatement(File $phpcsFile, $stackPtr)
229230
continue;
230231
}
231232

232-
$tokenCode = $tokens[$i]['code'];
233+
$tokenType = $tokens[$i]['type'];
233234

234235
/*
235236
* BC: Work round a tokenizer bug related to a parse error.
@@ -241,16 +242,16 @@ public static function splitImportUseStatement(File $phpcsFile, $stackPtr)
241242
*
242243
* Along the same lines, the `}` T_CLOSE_USE_GROUP would also be tokenized as T_STRING.
243244
*/
244-
if ($tokenCode === \T_STRING) {
245+
if ($tokenType === 'T_STRING') {
245246
if ($tokens[$i]['content'] === ';') {
246-
$tokenCode = \T_SEMICOLON;
247+
$tokenType = 'T_SEMICOLON';
247248
} elseif ($tokens[$i]['content'] === '}') {
248-
$tokenCode = \T_CLOSE_USE_GROUP;
249+
$tokenType = 'T_CLOSE_USE_GROUP';
249250
}
250251
}
251252

252-
switch ($tokenCode) {
253-
case \T_STRING:
253+
switch ($tokenType) {
254+
case 'T_STRING':
254255
// Only when either at the start of the statement or at the start of a new sub within a group.
255256
if ($start === true && $fixedType === false) {
256257
$content = \strtolower($tokens[$i]['content']);
@@ -276,23 +277,50 @@ public static function splitImportUseStatement(File $phpcsFile, $stackPtr)
276277
}
277278

278279
$alias = $tokens[$i]['content'];
280+
281+
/*
282+
* BC: work around PHPCS tokenizer issue in PHPCS < 3.5.7 where anything directly after
283+
* a `function` or `const` keyword would be retokenized to `T_STRING`, including the
284+
* PHP 8 identifier name tokens.
285+
*/
286+
$hasSlash = \strrpos($tokens[$i]['content'], '\\');
287+
if ($hasSlash !== false) {
288+
$alias = \substr($tokens[$i]['content'], ($hasSlash + 1));
289+
}
290+
291+
break;
292+
293+
case 'T_NAME_QUALIFIED':
294+
case 'T_NAME_FULLY_QUALIFIED': // This would be a parse error, but handle it anyway.
295+
// Only when either at the start of the statement or at the start of a new sub within a group.
296+
if ($start === true && $fixedType === false) {
297+
$type = 'name';
298+
}
299+
300+
$start = false;
301+
302+
if ($hasAlias === false) {
303+
$name .= $tokens[$i]['content'];
304+
}
305+
306+
$alias = \substr($tokens[$i]['content'], (\strrpos($tokens[$i]['content'], '\\') + 1));
279307
break;
280308

281-
case \T_AS:
309+
case 'T_AS':
282310
$hasAlias = true;
283311
break;
284312

285-
case \T_OPEN_USE_GROUP:
313+
case 'T_OPEN_USE_GROUP':
286314
$start = true;
287315
$useGroup = true;
288316
$baseName = $name;
289317
$name = '';
290318
break;
291319

292-
case \T_SEMICOLON:
293-
case \T_CLOSE_TAG:
294-
case \T_CLOSE_USE_GROUP:
295-
case \T_COMMA:
320+
case 'T_SEMICOLON':
321+
case 'T_CLOSE_TAG':
322+
case 'T_CLOSE_USE_GROUP':
323+
case 'T_COMMA':
296324
if ($name !== '') {
297325
if ($useGroup === true) {
298326
$statements[$type][$alias] = $baseName . $name;
@@ -301,7 +329,7 @@ public static function splitImportUseStatement(File $phpcsFile, $stackPtr)
301329
}
302330
}
303331

304-
if ($tokenCode !== \T_COMMA) {
332+
if ($tokenType !== 'T_COMMA') {
305333
break 2;
306334
}
307335

@@ -314,12 +342,12 @@ public static function splitImportUseStatement(File $phpcsFile, $stackPtr)
314342
}
315343
break;
316344

317-
case \T_NS_SEPARATOR:
345+
case 'T_NS_SEPARATOR':
318346
$name .= $tokens[$i]['content'];
319347
break;
320348

321-
case \T_FUNCTION:
322-
case \T_CONST:
349+
case 'T_FUNCTION':
350+
case 'T_CONST':
323351
/*
324352
* BC: Work around tokenizer bug in PHPCS < 3.4.1.
325353
*

0 commit comments

Comments
 (0)