@@ -157,34 +157,17 @@ function local_moodlecheck_functionarguments(local_moodlecheck_file $file) {
157157 // Must be at least type and parameter name.
158158 $ match = false ;
159159 } else {
160- $ expectedtype = local_moodlecheck_normalise_function_type ((string ) $ function ->arguments [$ i ][0 ]);
160+ $ expectedtype = local_moodlecheck_normalise_function_type ((string )$ function ->arguments [$ i ][0 ]);
161161 $ expectedparam = (string )$ function ->arguments [$ i ][1 ];
162- $ documentedtype = local_moodlecheck_normalise_function_type ((string ) $ documentedarguments [$ i ][0 ]);
162+ $ documentedtype = local_moodlecheck_normalise_function_type ((string )$ documentedarguments [$ i ][0 ]);
163163 $ documentedparam = $ documentedarguments [$ i ][1 ];
164164
165- $ typematch = $ expectedtype === $ documentedtype ;
166- $ parammatch = $ expectedparam === $ documentedparam ;
167- if ($ typematch && $ parammatch ) {
168- continue ;
165+ if ($ expectedparam !== $ documentedparam ) {
166+ $ match = false ;
169167 }
170168
171- // Documented types can be a collection (| separated).
172- foreach (explode ('| ' , $ documentedtype ) as $ documentedtype ) {
173- // Ignore null. They cannot match any type in function.
174- if (trim ($ documentedtype ) === 'null ' ) {
175- continue ;
176- }
177-
178- if (strlen ($ expectedtype ) && $ expectedtype !== $ documentedtype ) {
179- // It could be a type hinted array.
180- if ($ expectedtype !== 'array ' || substr ($ documentedtype , -2 ) !== '[] ' ) {
181- $ match = false ;
182- }
183- } else if ($ documentedtype === 'type ' ) {
184- $ match = false ;
185- } else if ($ expectedparam !== $ documentedparam ) {
186- $ match = false ;
187- }
169+ if (!local_moodlecheck_is_documented_type_allowed ($ expectedtype , $ documentedtype )) {
170+ $ match = false ;
188171 }
189172 }
190173 }
@@ -204,6 +187,49 @@ function local_moodlecheck_functionarguments(local_moodlecheck_file $file) {
204187 return $ errors ;
205188}
206189
190+ /**
191+ * Checks if a documented type is allowed for a parameter with the given real type declaration.
192+ *
193+ * @param string $expectedtype the real type declaration
194+ * @param string $documentedtype the type documented in PHPdoc
195+ * @return bool true if allowed, false if not
196+ */
197+ function local_moodlecheck_is_documented_type_allowed (string $ expectedtype , string $ documentedtype ): bool {
198+ if ($ expectedtype === $ documentedtype ) {
199+ return true ;
200+ }
201+
202+ // Documented types can be a collection (| separated).
203+ foreach (explode ('| ' , $ documentedtype ) as $ documentedtype ) {
204+ // Ignore null. They cannot match any type in function.
205+ if (trim ($ documentedtype ) === 'null ' ) {
206+ continue ;
207+ }
208+
209+ if (strlen ($ expectedtype ) && $ expectedtype !== $ documentedtype ) {
210+ // Allow type-hinted arrays.
211+ if ($ expectedtype === 'array ' && substr ($ documentedtype , -2 ) === '[] ' ) {
212+ continue ;
213+ }
214+
215+ $ withoutgenerics = substr ($ documentedtype , 0 , strpos ($ documentedtype , "< " )) ?: $ documentedtype ;
216+ // Allow class-string<T> and the like for a string parameter.
217+ if ($ expectedtype === 'string ' && in_array ($ withoutgenerics ,
218+ ["class-string " , "interface-string " , "trait-string " , "enum-string " , "callable-string " ,
219+ "numeric-string " , "literal-string " , "lowercase-string " , "non-empty-string " ,
220+ "non-empty-lowercase-string " ])) {
221+ continue ;
222+ }
223+
224+ return false ;
225+ } else if ($ documentedtype === 'type ' ) {
226+ return false ;
227+ }
228+ }
229+
230+ return true ;
231+ }
232+
207233/**
208234 * Normalise function type to be able to compare it.
209235 *
0 commit comments