@@ -37,6 +37,29 @@ class LowerCaseConstantSniff implements Sniff
3737 T_NULL => T_NULL ,
3838 ];
3939
40+ /**
41+ * Token types which can be encountered in a property type declaration.
42+ *
43+ * @var array<int|string, int|string>
44+ */
45+ private $ propertyTypeTokens = [
46+ T_CALLABLE => T_CALLABLE ,
47+ T_SELF => T_SELF ,
48+ T_PARENT => T_PARENT ,
49+ T_FALSE => T_FALSE ,
50+ T_TRUE => T_TRUE ,
51+ T_NULL => T_NULL ,
52+ T_STRING => T_STRING ,
53+ T_NAME_QUALIFIED => T_NAME_QUALIFIED ,
54+ T_NAME_FULLY_QUALIFIED => T_NAME_FULLY_QUALIFIED ,
55+ T_NAME_RELATIVE => T_NAME_RELATIVE ,
56+ T_NS_SEPARATOR => T_NS_SEPARATOR ,
57+ T_NAMESPACE => T_NAMESPACE ,
58+ T_TYPE_UNION => T_TYPE_UNION ,
59+ T_TYPE_INTERSECTION => T_TYPE_INTERSECTION ,
60+ T_NULLABLE => T_NULLABLE ,
61+ ];
62+
4063
4164 /**
4265 * Returns an array of tokens this test wants to listen for.
@@ -47,7 +70,13 @@ public function register()
4770 {
4871 $ targets = $ this ->targets ;
4972
50- // Register function keywords to filter out type declarations.
73+ // Register scope modifiers to filter out property type declarations.
74+ $ targets += Tokens::$ scopeModifiers ;
75+ $ targets [] = T_VAR ;
76+ $ targets [] = T_STATIC ;
77+ $ targets [] = T_READONLY ;
78+
79+ // Register function keywords to filter out param/return type declarations.
5180 $ targets [] = T_FUNCTION ;
5281 $ targets [] = T_CLOSURE ;
5382 $ targets [] = T_FN ;
@@ -64,12 +93,43 @@ public function register()
6493 * @param int $stackPtr The position of the current token in the
6594 * stack passed in $tokens.
6695 *
67- * @return void|int
96+ * @return void|int Optionally returns a stack pointer. The sniff will not be
97+ * called again on the current file until the returned stack
98+ * pointer is reached.
6899 */
69100 public function process (File $ phpcsFile , $ stackPtr )
70101 {
71102 $ tokens = $ phpcsFile ->getTokens ();
72103
104+ /*
105+ * Skip over type declarations for properties.
106+ *
107+ * Note: for other uses of the visibility modifiers (functions, constants, trait use),
108+ * nothing relevant will be skipped as the next non-empty token will be an "non-skippable"
109+ * one.
110+ * Functions are handled separately below (and then skip to their scope opener), so
111+ * this should also not cause any confusion for constructor property promotion.
112+ *
113+ * For other uses of the "static" keyword, it also shouldn't be problematic as the only
114+ * time the next non-empty token will be a "skippable" token will be in return type
115+ * declarations, in which case, it is correct to skip over them.
116+ */
117+
118+ if (isset (Tokens::$ scopeModifiers [$ tokens [$ stackPtr ]['code ' ]]) === true
119+ || $ tokens [$ stackPtr ]['code ' ] === T_VAR
120+ || $ tokens [$ stackPtr ]['code ' ] === T_STATIC
121+ || $ tokens [$ stackPtr ]['code ' ] === T_READONLY
122+ ) {
123+ $ skipOver = (Tokens::$ emptyTokens + $ this ->propertyTypeTokens );
124+ $ skipTo = $ phpcsFile ->findNext ($ skipOver , ($ stackPtr + 1 ), null , true );
125+ if ($ skipTo !== false ) {
126+ return $ skipTo ;
127+ }
128+
129+ // If we're at the end of the file, just return.
130+ return ;
131+ }
132+
73133 // Handle function declarations separately as they may contain the keywords in type declarations.
74134 if ($ tokens [$ stackPtr ]['code ' ] === T_FUNCTION
75135 || $ tokens [$ stackPtr ]['code ' ] === T_CLOSURE
@@ -79,9 +139,15 @@ public function process(File $phpcsFile, $stackPtr)
79139 return ;
80140 }
81141
142+ // Make sure to skip over return type declarations.
82143 $ end = $ tokens [$ stackPtr ]['parenthesis_closer ' ];
83144 if (isset ($ tokens [$ stackPtr ]['scope_opener ' ]) === true ) {
84145 $ end = $ tokens [$ stackPtr ]['scope_opener ' ];
146+ } else {
147+ $ skipTo = $ phpcsFile ->findNext ([T_SEMICOLON , T_OPEN_CURLY_BRACKET ], ($ end + 1 ), null , false , null , true );
148+ if ($ skipTo !== false ) {
149+ $ end = $ skipTo ;
150+ }
85151 }
86152
87153 // Do a quick check if any of the targets exist in the declaration.
@@ -114,21 +180,6 @@ public function process(File $phpcsFile, $stackPtr)
114180 return $ end ;
115181 }//end if
116182
117- // Handle property declarations separately as they may contain the keywords in type declarations.
118- if (isset ($ tokens [$ stackPtr ]['conditions ' ]) === true ) {
119- $ conditions = $ tokens [$ stackPtr ]['conditions ' ];
120- $ lastCondition = end ($ conditions );
121- if (isset (Tokens::$ ooScopeTokens [$ lastCondition ]) === true ) {
122- // This can only be an OO constant or property declaration as methods are handled above.
123- $ equals = $ phpcsFile ->findPrevious (T_EQUAL , ($ stackPtr - 1 ), null , false , null , true );
124- if ($ equals !== false ) {
125- $ this ->processConstant ($ phpcsFile , $ stackPtr );
126- }
127-
128- return ;
129- }
130- }
131-
132183 // Handle everything else.
133184 $ this ->processConstant ($ phpcsFile , $ stackPtr );
134185
0 commit comments