Skip to content

Commit e0d333f

Browse files
jrfnlgrogy
authored andcommitted
PHP 8.0: handle changed tokenization of namespaced names
> Namespaced names are now represented using the `T_NAME_QUALIFIED` (`Foo\Bar`), `T_NAME_FULLY_QUALIFIED` (`\Foo\Bar`) and `T_NAME_RELATIVE` (`namespace\Foo\Bar`) tokens. `T_NS_SEPARATOR` is only used for standalone namespace separators, and only syntactially valid in conjunction with group use declarations. Ref: https://www.php.net/manual/en/migration80.incompatible.php#migration80.incompatible.tokenizer This commit: * Adds recognition for the new PHP 8.0 tokens for namespaced names and highlights these as `TOKEN_DEFAULT`, same as a non-namespaced `T_STRING` name. * Adjusts the new unit tests to document the different highlighting between PHP < 8.0 and PHP 8.0+. I've chosen not to make force the same highlighting across versions, but to let the highlighting respect the different tokenization across PHP versions.
1 parent 5a6ff24 commit e0d333f

File tree

2 files changed

+97
-61
lines changed

2 files changed

+97
-61
lines changed

src/Highlighter.php

Lines changed: 55 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -118,46 +118,8 @@ private function tokenize($source)
118118

