1: <?php declare(strict_types = 1);
2:
3: namespace PHPStan\Type;
4:
5: use Closure;
6: use PHPStan\Analyser\OutOfClassScope;
7: use PHPStan\Php\PhpVersion;
8: use PHPStan\PhpDoc\Tag\TemplateTag;
9: use PHPStan\PhpDocParser\Ast\PhpDoc\TemplateTagValueNode;
10: use PHPStan\PhpDocParser\Ast\Type\CallableTypeNode;
11: use PHPStan\PhpDocParser\Ast\Type\CallableTypeParameterNode;
12: use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
13: use PHPStan\PhpDocParser\Ast\Type\TypeNode;
14: use PHPStan\PhpDocParser\Printer\Printer;
15: use PHPStan\Reflection\Assertions;
16: use PHPStan\Reflection\Callables\CallableParametersAcceptor;
17: use PHPStan\Reflection\Callables\SimpleImpurePoint;
18: use PHPStan\Reflection\Callables\SimpleThrowPoint;
19: use PHPStan\Reflection\ClassMemberAccessAnswerer;
20: use PHPStan\Reflection\ExtendedParameterReflection;
21: use PHPStan\Reflection\Native\NativeParameterReflection;
22: use PHPStan\Reflection\ParameterReflection;
23: use PHPStan\Reflection\ParametersAcceptor;
24: use PHPStan\Reflection\ParametersAcceptorSelector;
25: use PHPStan\Reflection\PassedByReference;
26: use PHPStan\Reflection\Php\DummyParameter;
27: use PHPStan\ShouldNotHappenException;
28: use PHPStan\TrinaryLogic;
29: use PHPStan\Type\Accessory\AccessoryNonEmptyStringType;
30: use PHPStan\Type\Enum\EnumCaseObjectType;
31: use PHPStan\Type\Generic\TemplateType;
32: use PHPStan\Type\Generic\TemplateTypeHelper;
33: use PHPStan\Type\Generic\TemplateTypeMap;
34: use PHPStan\Type\Generic\TemplateTypeVariance;
35: use PHPStan\Type\Generic\TemplateTypeVarianceMap;
36: use PHPStan\Type\Traits\MaybeArrayTypeTrait;
37: use PHPStan\Type\Traits\MaybeIterableTypeTrait;
38: use PHPStan\Type\Traits\MaybeObjectTypeTrait;
39: use PHPStan\Type\Traits\MaybeOffsetAccessibleTypeTrait;
40: use PHPStan\Type\Traits\NonGeneralizableTypeTrait;
41: use PHPStan\Type\Traits\NonRemoveableTypeTrait;
42: use PHPStan\Type\Traits\TruthyBooleanTypeTrait;
43: use PHPStan\Type\Traits\UndecidedComparisonCompoundTypeTrait;
44: use function array_key_exists;
45: use function array_map;
46: use function array_merge;
47: use function count;
48:
49: /** @api */
50: class CallableType implements CompoundType, CallableParametersAcceptor
51: {
52:
53: use MaybeArrayTypeTrait;
54: use MaybeIterableTypeTrait;
55: use MaybeObjectTypeTrait;
56: use MaybeOffsetAccessibleTypeTrait;
57: use TruthyBooleanTypeTrait;
58: use UndecidedComparisonCompoundTypeTrait;
59: use NonRemoveableTypeTrait;
60: use NonGeneralizableTypeTrait;
61:
62: /** @var list<ParameterReflection> */
63: private array $parameters;
64:
65: private Type $returnType;
66:
67: private bool $isCommonCallable;
68:
69: private TemplateTypeMap $templateTypeMap;
70:
71: private TemplateTypeMap $resolvedTemplateTypeMap;
72:
73: private TrinaryLogic $isPure;
74:
75: /**
76: * @api
77: * @param list<ParameterReflection>|null $parameters
78: * @param array<non-empty-string, TemplateTag> $templateTags
79: */
80: public function __construct(
81: ?array $parameters = null,
82: ?Type $returnType = null,
83: private bool $variadic = true,
84: ?TemplateTypeMap $templateTypeMap = null,
85: ?TemplateTypeMap $resolvedTemplateTypeMap = null,
86: private array $templateTags = [],
87: ?TrinaryLogic $isPure = null,
88: )
89: {
90: $this->parameters = $parameters ?? [];
91: $this->returnType = $returnType ?? new MixedType();
92: $this->isCommonCallable = $parameters === null && $returnType === null;
93: $this->templateTypeMap = $templateTypeMap ?? TemplateTypeMap::createEmpty();
94: $this->resolvedTemplateTypeMap = $resolvedTemplateTypeMap ?? TemplateTypeMap::createEmpty();
95: $this->isPure = $isPure ?? TrinaryLogic::createMaybe();
96: }
97:
98: /**
99: * @return array<non-empty-string, TemplateTag>
100: */
101: public function getTemplateTags(): array
102: {
103: return $this->templateTags;
104: }
105:
106: public function isPure(): TrinaryLogic
107: {
108: return $this->isPure;
109: }
110:
111: public function getReferencedClasses(): array
112: {
113: $classes = [];
114: foreach ($this->parameters as $parameter) {
115: $classes = array_merge($classes, $parameter->getType()->getReferencedClasses());
116: }
117:
118: return array_merge($classes, $this->returnType->getReferencedClasses());
119: }
120:
121: public function getObjectClassNames(): array
122: {
123: return [];
124: }
125:
126: public function getObjectClassReflections(): array
127: {
128: return [];
129: }
130:
131: public function getConstantStrings(): array
132: {
133: return [];
134: }
135:
136: public function accepts(Type $type, bool $strictTypes): AcceptsResult
137: {
138: if ($type instanceof CompoundType && !$type instanceof self) {
139: return $type->isAcceptedBy($this, $strictTypes);
140: }
141:
142: return $this->isSuperTypeOfInternal($type, true)->toAcceptsResult();
143: }
144:
145: public function isSuperTypeOf(Type $type): IsSuperTypeOfResult
146: {
147: if ($type instanceof CompoundType && !$type instanceof self) {
148: return $type->isSubTypeOf($this);
149: }
150:
151: return $this->isSuperTypeOfInternal($type, false);
152: }
153:
154: private function isSuperTypeOfInternal(Type $type, bool $treatMixedAsAny): IsSuperTypeOfResult
155: {
156: $isCallable = new IsSuperTypeOfResult($type->isCallable(), []);
157: if ($isCallable->no()) {
158: return $isCallable;
159: }
160:
161: static $scope;
162: if ($scope === null) {
163: $scope = new OutOfClassScope();
164: }
165:
166: if ($this->isCommonCallable) {
167: if ($this->isPure()->yes()) {
168: $typePure = TrinaryLogic::createYes();
169: foreach ($type->getCallableParametersAcceptors($scope) as $variant) {
170: $typePure = $typePure->and($variant->isPure());
171: }
172:
173: return $isCallable->and(new IsSuperTypeOfResult($typePure, []));
174: }
175:
176: return $isCallable;
177: }
178:
179: $parameterTypes = array_map(static fn ($parameter) => $parameter->getType(), $this->getParameters());
180:
181: $variantsResult = null;
182: foreach ($type->getCallableParametersAcceptors($scope) as $variant) {
183: $variant = ParametersAcceptorSelector::selectFromTypes($parameterTypes, [$variant], false);
184: if (!$variant instanceof CallableParametersAcceptor) {
185: return IsSuperTypeOfResult::createNo([]);
186: }
187: $isSuperType = CallableTypeHelper::isParametersAcceptorSuperTypeOf($this, $variant, $treatMixedAsAny);
188: if ($variantsResult === null) {
189: $variantsResult = $isSuperType;
190: } else {
191: $variantsResult = $variantsResult->or($isSuperType);
192: }
193: }
194:
195: if ($variantsResult === null) {
196: throw new ShouldNotHappenException();
197: }
198:
199: return $isCallable->and($variantsResult);
200: }
201:
202: public function isSubTypeOf(Type $otherType): IsSuperTypeOfResult
203: {
204: if ($otherType instanceof IntersectionType || $otherType instanceof UnionType) {
205: return $otherType->isSuperTypeOf($this);
206: }
207:
208: return new IsSuperTypeOfResult(
209: $otherType->isCallable()->and($otherType instanceof self ? TrinaryLogic::createYes() : TrinaryLogic::createMaybe()),
210: [],
211: );
212: }
213:
214: public function isAcceptedBy(Type $acceptingType, bool $strictTypes): AcceptsResult
215: {
216: return $this->isSubTypeOf($acceptingType)->toAcceptsResult();
217: }
218:
219: public function equals(Type $type): bool
220: {
221: if (!$type instanceof self) {
222: return false;
223: }
224:
225: if ($this->isCommonCallable) {
226: if (!$type->isCommonCallable) {
227: return false;
228: }
229: } elseif ($type->isCommonCallable) {
230: return false;
231: }
232:
233: if ($this->variadic !== $type->variadic) {
234: return false;
235: }
236:
237: if ($this->isPure !== $type->isPure) {
238: return false;
239: }
240:
241: if (!$this->returnType->equals($type->returnType)) {
242: return false;
243: }
244:
245: if (count($this->parameters) !== count($type->parameters)) {
246: return false;
247: }
248:
249: foreach ($this->parameters as $i => $parameter) {
250: $otherParameter = $type->parameters[$i];
251: if ($parameter->isOptional() !== $otherParameter->isOptional()) {
252: return false;
253: }
254: if (!$parameter->passedByReference()->equals($otherParameter->passedByReference())) {
255: return false;
256: }
257: if ($parameter->isVariadic() !== $otherParameter->isVariadic()) {
258: return false;
259: }
260: if (!$parameter->getType()->equals($otherParameter->getType())) {
261: return false;
262: }
263: if ($parameter->getDefaultValue() !== null) {
264: if ($otherParameter->getDefaultValue() === null) {
265: return false;
266: }
267:
268: return $parameter->getDefaultValue()->equals($otherParameter->getDefaultValue());
269: } elseif ($otherParameter->getDefaultValue() !== null) {
270: return false;
271: }
272: }
273:
274: foreach ([[$this->templateTypeMap, $type->templateTypeMap], [$this->resolvedTemplateTypeMap, $type->resolvedTemplateTypeMap]] as [$templateTypeMap, $otherTemplateTypeMap]) {
275: if ($templateTypeMap->count() !== $otherTemplateTypeMap->count()) {
276: return false;
277: }
278:
279: foreach ($templateTypeMap->getTypes() as $typeName => $templateType) {
280: $otherTemplateType = $otherTemplateTypeMap->getType($typeName);
281: if ($otherTemplateType === null) {
282: return false;
283: }
284:
285: if (!$templateType->equals($otherTemplateType)) {
286: return false;
287: }
288: }
289: }
290:
291: foreach ($this->templateTags as $tagName => $tag) {
292: if (!array_key_exists($tagName, $type->templateTags)) {
293: return false;
294: }
295:
296: $otherTag = $type->templateTags[$tagName];
297: if ($tag->getName() !== $otherTag->getName()) {
298: return false;
299: }
300:
301: if (!$tag->getBound()->equals($otherTag->getBound())) {
302: return false;
303: }
304: if (!$tag->getVariance()->equals($otherTag->getVariance())) {
305: return false;
306: }
307: if ($tag->getDefault() !== null) {
308: if ($otherTag->getDefault() === null) {
309: return false;
310: }
311:
312: return $tag->getDefault()->equals($otherTag->getDefault());
313: } elseif ($otherTag->getDefault() !== null) {
314: return false;
315: }
316: }
317:
318: return true;
319: }
320:
321: public function describe(VerbosityLevel $level): string
322: {
323: return $level->handle(
324: static fn (): string => 'callable',
325: function (): string {
326: $printer = new Printer();
327: $selfWithoutParameterNames = new self(
328: array_map(static fn (ParameterReflection $p): ParameterReflection => new DummyParameter(
329: '',
330: $p->getType(),
331: optional: $p->isOptional() && !$p->isVariadic(),
332: passedByReference: PassedByReference::createNo(),
333: variadic: $p->isVariadic(),
334: defaultValue: $p->getDefaultValue(),
335: ), $this->parameters),
336: $this->returnType,
337: $this->variadic,
338: $this->templateTypeMap,
339: $this->resolvedTemplateTypeMap,
340: $this->templateTags,
341: $this->isPure,
342: );
343:
344: return $printer->print($selfWithoutParameterNames->toPhpDocNode());
345: },
346: );
347: }
348:
349: public function isCallable(): TrinaryLogic
350: {
351: return TrinaryLogic::createYes();
352: }
353:
354: public function getCallableParametersAcceptors(ClassMemberAccessAnswerer $scope): array
355: {
356: return [$this];
357: }
358:
359: public function getThrowPoints(): array
360: {
361: return [
362: SimpleThrowPoint::createImplicit(),
363: ];
364: }
365:
366: public function getImpurePoints(): array
367: {
368: $pure = $this->isPure();
369: if ($pure->yes()) {
370: return [];
371: }
372:
373: return [
374: new SimpleImpurePoint(
375: 'functionCall',
376: 'call to a callable',
377: $pure->no(),
378: ),
379: ];
380: }
381:
382: public function getInvalidateExpressions(): array
383: {
384: return [];
385: }
386:
387: public function getUsedVariables(): array
388: {
389: return [];
390: }
391:
392: public function acceptsNamedArguments(): TrinaryLogic
393: {
394: return TrinaryLogic::createYes();
395: }
396:
397: public function mustUseReturnValue(): TrinaryLogic
398: {
399: return TrinaryLogic::createMaybe();
400: }
401:
402: public function getAsserts(): Assertions
403: {
404: return Assertions::createEmpty();
405: }
406:
407: public function toNumber(): Type
408: {
409: return new ErrorType();
410: }
411:
412: public function toAbsoluteNumber(): Type
413: {
414: return new ErrorType();
415: }
416:
417: public function toString(): Type
418: {
419: return new ErrorType();
420: }
421:
422: public function toInteger(): Type
423: {
424: return new ErrorType();
425: }
426:
427: public function toFloat(): Type
428: {
429: return new ErrorType();
430: }
431:
432: public function toArray(): Type
433: {
434: return new ArrayType(new MixedType(), new MixedType());
435: }
436:
437: public function toArrayKey(): Type
438: {
439: return new ErrorType();
440: }
441:
442: public function toCoercedArgumentType(bool $strictTypes): Type
443: {
444: return TypeCombinator::union(
445: $this,
446: new IntersectionType([new StringType(), new AccessoryNonEmptyStringType()]),
447: new ArrayType(new MixedType(true), new MixedType(true)),
448: new ObjectType(Closure::class),
449: );
450: }
451:
452: public function getTemplateTypeMap(): TemplateTypeMap
453: {
454: return $this->templateTypeMap;
455: }
456:
457: public function getResolvedTemplateTypeMap(): TemplateTypeMap
458: {
459: return $this->resolvedTemplateTypeMap;
460: }
461:
462: public function getCallSiteVarianceMap(): TemplateTypeVarianceMap
463: {
464: return TemplateTypeVarianceMap::createEmpty();
465: }
466:
467: /**
468: * @return list<ParameterReflection>
469: */
470: public function getParameters(): array
471: {
472: return $this->parameters;
473: }
474:
475: public function isVariadic(): bool
476: {
477: return $this->variadic;
478: }
479:
480: public function getReturnType(): Type
481: {
482: return $this->returnType;
483: }
484:
485: public function inferTemplateTypes(Type $receivedType): TemplateTypeMap
486: {
487: if ($receivedType instanceof UnionType || $receivedType instanceof IntersectionType) {
488: return $receivedType->inferTemplateTypesOn($this);
489: }
490:
491: if (! $receivedType->isCallable()->yes()) {
492: return TemplateTypeMap::createEmpty();
493: }
494:
495: $parametersAcceptors = $receivedType->getCallableParametersAcceptors(new OutOfClassScope());
496:
497: $typeMap = TemplateTypeMap::createEmpty();
498:
499: foreach ($parametersAcceptors as $parametersAcceptor) {
500: $typeMap = $typeMap->union($this->inferTemplateTypesOnParametersAcceptor($parametersAcceptor));
501: }
502:
503: return $typeMap;
504: }
505:
506: private function inferTemplateTypesOnParametersAcceptor(ParametersAcceptor $parametersAcceptor): TemplateTypeMap
507: {
508: $parameterTypes = array_map(static fn ($parameter) => $parameter->getType(), $this->getParameters());
509: $parametersAcceptor = ParametersAcceptorSelector::selectFromTypes($parameterTypes, [$parametersAcceptor], false);
510: $args = $parametersAcceptor->getParameters();
511: $returnType = $parametersAcceptor->getReturnType();
512:
513: $typeMap = TemplateTypeMap::createEmpty();
514: foreach ($this->getParameters() as $i => $param) {
515: $paramType = $param->getType();
516: if (isset($args[$i])) {
517: $argType = $args[$i]->getType();
518: } elseif ($paramType instanceof TemplateType) {
519: $argType = TemplateTypeHelper::resolveToBounds($paramType);
520: } else {
521: $argType = new NeverType();
522: }
523:
524: $typeMap = $typeMap->union($paramType->inferTemplateTypes($argType)->convertToLowerBoundTypes());
525: }
526:
527: return $typeMap->union($this->getReturnType()->inferTemplateTypes($returnType));
528: }
529:
530: public function getReferencedTemplateTypes(TemplateTypeVariance $positionVariance): array
531: {
532: $references = $this->getReturnType()->getReferencedTemplateTypes(
533: $positionVariance->compose(TemplateTypeVariance::createCovariant()),
534: );
535:
536: $paramVariance = $positionVariance->compose(TemplateTypeVariance::createContravariant());
537:
538: foreach ($this->getParameters() as $param) {
539: foreach ($param->getType()->getReferencedTemplateTypes($paramVariance) as $reference) {
540: $references[] = $reference;
541: }
542: }
543:
544: return $references;
545: }
546:
547: public function traverse(callable $cb): Type
548: {
549: if ($this->isCommonCallable) {
550: return $this;
551: }
552:
553: $parameters = array_map(static function (ParameterReflection $param) use ($cb): NativeParameterReflection {
554: $defaultValue = $param->getDefaultValue();
555: return new NativeParameterReflection(
556: $param->getName(),
557: $param->isOptional(),
558: $cb($param->getType()),
559: $param->passedByReference(),
560: $param->isVariadic(),
561: $defaultValue !== null ? $cb($defaultValue) : null,
562: );
563: }, $this->getParameters());
564:
565: return new self(
566: $parameters,
567: $cb($this->getReturnType()),
568: $this->isVariadic(),
569: $this->templateTypeMap,
570: $this->resolvedTemplateTypeMap,
571: $this->templateTags,
572: $this->isPure,
573: );
574: }
575:
576: public function traverseSimultaneously(Type $right, callable $cb): Type
577: {
578: if ($this->isCommonCallable) {
579: return $this;
580: }
581:
582: if (!$right->isCallable()->yes()) {
583: return $this;
584: }
585:
586: $rightAcceptors = $right->getCallableParametersAcceptors(new OutOfClassScope());
587: if (count($rightAcceptors) !== 1) {
588: return $this;
589: }
590:
591: $rightParameters = $rightAcceptors[0]->getParameters();
592: if (count($this->getParameters()) !== count($rightParameters)) {
593: return $this;
594: }
595:
596: $parameters = [];
597: foreach ($this->getParameters() as $i => $leftParam) {
598: $rightParam = $rightParameters[$i];
599: $leftDefaultValue = $leftParam->getDefaultValue();
600: $rightDefaultValue = $rightParam->getDefaultValue();
601: $defaultValue = $leftDefaultValue;
602: if ($leftDefaultValue !== null && $rightDefaultValue !== null) {
603: $defaultValue = $cb($leftDefaultValue, $rightDefaultValue);
604: }
605: $parameters[] = new NativeParameterReflection(
606: $leftParam->getName(),
607: $leftParam->isOptional(),
608: $cb($leftParam->getType(), $rightParam->getType()),
609: $leftParam->passedByReference(),
610: $leftParam->isVariadic(),
611: $defaultValue,
612: );
613: }
614:
615: return new self(
616: $parameters,
617: $cb($this->getReturnType(), $rightAcceptors[0]->getReturnType()),
618: $this->isVariadic(),
619: $this->templateTypeMap,
620: $this->resolvedTemplateTypeMap,
621: $this->templateTags,
622: $this->isPure,
623: );
624: }
625:
626: public function isOversizedArray(): TrinaryLogic
627: {
628: return TrinaryLogic::createNo();
629: }
630:
631: public function isNull(): TrinaryLogic
632: {
633: return TrinaryLogic::createNo();
634: }
635:
636: public function isConstantValue(): TrinaryLogic
637: {
638: return TrinaryLogic::createNo();
639: }
640:
641: public function isConstantScalarValue(): TrinaryLogic
642: {
643: return TrinaryLogic::createNo();
644: }
645:
646: public function getConstantScalarTypes(): array
647: {
648: return [];
649: }
650:
651: public function getConstantScalarValues(): array
652: {
653: return [];
654: }
655:
656: public function isTrue(): TrinaryLogic
657: {
658: return TrinaryLogic::createNo();
659: }
660:
661: public function isFalse(): TrinaryLogic
662: {
663: return TrinaryLogic::createNo();
664: }
665:
666: public function isBoolean(): TrinaryLogic
667: {
668: return TrinaryLogic::createNo();
669: }
670:
671: public function isFloat(): TrinaryLogic
672: {
673: return TrinaryLogic::createNo();
674: }
675:
676: public function isInteger(): TrinaryLogic
677: {
678: return TrinaryLogic::createNo();
679: }
680:
681: public function isString(): TrinaryLogic
682: {
683: return TrinaryLogic::createMaybe();
684: }
685:
686: public function isNumericString(): TrinaryLogic
687: {
688: return TrinaryLogic::createNo();
689: }
690:
691: public function isNonEmptyString(): TrinaryLogic
692: {
693: return TrinaryLogic::createMaybe();
694: }
695:
696: public function isNonFalsyString(): TrinaryLogic
697: {
698: return TrinaryLogic::createMaybe();
699: }
700:
701: public function isLiteralString(): TrinaryLogic
702: {
703: return TrinaryLogic::createMaybe();
704: }
705:
706: public function isLowercaseString(): TrinaryLogic
707: {
708: return TrinaryLogic::createMaybe();
709: }
710:
711: public function isClassString(): TrinaryLogic
712: {
713: return TrinaryLogic::createMaybe();
714: }
715:
716: public function isUppercaseString(): TrinaryLogic
717: {
718: return TrinaryLogic::createMaybe();
719: }
720:
721: public function getClassStringObjectType(): Type
722: {
723: return new ObjectWithoutClassType();
724: }
725:
726: public function getObjectTypeOrClassStringObjectType(): Type
727: {
728: return new ObjectWithoutClassType();
729: }
730:
731: public function isVoid(): TrinaryLogic
732: {
733: return TrinaryLogic::createNo();
734: }
735:
736: public function isScalar(): TrinaryLogic
737: {
738: return TrinaryLogic::createMaybe();
739: }
740:
741: public function looseCompare(Type $type, PhpVersion $phpVersion): BooleanType
742: {
743: return new BooleanType();
744: }
745:
746: public function getEnumCases(): array
747: {
748: return [];
749: }
750:
751: public function getEnumCaseObject(): ?EnumCaseObjectType
752: {
753: return null;
754: }
755:
756: public function isCommonCallable(): bool
757: {
758: return $this->isCommonCallable;
759: }
760:
761: public function exponentiate(Type $exponent): Type
762: {
763: return new ErrorType();
764: }
765:
766: public function getFiniteTypes(): array
767: {
768: return [];
769: }
770:
771: public function toPhpDocNode(): TypeNode
772: {
773: if ($this->isCommonCallable) {
774: return new IdentifierTypeNode($this->isPure()->yes() ? 'pure-callable' : 'callable');
775: }
776:
777: $parameters = [];
778: foreach ($this->parameters as $parameter) {
779: $parameters[] = new CallableTypeParameterNode(
780: $parameter->getType()->toPhpDocNode(),
781: !$parameter->passedByReference()->no(),
782: $parameter->isVariadic(),
783: $parameter->getName() === '' ? '' : '$' . $parameter->getName(),
784: $parameter->isOptional(),
785: );
786: }
787:
788: $templateTags = [];
789: foreach ($this->templateTags as $templateName => $templateTag) {
790: $templateTags[] = new TemplateTagValueNode(
791: $templateName,
792: $templateTag->getBound()->toPhpDocNode(),
793: '',
794: );
795: }
796:
797: return new CallableTypeNode(
798: new IdentifierTypeNode($this->isPure->yes() ? 'pure-callable' : 'callable'),
799: $parameters,
800: $this->returnType->toPhpDocNode(),
801: $templateTags,
802: );
803: }
804:
805: public function hasTemplateOrLateResolvableType(): bool
806: {
807: foreach ($this->parameters as $parameter) {
808: if ($parameter->getType()->hasTemplateOrLateResolvableType()) {
809: return true;
810: }
811:
812: if (!$parameter instanceof ExtendedParameterReflection) {
813: continue;
814: }
815:
816: if ($parameter->getOutType() !== null && $parameter->getOutType()->hasTemplateOrLateResolvableType()) {
817: return true;
818: }
819: if ($parameter->getClosureThisType() !== null && $parameter->getClosureThisType()->hasTemplateOrLateResolvableType()) {
820: return true;
821: }
822: }
823:
824: return $this->getReturnType()->hasTemplateOrLateResolvableType();
825: }
826:
827: }
828: