@@ -154,6 +154,7 @@ private function listFieldResolvedValueErrors(
154
154
$ objectType ,
155
155
$ fieldName ,
156
156
$ resolverClassType ,
157
+ [],
157
158
);
158
159
159
160
foreach ($ errors as $ error ) {
@@ -186,6 +187,7 @@ private function listFieldResolvedValueErrors(
186
187
187
188
188
189
/**
190
+ * @param list<string> $alreadyVisitedFields
189
191
* @return array{list<PHPStan\Type\Type>, list<string>}
190
192
*/
191
193
private function listFieldResolvedValueTypes (
@@ -194,15 +196,23 @@ private function listFieldResolvedValueTypes(
194
196
string $ objectType ,
195
197
string $ fieldName ,
196
198
PHPStan \Type \ObjectType $ resolverClassType ,
199
+ array $ alreadyVisitedFields ,
197
200
): array
198
201
{
199
202
$ errors = [];
200
203
$ types = [];
201
204
205
+ $ parentTypes = $ this ->listObjectTypeResolvedValueTypes (
206
+ $ scope ,
207
+ $ schemaServiceOraculum ,
208
+ $ objectType ,
209
+ [...$ alreadyVisitedFields , "$ objectType. $ fieldName " ],
210
+ );
211
+
202
212
if ($ resolverClassType ->getClassName () === Vojtechdobes \GraphQL \ArrayFieldResolver::class) {
203
213
$ offsetType = new PHPStan \Type \Constant \ConstantStringType ($ fieldName );
204
214
205
- foreach ($ this -> listObjectTypeResolvedValueTypes ( $ scope , $ schemaServiceOraculum , $ objectType ) as $ parentType ) {
215
+ foreach ($ parentTypes as $ parentType ) {
206
216
if ($ parentType ->isOffsetAccessible ()->yes () === false ) {
207
217
$ errors [] = sprintf (
208
218
"Resolver %s of field %s expects parent to have array access, but parent is resolved to %s " ,
@@ -225,7 +235,7 @@ private function listFieldResolvedValueTypes(
225
235
} elseif ($ resolverClassType ->getClassName () === Vojtechdobes \GraphQL \GetterFieldResolver::class) {
226
236
$ methodName = 'get ' . ucfirst ($ fieldName );
227
237
228
- foreach ($ this -> listObjectTypeResolvedValueTypes ( $ scope , $ schemaServiceOraculum , $ objectType ) as $ parentType ) {
238
+ foreach ($ parentTypes as $ parentType ) {
229
239
if ($ parentType ->isObject ()->yes () === false ) {
230
240
$ errors [] = sprintf (
231
241
"Resolver %s of field %s expects parent to be an object, but parent is resolved to %s " ,
@@ -250,7 +260,7 @@ private function listFieldResolvedValueTypes(
250
260
}
251
261
}
252
262
} elseif ($ resolverClassType ->getClassName () === Vojtechdobes \GraphQL \PropertyFieldResolver::class) {
253
- foreach ($ this -> listObjectTypeResolvedValueTypes ( $ scope , $ schemaServiceOraculum , $ objectType ) as $ parentType ) {
263
+ foreach ($ parentTypes as $ parentType ) {
254
264
if ($ parentType ->isObject ()->yes () === false ) {
255
265
$ errors [] = sprintf (
256
266
"Resolver %s of field %s expects parent to be an object, but parent is resolved to %s " ,
@@ -274,7 +284,7 @@ private function listFieldResolvedValueTypes(
274
284
} else {
275
285
$ expectedParentType = $ resolverClassType ->getTemplateType (Vojtechdobes \GraphQL \FieldResolver::class, 'TObjectValue ' );
276
286
277
- foreach ($ this -> listObjectTypeResolvedValueTypes ( $ scope , $ schemaServiceOraculum , $ objectType ) as $ parentType ) {
287
+ foreach ($ parentTypes as $ parentType ) {
278
288
if ($ expectedParentType ->isSuperTypeOf ($ parentType )->yes () === false ) {
279
289
$ errors [] = sprintf (
280
290
"Resolver %s of field %s expects parent to be %s, but parent is resolved to %s " ,
@@ -303,12 +313,14 @@ private function listFieldResolvedValueTypes(
303
313
304
314
305
315
/**
316
+ * @param list<string> $alreadyVisitedFields
306
317
* @return list<PHPStan\Type\Type>
307
318
*/
308
319
private function listObjectTypeResolvedValueTypes (
309
320
PHPStan \Analyser \Scope $ scope ,
310
321
SchemaServiceOraculum $ schemaServiceOraculum ,
311
322
string $ objectType ,
323
+ array $ alreadyVisitedFields ,
312
324
): array
313
325
{
314
326
$ result = [];
@@ -318,12 +330,19 @@ private function listObjectTypeResolvedValueTypes(
318
330
}
319
331
320
332
foreach ($ schemaServiceOraculum ->listFieldsResolvedToObjectType ($ objectType ) as [$ parentObjectType , $ parentFieldName ]) {
333
+ $ parentField = "{$ parentObjectType }. {$ parentFieldName }" ;
334
+
335
+ if (in_array ($ parentField , $ alreadyVisitedFields , true )) {
336
+ continue ;
337
+ }
338
+
321
339
[$ parentTypes ] = $ this ->listFieldResolvedValueTypes (
322
340
$ scope ,
323
341
$ schemaServiceOraculum ,
324
342
$ parentObjectType ,
325
343
$ parentFieldName ,
326
344
$ schemaServiceOraculum ->getFieldResolverType ($ parentObjectType , $ parentFieldName ),
345
+ [...$ alreadyVisitedFields , $ parentField ],
327
346
);
328
347
329
348
foreach ($ parentTypes as $ parentType ) {
0 commit comments