Skip to content

Commit

Permalink
Merge pull request #331 from PHPCSStandards/php-8.3/generic-lowercase…
Browse files Browse the repository at this point in the history
…type-support-typed-constants

PHP 8.3 | Generic/LowerCaseType: add support for typed constants
  • Loading branch information
jrfnl committed Feb 15, 2024
2 parents 1969478 + b265092 commit 5e9339a
Show file tree
Hide file tree
Showing 4 changed files with 181 additions and 47 deletions.
68 changes: 64 additions & 4 deletions src/Standards/Generic/Sniffs/PHP/LowerCaseTypeSniff.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ public function process(File $phpcsFile, $stackPtr)
}

/*
* Check property types.
* Check OO constant and property types.
*/

if (isset(Tokens::$ooScopeTokens[$tokens[$stackPtr]['code']]) === true) {
Expand All @@ -97,22 +97,82 @@ public function process(File $phpcsFile, $stackPtr)

for ($i = ($tokens[$stackPtr]['scope_opener'] + 1); $i < $tokens[$stackPtr]['scope_closer']; $i++) {
// Skip over potentially large docblocks.
if ($tokens[$i]['code'] === \T_DOC_COMMENT_OPEN_TAG
if ($tokens[$i]['code'] === T_DOC_COMMENT_OPEN_TAG
&& isset($tokens[$i]['comment_closer']) === true
) {
$i = $tokens[$i]['comment_closer'];
continue;
}

// Skip over function declarations and everything nested within.
if ($tokens[$i]['code'] === \T_FUNCTION
if ($tokens[$i]['code'] === T_FUNCTION
&& isset($tokens[$i]['scope_closer']) === true
) {
$i = $tokens[$i]['scope_closer'];
continue;
}

if ($tokens[$i]['code'] !== \T_VARIABLE) {
if ($tokens[$i]['code'] === T_CONST) {
$ignore = Tokens::$emptyTokens;
$ignore[T_NULLABLE] = T_NULLABLE;

$startOfType = $phpcsFile->findNext($ignore, ($i + 1), null, true);
if ($startOfType === false) {
// Parse error/live coding. Nothing to do. Rest of loop is moot.
return;
}

$assignmentOperator = $phpcsFile->findNext([T_EQUAL, T_SEMICOLON], ($startOfType + 1));
if ($assignmentOperator === false || $tokens[$assignmentOperator]['code'] !== T_EQUAL) {
// Parse error/live coding. Nothing to do. Rest of loop is moot.
return;
}

$constName = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($assignmentOperator - 1), null, true);
if ($startOfType !== $constName) {
$endOfType = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($constName - 1), null, true);

$type = '';
$isUnionType = false;
$isIntersectionType = false;
for ($j = $startOfType; $j <= $endOfType; $j++) {
if (isset($ignore[$tokens[$j]['code']]) === true) {
continue;
}

if ($tokens[$j]['code'] === T_TYPE_UNION) {
$isUnionType = true;
}

if ($tokens[$j]['code'] === T_TYPE_INTERSECTION) {
$isIntersectionType = true;
}

$type .= $tokens[$j]['content'];
}

$error = 'PHP constant type declarations must be lowercase; expected "%s" but found "%s"';
$errorCode = 'ConstantTypeFound';

if ($isIntersectionType === true) {
// Intersection types don't support simple types.
} else if ($isUnionType === true) {
$this->processUnionType(
$phpcsFile,
$startOfType,
$endOfType,
$error,
$errorCode
);
} else if (isset($this->phpTypes[strtolower($type)]) === true) {
$this->processType($phpcsFile, $startOfType, $type, $error, $errorCode);
}
}//end if

continue;
}//end if

if ($tokens[$i]['code'] !== T_VARIABLE) {
continue;
}

Expand Down
30 changes: 30 additions & 0 deletions src/Standards/Generic/Tests/PHP/LowerCaseTypeUnitTest.inc
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,36 @@ $arrow = fn (Int $a, String $b, BOOL $c, Array $d, Foo\Bar $e) : Float => $a * $

$cl = function (False $a, TRUE $b, Null $c): ?True {};

class TypedClassConstants
{
const UNTYPED = null;
const FLOAT = 'Reserved keyword as name is valid and should not be changed';
const OBJECT = 'Reserved keyword as name is valid and should not be changed';

const ClassName FIRST = null;
public const Int SECOND = 0;
private const ?BOOL THIRD = false;
public const Self FOURTH = null;
}
interface TypedInterfaceConstants
{
protected const PaRenT FIRST = null;
private const ARRAY SECOND = [];
public const Float THIRD = 2.5;
final const ?STRING FOURTH = 'fourth';
}
trait TypedTraitConstants {
const IterablE FIRST = null;
const Object SECOND = null;
const Mixed THIRD = 'third';
}
enum TypedEnumConstants {
public const Iterable|FALSE|NULL FIRST = null;
protected const SELF|Parent /* comment */ |\Fully\Qualified\ClassName|UnQualifiedClass SECOND = null;
private const ClassName|/*comment*/Float|STRING|False THIRD = 'third';
public const sTRing | aRRaY | FaLSe FOURTH = 'fourth';
}

// Intentional error, should be ignored by the sniff.
interface PropertiesNotAllowed {
public $notAllowed;
Expand Down
30 changes: 30 additions & 0 deletions src/Standards/Generic/Tests/PHP/LowerCaseTypeUnitTest.inc.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,36 @@ $arrow = fn (int $a, string $b, bool $c, array $d, Foo\Bar $e) : float => $a * $

$cl = function (false $a, true $b, null $c): ?true {};

class TypedClassConstants
{
const UNTYPED = null;
const FLOAT = 'Reserved keyword as name is valid and should not be changed';
const OBJECT = 'Reserved keyword as name is valid and should not be changed';

const ClassName FIRST = null;
public const int SECOND = 0;
private const ?bool THIRD = false;
public const self FOURTH = null;
}
interface TypedInterfaceConstants
{
protected const parent FIRST = null;
private const array SECOND = [];
public const float THIRD = 2.5;
final const ?string FOURTH = 'fourth';
}
trait TypedTraitConstants {
const iterable FIRST = null;
const object SECOND = null;
const mixed THIRD = 'third';
}
enum TypedEnumConstants {
public const iterable|false|null FIRST = null;
protected const self|parent /* comment */ |\Fully\Qualified\ClassName|UnQualifiedClass SECOND = null;
private const ClassName|/*comment*/float|string|false THIRD = 'third';
public const string | array | false FOURTH = 'fourth';
}

// Intentional error, should be ignored by the sniff.
interface PropertiesNotAllowed {
public $notAllowed;
Expand Down
100 changes: 57 additions & 43 deletions src/Standards/Generic/Tests/PHP/LowerCaseTypeUnitTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,48 +31,62 @@ final class LowerCaseTypeUnitTest extends AbstractSniffUnitTest
public function getErrorList()
{
return [
14 => 1,
15 => 1,
16 => 1,
17 => 1,
18 => 1,
21 => 4,
22 => 3,
23 => 3,
25 => 1,
26 => 2,
27 => 2,
32 => 4,
36 => 1,
37 => 1,
38 => 1,
39 => 1,
43 => 2,
44 => 1,
46 => 1,
49 => 1,
51 => 2,
53 => 1,
55 => 2,
60 => 1,
61 => 1,
62 => 1,
63 => 1,
64 => 1,
65 => 1,
66 => 1,
67 => 1,
68 => 1,
69 => 1,
71 => 3,
72 => 2,
73 => 3,
74 => 3,
78 => 3,
82 => 2,
85 => 1,
94 => 5,
96 => 4,
14 => 1,
15 => 1,
16 => 1,
17 => 1,
18 => 1,
21 => 4,
22 => 3,
23 => 3,
25 => 1,
26 => 2,
27 => 2,
32 => 4,
36 => 1,
37 => 1,
38 => 1,
39 => 1,
43 => 2,
44 => 1,
46 => 1,
49 => 1,
51 => 2,
53 => 1,
55 => 2,
60 => 1,
61 => 1,
62 => 1,
63 => 1,
64 => 1,
65 => 1,
66 => 1,
67 => 1,
68 => 1,
69 => 1,
71 => 3,
72 => 2,
73 => 3,
74 => 3,
78 => 3,
82 => 2,
85 => 1,
94 => 5,
96 => 4,
105 => 1,
106 => 1,
107 => 1,
111 => 1,
112 => 1,
113 => 1,
114 => 1,
117 => 1,
118 => 1,
119 => 1,
122 => 3,
123 => 2,
124 => 3,
125 => 3,
];

}//end getErrorList()
Expand All @@ -89,7 +103,7 @@ public function getErrorList()
public function getWarningList()
{
// Warning from getMemberProperties() about parse error.
return [100 => 1];
return [130 => 1];

}//end getWarningList()

Expand Down

0 comments on commit 5e9339a

Please sign in to comment.