@@ -88,16 +88,23 @@ public function process(File $phpcsFile, $stackPtr)
8888 $ props = $ phpcsFile ->getMethodProperties ($ stackPtr );
8989
9090 // Strip off potential nullable indication.
91- $ returnType = ltrim ($ props ['return_type ' ], '? ' );
92- $ returnTypeLower = strtolower ($ returnType );
91+ $ returnType = ltrim ($ props ['return_type ' ], '? ' );
9392
94- if ($ returnType !== ''
95- && isset ($ this ->phpTypes [$ returnTypeLower ]) === true
96- ) {
93+ if ($ returnType !== '' ) {
9794 $ error = 'PHP return type declarations must be lowercase; expected "%s" but found "%s" ' ;
9895 $ errorCode = 'ReturnTypeFound ' ;
9996
100- $ this ->processType ($ phpcsFile , $ props ['return_type_token ' ], $ returnType , $ error , $ errorCode );
97+ if (strpos ($ returnType , '| ' ) !== false ) {
98+ $ this ->processUnionType (
99+ $ phpcsFile ,
100+ $ props ['return_type_token ' ],
101+ $ props ['return_type_end_token ' ],
102+ $ error ,
103+ $ errorCode
104+ );
105+ } else if (isset ($ this ->phpTypes [strtolower ($ returnType )]) === true ) {
106+ $ this ->processType ($ phpcsFile , $ props ['return_type_token ' ], $ returnType , $ error , $ errorCode );
107+ }
101108 }
102109
103110 /*
@@ -111,22 +118,77 @@ public function process(File $phpcsFile, $stackPtr)
111118
112119 foreach ($ params as $ param ) {
113120 // Strip off potential nullable indication.
114- $ typeHint = ltrim ($ param ['type_hint ' ], '? ' );
115- $ typeHintLower = strtolower ($ typeHint );
121+ $ typeHint = ltrim ($ param ['type_hint ' ], '? ' );
116122
117- if ($ typeHint !== ''
118- && isset ($ this ->phpTypes [$ typeHintLower ]) === true
119- ) {
123+ if ($ typeHint !== '' ) {
120124 $ error = 'PHP parameter type declarations must be lowercase; expected "%s" but found "%s" ' ;
121125 $ errorCode = 'ParamTypeFound ' ;
122126
123- $ this ->processType ($ phpcsFile , $ param ['type_hint_token ' ], $ typeHint , $ error , $ errorCode );
127+ if (strpos ($ typeHint , '| ' ) !== false ) {
128+ $ this ->processUnionType (
129+ $ phpcsFile ,
130+ $ param ['type_hint_token ' ],
131+ $ param ['type_hint_end_token ' ],
132+ $ error ,
133+ $ errorCode
134+ );
135+ } else if (isset ($ this ->phpTypes [strtolower ($ typeHint )]) === true ) {
136+ $ this ->processType ($ phpcsFile , $ param ['type_hint_token ' ], $ typeHint , $ error , $ errorCode );
137+ }
124138 }
125- }
139+ }//end foreach
126140
127141 }//end process()
128142
129143
144+ /**
145+ * Processes a union type declaration.
146+ *
147+ * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
148+ * @param int $typeDeclStart The position of the start of the type token.
149+ * @param int $typeDeclEnd The position of the end of the type token.
150+ * @param string $error Error message template.
151+ * @param string $errorCode The error code.
152+ *
153+ * @return void
154+ */
155+ protected function processUnionType (File $ phpcsFile , $ typeDeclStart , $ typeDeclEnd , $ error , $ errorCode )
156+ {
157+ $ tokens = $ phpcsFile ->getTokens ();
158+ $ current = $ typeDeclStart ;
159+
160+ do {
161+ $ endOfType = $ phpcsFile ->findNext (T_TYPE_UNION , $ current , $ typeDeclEnd );
162+ if ($ endOfType === false ) {
163+ // This must be the last type in the union.
164+ $ endOfType = ($ typeDeclEnd + 1 );
165+ }
166+
167+ $ hasNsSep = $ phpcsFile ->findNext (T_NS_SEPARATOR , $ current , $ endOfType );
168+ if ($ hasNsSep !== false ) {
169+ // Multi-token class based type. Ignore.
170+ $ current = ($ endOfType + 1 );
171+ continue ;
172+ }
173+
174+ // Type consisting of a single token.
175+ $ startOfType = $ phpcsFile ->findNext (Tokens::$ emptyTokens , $ current , $ endOfType , true );
176+ if ($ startOfType === false ) {
177+ // Parse error.
178+ return ;
179+ }
180+
181+ $ type = $ tokens [$ startOfType ]['content ' ];
182+ if (isset ($ this ->phpTypes [strtolower ($ type )]) === true ) {
183+ $ this ->processType ($ phpcsFile , $ startOfType , $ type , $ error , $ errorCode );
184+ }
185+
186+ $ current = ($ endOfType + 1 );
187+ } while ($ current <= $ typeDeclEnd );
188+
189+ }//end processUnionType()
190+
191+
130192 /**
131193 * Processes a type cast or a singular type declaration.
132194 *
0 commit comments