Skip to content

Commit f4115be

Browse files
committed
PHP 8.1: Added T_ENUM_CASE
1 parent 498a939 commit f4115be

File tree

5 files changed

+264
-0
lines changed

5 files changed

+264
-0
lines changed

package.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,8 @@ http://pear.php.net/dtd/package-2.0.xsd">
164164
<file baseinstalldir="" name="DefaultKeywordTest.php" role="test" />
165165
<file baseinstalldir="" name="DoubleArrowTest.inc" role="test" />
166166
<file baseinstalldir="" name="DoubleArrowTest.php" role="test" />
167+
<file baseinstalldir="" name="EnumCaseTest.inc" role="test" />
168+
<file baseinstalldir="" name="EnumCaseTest.php" role="test" />
167169
<file baseinstalldir="" name="FinallyTest.inc" role="test" />
168170
<file baseinstalldir="" name="FinallyTest.php" role="test" />
169171
<file baseinstalldir="" name="GotoLabelTest.inc" role="test" />
@@ -2127,6 +2129,8 @@ http://pear.php.net/dtd/package-2.0.xsd">
21272129
<install as="CodeSniffer/Core/Tokenizer/DefaultKeywordTest.inc" name="tests/Core/Tokenizer/DefaultKeywordTest.inc" />
21282130
<install as="CodeSniffer/Core/Tokenizer/DoubleArrowTest.php" name="tests/Core/Tokenizer/DoubleArrowTest.php" />
21292131
<install as="CodeSniffer/Core/Tokenizer/DoubleArrowTest.inc" name="tests/Core/Tokenizer/DoubleArrowTest.inc" />
2132+
<install as="CodeSniffer/Core/Tokenizer/EnumCaseTest.php" name="tests/Core/Tokenizer/EnumCaseTest.php" />
2133+
<install as="CodeSniffer/Core/Tokenizer/EnumCaseTest.inc" name="tests/Core/Tokenizer/EnumCaseTest.inc" />
21302134
<install as="CodeSniffer/Core/Tokenizer/FinallyTest.php" name="tests/Core/Tokenizer/FinallyTest.php" />
21312135
<install as="CodeSniffer/Core/Tokenizer/FinallyTest.inc" name="tests/Core/Tokenizer/FinallyTest.inc" />
21322136
<install as="CodeSniffer/Core/Tokenizer/GotoLabelTest.php" name="tests/Core/Tokenizer/GotoLabelTest.php" />
@@ -2225,6 +2229,8 @@ http://pear.php.net/dtd/package-2.0.xsd">
22252229
<install as="CodeSniffer/Core/Tokenizer/DefaultKeywordTest.inc" name="tests/Core/Tokenizer/DefaultKeywordTest.inc" />
22262230
<install as="CodeSniffer/Core/Tokenizer/DoubleArrowTest.php" name="tests/Core/Tokenizer/DoubleArrowTest.php" />
22272231
<install as="CodeSniffer/Core/Tokenizer/DoubleArrowTest.inc" name="tests/Core/Tokenizer/DoubleArrowTest.inc" />
2232+
<install as="CodeSniffer/Core/Tokenizer/EnumCaseTest.php" name="tests/Core/Tokenizer/EnumCaseTest.php" />
2233+
<install as="CodeSniffer/Core/Tokenizer/EnumCaseTest.inc" name="tests/Core/Tokenizer/EnumCaseTest.inc" />
22282234
<install as="CodeSniffer/Core/Tokenizer/FinallyTest.php" name="tests/Core/Tokenizer/FinallyTest.php" />
22292235
<install as="CodeSniffer/Core/Tokenizer/FinallyTest.inc" name="tests/Core/Tokenizer/FinallyTest.inc" />
22302236
<install as="CodeSniffer/Core/Tokenizer/GotoLabelTest.php" name="tests/Core/Tokenizer/GotoLabelTest.php" />

src/Tokenizers/PHP.php

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,7 @@ class PHP extends Tokenizer
347347
T_ENDSWITCH => 9,
348348
T_ENDWHILE => 8,
349349
T_ENUM => 4,
350+
T_ENUM_CASE => 4,
350351
T_EVAL => 4,
351352
T_EXTENDS => 7,
352353
T_FILE => 8,
@@ -982,6 +983,9 @@ protected function tokenize($string)
982983
&& is_array($tokens[$i]) === true
983984
&& $tokens[$i][0] === T_STRING
984985
) {
986+
// Modify $tokens directly so we can use it later when converting enum "case".
987+
$tokens[$stackPtr][0] = T_ENUM;
988+
985989
$newToken = [];
986990
$newToken['code'] = T_ENUM;
987991
$newToken['type'] = 'T_ENUM';
@@ -997,6 +1001,65 @@ protected function tokenize($string)
9971001
}
9981002
}//end if
9991003