119119
foreach ($tokens as $token) {
120120
if (is_array($token)) {
121-
switch ($token[0]) {
122-
case T_WHITESPACE:
123-
break;
124-
125-
case T_OPEN_TAG:
126-
case T_OPEN_TAG_WITH_ECHO:
127-
case T_CLOSE_TAG:
128-
case T_STRING:
129-
case T_VARIABLE:
130-
131-
// Constants
132-
case T_DIR:
133-
case T_FILE:
134-
case T_METHOD_C:
135-
case T_DNUMBER:
136-
case T_LNUMBER:
137-
case T_NS_C:
138-
case T_LINE:
139-
case T_CLASS_C:
140-
case T_FUNC_C:
141-
case T_TRAIT_C:
142-
$newType = self::TOKEN_DEFAULT;
143-
break;
144-
145-
case T_COMMENT:
146-
case T_DOC_COMMENT:
147-
$newType = self::TOKEN_COMMENT;
148-
break;
149-
150-
case T_ENCAPSED_AND_WHITESPACE:
151-
case T_CONSTANT_ENCAPSED_STRING:
152-
$newType = self::TOKEN_STRING;
153-
break;
154-
155-
case T_INLINE_HTML:
156-
$newType = self::TOKEN_HTML;
157-
break;
158-
159-
default:
160-
$newType = self::TOKEN_KEYWORD;
121+
if ($token[0] !== T_WHITESPACE) {
122+
$newType = $this->getTokenType($token);
161123
}
162124
} else {
163125
$newType = $token === '"' ? self::TOKEN_STRING : self::TOKEN_KEYWORD;
@@ -183,6 +145,59 @@ private function tokenize($source)
183145
return $output;
184146
}
185147

148+
/**
149+
* @param array $arrayToken
150+
* @return string
151+
*/
152+
private function getTokenType($arrayToken)
153+
{
154+
switch ($arrayToken[0]) {
155+
case T_OPEN_TAG:
156+
case T_OPEN_TAG_WITH_ECHO:
157+
case T_CLOSE_TAG:
158+
case T_STRING:
159+
case T_VARIABLE:
160+
161+
// Constants
162+
case T_DIR:
163+
case T_FILE:
164+
case T_METHOD_C:
165+
case T_DNUMBER:
166+
case T_LNUMBER:
167+
case T_NS_C:
168+
case T_LINE:
169+
case T_CLASS_C:
170+
case T_FUNC_C:
171+
case T_TRAIT_C:
172+
return self::TOKEN_DEFAULT;
173+
174+
case T_COMMENT:
175+
case T_DOC_COMMENT:
176+
return self::TOKEN_COMMENT;
177+
178+
case T_ENCAPSED_AND_WHITESPACE:
179+
case T_CONSTANT_ENCAPSED_STRING:
180+
return self::TOKEN_STRING;
181+
182+
case T_INLINE_HTML:
183+
return self::TOKEN_HTML;
184+
}
185+
186+
// Handle PHP >= 8.0 namespaced name tokens.
187+
// https://www.php.net/manual/en/migration80.incompatible.php#migration80.incompatible.tokenizer
188+
if (defined('T_NAME_QUALIFIED') && $arrayToken[0] === T_NAME_QUALIFIED) {
189+
return self::TOKEN_DEFAULT;
190+
}
191+
if (defined('T_NAME_FULLY_QUALIFIED') && $arrayToken[0] === T_NAME_FULLY_QUALIFIED) {
192+
return self::TOKEN_DEFAULT;
193+
}
194+
if (defined('T_NAME_RELATIVE') && $arrayToken[0] === T_NAME_RELATIVE) {
195+
return self::TOKEN_DEFAULT;
196+
}
197+
198+
return self::TOKEN_KEYWORD;
199+
}
200+
186201
/**
187202
* @param array $tokens
188203
* @return array

tests/HighlighterTest.php

Lines changed: 42 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -297,46 +297,67 @@ public function testFunctionCall()
297297

298298
public function testFQNFunctionCall()
299299
{
300-
$this->compare(
301-
<<<'EOL'
300+
$original = <<<'EOL'
302301
<?php
303302
echo \My\Package\functionName();
304-
EOL
305-
,
306-
<<<'EOL'
303+
EOL;
304+
305+
if (PHP_VERSION_ID < 80000) {
306+
$expected = <<<'EOL'
307307
<token_default><?php</token_default>
308308
<token_keyword>echo \</token_keyword><token_default>My</token_default><token_keyword>\</token_keyword><token_default>Package</token_default><token_keyword>\</token_keyword><token_default>functionName</token_default><token_keyword>();</token_keyword>
309-
EOL
310-
);
309+
EOL;
310+
} else {
311+
$expected = <<<'EOL'
312+
<token_default><?php</token_default>
313+
<token_keyword>echo </token_keyword><token_default>\My\Package\functionName</token_default><token_keyword>();</token_keyword>
314+
EOL;
315+
}
316+
317+
$this->compare($original, $expected);
311318
}
312319

313320
public function testNamespaceRelativeFunctionCall()
314321
{
315-
$this->compare(
316-
<<<'EOL'
322+
$original = <<<'EOL'
317323
<?php
318324
echo namespace\functionName();
319-
EOL
320-
,
321-
<<<'EOL'
325+
EOL;
326+
327+
if (PHP_VERSION_ID < 80000) {
328+
$expected = <<<'EOL'
322329
<token_default><?php</token_default>
323330
<token_keyword>echo namespace\</token_keyword><token_default>functionName</token_default><token_keyword>();</token_keyword>
324-
EOL
325-
);
331+
EOL;
332+
} else {
333+
$expected = <<<'EOL'
334+
<token_default><?php</token_default>
335+
<token_keyword>echo </token_keyword><token_default>namespace\functionName</token_default><token_keyword>();</token_keyword>
336+
EOL;
337+
}
338+
339+
$this->compare($original, $expected);
326340
}
327341

328342
public function testQualifiedFunctionCall()
329343
{
330-
$this->compare(
331-
<<<'EOL'
344+
$original = <<<'EOL'
332345
<?php
333346
echo Package\functionName();
334-
EOL
335-
,
336-
<<<'EOL'
347+
EOL;
348+
349+
if (PHP_VERSION_ID < 80000) {
350+
$expected = <<<'EOL'
337351
<token_default><?php</token_default>
338352
<token_keyword>echo </token_keyword><token_default>Package</token_default><token_keyword>\</token_keyword><token_default>functionName</token_default><token_keyword>();</token_keyword>
339-
EOL
340-
);
353+
EOL;
354+
} else {
355+
$expected = <<<'EOL'
356+
<token_default><?php</token_default>
357+
<token_keyword>echo </token_keyword><token_default>Package\functionName</token_default><token_keyword>();</token_keyword>
358+
EOL;
359+
}
360+
361+
$this->compare($original, $expected);
341362
}
342363
}

0 commit comments

Comments
 (0)