1004+
/*
1005+
Convert enum "case" to T_ENUM_CASE
1006+
*/
1007+
1008+
if ($tokenIsArray === true
1009+
&& $token[0] === T_CASE
1010+
&& isset($this->tstringContexts[$finalTokens[$lastNotEmptyToken]['code']]) === false
1011+
) {
1012+
$isEnumCase = false;
1013+
$scope = 1;
1014+
1015+
for ($i = ($stackPtr - 1); $i > 0; $i--) {
1016+
if ($tokens[$i] === '}') {
1017+
$scope++;
1018+
continue;
1019+
}
1020+
1021+
if ($tokens[$i] === '{') {
1022+
$scope--;
1023+
continue;
1024+
}
1025+
1026+
if (is_array($tokens[$i]) === false) {
1027+
continue;
1028+
}
1029+
1030+
if ($scope !== 0) {
1031+
continue;
1032+
}
1033+
1034+
if ($tokens[$i][0] === T_SWITCH) {
1035+
break;
1036+
}
1037+
1038+
if ($tokens[$i][0] === T_ENUM || $tokens[$i][0] === T_ENUM_CASE) {
1039+
$isEnumCase = true;
1040+
break;
1041+
}
1042+
}//end for
1043+
1044+
if ($isEnumCase === true) {
1045+
// Modify $tokens directly so we can use it as optimisation for other enum "case".
1046+
$tokens[$stackPtr][0] = T_ENUM_CASE;
1047+
1048+
$newToken = [];
1049+
$newToken['code'] = T_ENUM_CASE;
1050+
$newToken['type'] = 'T_ENUM_CASE';
1051+
$newToken['content'] = $token[1];
1052+
$finalTokens[$newStackPtr] = $newToken;
1053+
1054+
if (PHP_CODESNIFFER_VERBOSITY > 1) {
1055+
echo "\t\t* token $stackPtr changed from T_CASE to T_ENUM_CASE".PHP_EOL;
1056+
}
1057+
1058+
$newStackPtr++;
1059+
continue;
1060+
}
1061+
}//end if
1062+
10001063
/*
10011064
As of PHP 8.0 fully qualified, partially qualified and namespace relative
10021065
identifier names are tokenized differently.

src/Util/Tokens.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
define('T_MATCH_ARROW', 'PHPCS_T_MATCH_ARROW');
8181
define('T_MATCH_DEFAULT', 'PHPCS_T_MATCH_DEFAULT');
8282
define('T_ATTRIBUTE_END', 'PHPCS_T_ATTRIBUTE_END');
83+
define('T_ENUM_CASE', 'PHPCS_T_ENUM_CASE');
8384

8485
// Some PHP 5.5 tokens, replicated for lower versions.
8586
if (defined('T_FINALLY') === false) {

tests/Core/Tokenizer/EnumCaseTest.inc

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<?php
2+
3+
enum Foo
4+
{
5+
/* testPureEnumCase */
6+
case SOME_CASE;
7+
}
8+
9+
enum Boo: int {
10+
/* testBackingIntegerEnumCase */
11+
case ONE = 1;
12+
}
13+
14+
enum Hoo: string
15+
{
16+
/* testBackingStringEnumCase */
17+
case ONE = 'one';
18+
}
19+
20+
enum ComplexEnum: int implements SomeInterface
21+
{
22+
use SomeTrait {
23+
traitMethod as enumMethod;
24+
}
25+
26+
const SOME_CONSTANT = true;
27+
28+
/* testEnumCaseInComplexEnum */
29+
case ONE = 1;
30+
31+
/* testEnumCaseIsCaseInsensitive */
32+
CaSe TWO = 2;
33+
34+
public function someMethod(): bool
35+
{
36+
switch (true) {
37+
/* testCaseWithSemicolonIsNotEnumCase */
38+
case CONSTANT;
39+
}
40+
}
41+
42+
/* testEnumCaseAfterSwitch */
43+
case THREE = 3;
44+
45+
public function someOtherMethod(): bool
46+
{
47+
switch (true):
48+
case false:
49+
endswitch;
50+
}
51+
52+
/* testEnumCaseAfterSwitchWithEndSwitch */
53+
case FOUR = 4;
54+
}
55+
56+
switch (true) {
57+
/* testCaseWithConstantIsNotEnumCase */
58+
case CONSTANT:
59+
/* testCaseWithConstantAndIdenticalIsNotEnumCase */
60+
case CONSTANT === 1:
61+
/* testCaseWithAssigmentToConstantIsNotEnumCase */
62+
case CONSTANT = 1:
63+
/* testIsNotEnumCaseIsCaseInsensitive */
64+
cAsE CONSTANT:
65+
}
66+
67+
switch ($x) {
68+
/* testCaseInSwitchWhenCreatingEnumInSwitch1 */
69+
case 'a': {
70+
enum Foo {}
71+
break;
72+
}
73+
74+
/* testCaseInSwitchWhenCreatingEnumInSwitch2 */
75+
case 'b';
76+
enum Bar {}
77+
break;
78+
}

tests/Core/Tokenizer/EnumCaseTest.php

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
<?php
2+
/**
3+
* Tests converting enum "case" to T_ENUM_CASE.
4+
*
5+
* @author Jaroslav Hanslík <[email protected]>
6+
* @copyright 2021 Squiz Pty Ltd (ABN 77 084 670 600)
7+
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
8+
*/
9+
10+
namespace PHP_CodeSniffer\Tests\Core\Tokenizer;
11+
12+
use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest;
13+
14+
class EnumCaseTest extends AbstractMethodUnitTest
15+
{
16+
17+
18+
/**
19+
* Test that the enum "case" is converted to T_ENUM_CASE.
20+
*
21+
* @param string $testMarker The comment which prefaces the target token in the test file.
22+
*
23+
* @dataProvider dataEnumCases
24+
* @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize
25+
* @covers PHP_CodeSniffer\Tokenizers\Tokenizer::recurseScopeMap
26+
*
27+
* @return void
28+
*/
29+
public function testEnumCases($testMarker)
30+
{
31+
$tokens = self::$phpcsFile->getTokens();
32+
33+
$enumCase = $this->getTargetToken($testMarker, [T_ENUM_CASE, T_CASE]);
34+
35+
$this->assertSame(T_ENUM_CASE, $tokens[$enumCase]['code']);
36+
$this->assertSame('T_ENUM_CASE', $tokens[$enumCase]['type']);
37+
38+
$this->assertArrayNotHasKey('scope_condition', $tokens[$enumCase], 'Scope condition is set');
39+
$this->assertArrayNotHasKey('scope_opener', $tokens[$enumCase], 'Scope opener is set');
40+
$this->assertArrayNotHasKey('scope_closer', $tokens[$enumCase], 'Scope closer is set');
41+
42+
}//end testEnumCases()
43+
44+
45+
/**
46+
* Data provider.
47+
*
48+
* @see testEnumCases()
49+
*
50+
* @return array
51+
*/
52+
public function dataEnumCases()
53+
{
54+
return [
55+
['/* testPureEnumCase */'],
56+
['/* testBackingIntegerEnumCase */'],
57+
['/* testBackingStringEnumCase */'],
58+
['/* testEnumCaseInComplexEnum */'],
59+
['/* testEnumCaseIsCaseInsensitive */'],
60+
['/* testEnumCaseAfterSwitch */'],
61+
['/* testEnumCaseAfterSwitchWithEndSwitch */'],
62+
];
63+
64+
}//end dataEnumCases()
65+
66+
67+
/**
68+
* Test that "case" that is not enum case is still tokenized as `T_CASE`.
69+
*
70+
* @param string $testMarker The comment which prefaces the target token in the test file.
71+
*
72+
* @dataProvider dataNotEnumCases
73+
* @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize
74+
* @covers PHP_CodeSniffer\Tokenizers\Tokenizer::recurseScopeMap
75+
*
76+
* @return void
77+
*/
78+
public function testNotEnumCases($testMarker)
79+
{
80+
$tokens = self::$phpcsFile->getTokens();
81+
82+
$case = $this->getTargetToken($testMarker, [T_ENUM_CASE, T_CASE]);
83+
84+
$this->assertSame(T_CASE, $tokens[$case]['code']);
85+
$this->assertSame('T_CASE', $tokens[$case]['type']);
86+
87+
$this->assertArrayHasKey('scope_condition', $tokens[$case], 'Scope condition is not set');
88+
$this->assertArrayHasKey('scope_opener', $tokens[$case], 'Scope opener is not set');
89+
$this->assertArrayHasKey('scope_closer', $tokens[$case], 'Scope closer is not set');
90+
91+
}//end testNotEnumCases()
92+
93+
94+
/**
95+
* Data provider.
96+
*
97+
* @see testNotEnumCases()
98+
*
99+
* @return array
100+
*/
101+
public function dataNotEnumCases()
102+
{
103+
return [
104+
['/* testCaseWithSemicolonIsNotEnumCase */'],
105+
['/* testCaseWithConstantIsNotEnumCase */'],
106+
['/* testCaseWithConstantAndIdenticalIsNotEnumCase */'],
107+
['/* testCaseWithAssigmentToConstantIsNotEnumCase */'],
108+
['/* testIsNotEnumCaseIsCaseInsensitive */'],
109+
['/* testCaseInSwitchWhenCreatingEnumInSwitch1 */'],
110+
['/* testCaseInSwitchWhenCreatingEnumInSwitch2 */'],
111+
];
112+
113+
}//end dataNotEnumCases()
114+
115+
116+
}//end class

0 commit comments

Comments
 (0)