From dbe08a6feac7e98725fa8baec20c1309867e8f03 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Fri, 2 Apr 2021 11:30:30 +0200 Subject: [PATCH] Cache usual suspects --- src/Reflection/ClassReflection.php | 20 +++++- ...ackUnresolvedMethodPrototypeReflection.php | 15 ++++- ...kUnresolvedPropertyPrototypeReflection.php | 15 ++++- ...ypeUnresolvedMethodPrototypeReflection.php | 15 ++++- ...eUnresolvedPropertyPrototypeReflection.php | 15 ++++- ...ypeUnresolvedMethodPrototypeReflection.php | 15 ++++- ...eUnresolvedPropertyPrototypeReflection.php | 15 ++++- ...ypeUnresolvedMethodPrototypeReflection.php | 16 ++++- ...eUnresolvedPropertyPrototypeReflection.php | 15 ++++- src/Type/ObjectType.php | 65 +++++++++++++++---- 10 files changed, 176 insertions(+), 30 deletions(-) diff --git a/src/Reflection/ClassReflection.php b/src/Reflection/ClassReflection.php index 2ed3641c19..4952bb1938 100644 --- a/src/Reflection/ClassReflection.php +++ b/src/Reflection/ClassReflection.php @@ -92,6 +92,12 @@ class ClassReflection implements ReflectionWithFilename /** @var string|false|null */ private $reflectionDocComment; + /** @var \PHPStan\Reflection\ClassReflection[]|null */ + private ?array $cachedInterfaces = null; + + /** @var \PHPStan\Reflection\ClassReflection|false|null */ + private $cachedParentClass = null; + /** * @param \PHPStan\Reflection\ReflectionProvider $reflectionProvider * @param \PHPStan\Type\FileTypeMapper $fileTypeMapper @@ -178,10 +184,14 @@ public function getFileNameWithPhpDocs(): ?string */ public function getParentClass() { + if ($this->cachedParentClass !== null) { + return $this->cachedParentClass; + } + $parentClass = $this->reflection->getParentClass(); if ($parentClass === false) { - return false; + return $this->cachedParentClass = false; } $extendsTag = $this->getFirstExtendsTag(); @@ -210,6 +220,8 @@ public function getParentClass() ); } + $this->cachedParentClass = $parentReflection; + return $parentReflection; } @@ -570,6 +582,10 @@ public function getParents(): array */ public function getInterfaces(): array { + if ($this->cachedInterfaces !== null) { + return $this->cachedInterfaces; + } + $interfaces = []; $parent = $this->getParentClass(); @@ -638,6 +654,8 @@ public function getInterfaces(): array ); } + $this->cachedInterfaces = $interfaces; + return $interfaces; } diff --git a/src/Reflection/Type/CallbackUnresolvedMethodPrototypeReflection.php b/src/Reflection/Type/CallbackUnresolvedMethodPrototypeReflection.php index 34fb5aa892..70fae4e624 100644 --- a/src/Reflection/Type/CallbackUnresolvedMethodPrototypeReflection.php +++ b/src/Reflection/Type/CallbackUnresolvedMethodPrototypeReflection.php @@ -24,6 +24,10 @@ class CallbackUnresolvedMethodPrototypeReflection implements UnresolvedMethodPro /** @var callable(Type): Type */ private $transformStaticTypeCallback; + private ?MethodReflection $transformedMethod = null; + + private ?self $cachedDoNotResolveTemplateTypeMapToBounds = null; + /** * @param MethodReflection $methodReflection * @param ClassReflection $resolvedDeclaringClass @@ -45,7 +49,11 @@ public function __construct( public function doNotResolveTemplateTypeMapToBounds(): UnresolvedMethodPrototypeReflection { - return new self( + if ($this->cachedDoNotResolveTemplateTypeMapToBounds !== null) { + return $this->cachedDoNotResolveTemplateTypeMapToBounds; + } + + return $this->cachedDoNotResolveTemplateTypeMapToBounds = new self( $this->methodReflection, $this->resolvedDeclaringClass, false, @@ -60,9 +68,12 @@ public function getNakedMethod(): MethodReflection public function getTransformedMethod(): MethodReflection { + if ($this->transformedMethod !== null) { + return $this->transformedMethod; + } $templateTypeMap = $this->resolvedDeclaringClass->getActiveTemplateTypeMap(); - return new ResolvedMethodReflection( + return $this->transformedMethod = new ResolvedMethodReflection( $this->transformMethodWithStaticType($this->resolvedDeclaringClass, $this->methodReflection), $this->resolveTemplateTypeMapToBounds ? $templateTypeMap->resolveToBounds() : $templateTypeMap ); diff --git a/src/Reflection/Type/CallbackUnresolvedPropertyPrototypeReflection.php b/src/Reflection/Type/CallbackUnresolvedPropertyPrototypeReflection.php index d520e2e42f..903b081c58 100644 --- a/src/Reflection/Type/CallbackUnresolvedPropertyPrototypeReflection.php +++ b/src/Reflection/Type/CallbackUnresolvedPropertyPrototypeReflection.php @@ -20,6 +20,10 @@ class CallbackUnresolvedPropertyPrototypeReflection implements UnresolvedPropert /** @var callable(Type): Type */ private $transformStaticTypeCallback; + private ?PropertyReflection $transformedProperty = null; + + private ?self $cachedDoNotResolveTemplateTypeMapToBounds = null; + /** * @param PropertyReflection $propertyReflection * @param ClassReflection $resolvedDeclaringClass @@ -41,7 +45,11 @@ public function __construct( public function doNotResolveTemplateTypeMapToBounds(): UnresolvedPropertyPrototypeReflection { - return new self( + if ($this->cachedDoNotResolveTemplateTypeMapToBounds !== null) { + return $this->cachedDoNotResolveTemplateTypeMapToBounds; + } + + return $this->cachedDoNotResolveTemplateTypeMapToBounds = new self( $this->propertyReflection, $this->resolvedDeclaringClass, false, @@ -56,9 +64,12 @@ public function getNakedProperty(): PropertyReflection public function getTransformedProperty(): PropertyReflection { + if ($this->transformedProperty !== null) { + return $this->transformedProperty; + } $templateTypeMap = $this->resolvedDeclaringClass->getActiveTemplateTypeMap(); - return new ResolvedPropertyReflection( + return $this->transformedProperty = new ResolvedPropertyReflection( $this->transformPropertyWithStaticType($this->resolvedDeclaringClass, $this->propertyReflection), $this->resolveTemplateTypeMapToBounds ? $templateTypeMap->resolveToBounds() : $templateTypeMap ); diff --git a/src/Reflection/Type/CalledOnTypeUnresolvedMethodPrototypeReflection.php b/src/Reflection/Type/CalledOnTypeUnresolvedMethodPrototypeReflection.php index ffe8f0ef00..069592d59d 100644 --- a/src/Reflection/Type/CalledOnTypeUnresolvedMethodPrototypeReflection.php +++ b/src/Reflection/Type/CalledOnTypeUnresolvedMethodPrototypeReflection.php @@ -25,6 +25,10 @@ class CalledOnTypeUnresolvedMethodPrototypeReflection implements UnresolvedMetho private Type $calledOnType; + private ?MethodReflection $transformedMethod = null; + + private ?self $cachedDoNotResolveTemplateTypeMapToBounds = null; + public function __construct( MethodReflection $methodReflection, ClassReflection $resolvedDeclaringClass, @@ -40,7 +44,11 @@ public function __construct( public function doNotResolveTemplateTypeMapToBounds(): UnresolvedMethodPrototypeReflection { - return new self( + if ($this->cachedDoNotResolveTemplateTypeMapToBounds !== null) { + return $this->cachedDoNotResolveTemplateTypeMapToBounds; + } + + return $this->cachedDoNotResolveTemplateTypeMapToBounds = new self( $this->methodReflection, $this->resolvedDeclaringClass, false, @@ -55,9 +63,12 @@ public function getNakedMethod(): MethodReflection public function getTransformedMethod(): MethodReflection { + if ($this->transformedMethod !== null) { + return $this->transformedMethod; + } $templateTypeMap = $this->resolvedDeclaringClass->getActiveTemplateTypeMap(); - return new ResolvedMethodReflection( + return $this->transformedMethod = new ResolvedMethodReflection( $this->transformMethodWithStaticType($this->resolvedDeclaringClass, $this->methodReflection), $this->resolveTemplateTypeMapToBounds ? $templateTypeMap->resolveToBounds() : $templateTypeMap ); diff --git a/src/Reflection/Type/CalledOnTypeUnresolvedPropertyPrototypeReflection.php b/src/Reflection/Type/CalledOnTypeUnresolvedPropertyPrototypeReflection.php index 26ef42dcb5..59bd33d067 100644 --- a/src/Reflection/Type/CalledOnTypeUnresolvedPropertyPrototypeReflection.php +++ b/src/Reflection/Type/CalledOnTypeUnresolvedPropertyPrototypeReflection.php @@ -21,6 +21,10 @@ class CalledOnTypeUnresolvedPropertyPrototypeReflection implements UnresolvedPro private Type $fetchedOnType; + private ?PropertyReflection $transformedProperty = null; + + private ?self $cachedDoNotResolveTemplateTypeMapToBounds = null; + public function __construct( PropertyReflection $propertyReflection, ClassReflection $resolvedDeclaringClass, @@ -36,7 +40,11 @@ public function __construct( public function doNotResolveTemplateTypeMapToBounds(): UnresolvedPropertyPrototypeReflection { - return new self( + if ($this->cachedDoNotResolveTemplateTypeMapToBounds !== null) { + return $this->cachedDoNotResolveTemplateTypeMapToBounds; + } + + return $this->cachedDoNotResolveTemplateTypeMapToBounds = new self( $this->propertyReflection, $this->resolvedDeclaringClass, false, @@ -51,9 +59,12 @@ public function getNakedProperty(): PropertyReflection public function getTransformedProperty(): PropertyReflection { + if ($this->transformedProperty !== null) { + return $this->transformedProperty; + } $templateTypeMap = $this->resolvedDeclaringClass->getActiveTemplateTypeMap(); - return new ResolvedPropertyReflection( + return $this->transformedProperty = new ResolvedPropertyReflection( $this->transformPropertyWithStaticType($this->resolvedDeclaringClass, $this->propertyReflection), $this->resolveTemplateTypeMapToBounds ? $templateTypeMap->resolveToBounds() : $templateTypeMap ); diff --git a/src/Reflection/Type/IntersectionTypeUnresolvedMethodPrototypeReflection.php b/src/Reflection/Type/IntersectionTypeUnresolvedMethodPrototypeReflection.php index b1de327bdd..d8252da113 100644 --- a/src/Reflection/Type/IntersectionTypeUnresolvedMethodPrototypeReflection.php +++ b/src/Reflection/Type/IntersectionTypeUnresolvedMethodPrototypeReflection.php @@ -13,6 +13,10 @@ class IntersectionTypeUnresolvedMethodPrototypeReflection implements UnresolvedM /** @var UnresolvedMethodPrototypeReflection[] */ private array $methodPrototypes; + private ?MethodReflection $transformedMethod = null; + + private ?self $cachedDoNotResolveTemplateTypeMapToBounds = null; + /** * @param UnresolvedMethodPrototypeReflection[] $methodPrototypes */ @@ -27,7 +31,11 @@ public function __construct( public function doNotResolveTemplateTypeMapToBounds(): UnresolvedMethodPrototypeReflection { - return new self($this->methodName, array_map(static function (UnresolvedMethodPrototypeReflection $prototype): UnresolvedMethodPrototypeReflection { + if ($this->cachedDoNotResolveTemplateTypeMapToBounds !== null) { + return $this->cachedDoNotResolveTemplateTypeMapToBounds; + } + + return $this->cachedDoNotResolveTemplateTypeMapToBounds = new self($this->methodName, array_map(static function (UnresolvedMethodPrototypeReflection $prototype): UnresolvedMethodPrototypeReflection { return $prototype->doNotResolveTemplateTypeMapToBounds(); }, $this->methodPrototypes)); } @@ -39,11 +47,14 @@ public function getNakedMethod(): MethodReflection public function getTransformedMethod(): MethodReflection { + if ($this->transformedMethod !== null) { + return $this->transformedMethod; + } $methods = array_map(static function (UnresolvedMethodPrototypeReflection $prototype): MethodReflection { return $prototype->getTransformedMethod(); }, $this->methodPrototypes); - return new IntersectionTypeMethodReflection($this->methodName, $methods); + return $this->transformedMethod = new IntersectionTypeMethodReflection($this->methodName, $methods); } public function withCalledOnType(Type $type): UnresolvedMethodPrototypeReflection diff --git a/src/Reflection/Type/IntersectionTypeUnresolvedPropertyPrototypeReflection.php b/src/Reflection/Type/IntersectionTypeUnresolvedPropertyPrototypeReflection.php index 3bd1d3ea58..4be1d50ea0 100644 --- a/src/Reflection/Type/IntersectionTypeUnresolvedPropertyPrototypeReflection.php +++ b/src/Reflection/Type/IntersectionTypeUnresolvedPropertyPrototypeReflection.php @@ -13,6 +13,10 @@ class IntersectionTypeUnresolvedPropertyPrototypeReflection implements Unresolve /** @var UnresolvedPropertyPrototypeReflection[] */ private array $propertyPrototypes; + private ?PropertyReflection $transformedProperty = null; + + private ?self $cachedDoNotResolveTemplateTypeMapToBounds = null; + /** * @param UnresolvedPropertyPrototypeReflection[] $propertyPrototypes */ @@ -27,7 +31,11 @@ public function __construct( public function doNotResolveTemplateTypeMapToBounds(): UnresolvedPropertyPrototypeReflection { - return new self($this->propertyName, array_map(static function (UnresolvedPropertyPrototypeReflection $prototype): UnresolvedPropertyPrototypeReflection { + if ($this->cachedDoNotResolveTemplateTypeMapToBounds !== null) { + return $this->cachedDoNotResolveTemplateTypeMapToBounds; + } + + return $this->cachedDoNotResolveTemplateTypeMapToBounds = new self($this->propertyName, array_map(static function (UnresolvedPropertyPrototypeReflection $prototype): UnresolvedPropertyPrototypeReflection { return $prototype->doNotResolveTemplateTypeMapToBounds(); }, $this->propertyPrototypes)); } @@ -39,11 +47,14 @@ public function getNakedProperty(): PropertyReflection public function getTransformedProperty(): PropertyReflection { + if ($this->transformedProperty !== null) { + return $this->transformedProperty; + } $properties = array_map(static function (UnresolvedPropertyPrototypeReflection $prototype): PropertyReflection { return $prototype->getTransformedProperty(); }, $this->propertyPrototypes); - return new IntersectionTypePropertyReflection($properties); + return $this->transformedProperty = new IntersectionTypePropertyReflection($properties); } public function withFechedOnType(Type $type): UnresolvedPropertyPrototypeReflection diff --git a/src/Reflection/Type/UnionTypeUnresolvedMethodPrototypeReflection.php b/src/Reflection/Type/UnionTypeUnresolvedMethodPrototypeReflection.php index 9a36b8395f..3d8e295dc7 100644 --- a/src/Reflection/Type/UnionTypeUnresolvedMethodPrototypeReflection.php +++ b/src/Reflection/Type/UnionTypeUnresolvedMethodPrototypeReflection.php @@ -13,6 +13,10 @@ class UnionTypeUnresolvedMethodPrototypeReflection implements UnresolvedMethodPr /** @var UnresolvedMethodPrototypeReflection[] */ private array $methodPrototypes; + private ?MethodReflection $transformedMethod = null; + + private ?self $cachedDoNotResolveTemplateTypeMapToBounds = null; + /** * @param UnresolvedMethodPrototypeReflection[] $methodPrototypes */ @@ -27,7 +31,11 @@ public function __construct( public function doNotResolveTemplateTypeMapToBounds(): UnresolvedMethodPrototypeReflection { - return new self($this->methodName, array_map(static function (UnresolvedMethodPrototypeReflection $prototype): UnresolvedMethodPrototypeReflection { + if ($this->cachedDoNotResolveTemplateTypeMapToBounds !== null) { + return $this->cachedDoNotResolveTemplateTypeMapToBounds; + } + + return $this->cachedDoNotResolveTemplateTypeMapToBounds = new self($this->methodName, array_map(static function (UnresolvedMethodPrototypeReflection $prototype): UnresolvedMethodPrototypeReflection { return $prototype->doNotResolveTemplateTypeMapToBounds(); }, $this->methodPrototypes)); } @@ -39,11 +47,15 @@ public function getNakedMethod(): MethodReflection public function getTransformedMethod(): MethodReflection { + if ($this->transformedMethod !== null) { + return $this->transformedMethod; + } + $methods = array_map(static function (UnresolvedMethodPrototypeReflection $prototype): MethodReflection { return $prototype->getTransformedMethod(); }, $this->methodPrototypes); - return new UnionTypeMethodReflection($this->methodName, $methods); + return $this->transformedMethod = new UnionTypeMethodReflection($this->methodName, $methods); } public function withCalledOnType(Type $type): UnresolvedMethodPrototypeReflection diff --git a/src/Reflection/Type/UnionTypeUnresolvedPropertyPrototypeReflection.php b/src/Reflection/Type/UnionTypeUnresolvedPropertyPrototypeReflection.php index 8d36cbd246..ce86e932b2 100644 --- a/src/Reflection/Type/UnionTypeUnresolvedPropertyPrototypeReflection.php +++ b/src/Reflection/Type/UnionTypeUnresolvedPropertyPrototypeReflection.php @@ -13,6 +13,10 @@ class UnionTypeUnresolvedPropertyPrototypeReflection implements UnresolvedProper /** @var UnresolvedPropertyPrototypeReflection[] */ private array $propertyPrototypes; + private ?PropertyReflection $transformedProperty = null; + + private ?self $cachedDoNotResolveTemplateTypeMapToBounds = null; + /** * @param UnresolvedPropertyPrototypeReflection[] $propertyPrototypes */ @@ -27,7 +31,10 @@ public function __construct( public function doNotResolveTemplateTypeMapToBounds(): UnresolvedPropertyPrototypeReflection { - return new self($this->propertyName, array_map(static function (UnresolvedPropertyPrototypeReflection $prototype): UnresolvedPropertyPrototypeReflection { + if ($this->cachedDoNotResolveTemplateTypeMapToBounds !== null) { + return $this->cachedDoNotResolveTemplateTypeMapToBounds; + } + return $this->cachedDoNotResolveTemplateTypeMapToBounds = new self($this->propertyName, array_map(static function (UnresolvedPropertyPrototypeReflection $prototype): UnresolvedPropertyPrototypeReflection { return $prototype->doNotResolveTemplateTypeMapToBounds(); }, $this->propertyPrototypes)); } @@ -39,11 +46,15 @@ public function getNakedProperty(): PropertyReflection public function getTransformedProperty(): PropertyReflection { + if ($this->transformedProperty !== null) { + return $this->transformedProperty; + } + $methods = array_map(static function (UnresolvedPropertyPrototypeReflection $prototype): PropertyReflection { return $prototype->getTransformedProperty(); }, $this->propertyPrototypes); - return new UnionTypePropertyReflection($methods); + return $this->transformedProperty = new UnionTypePropertyReflection($methods); } public function withFechedOnType(Type $type): UnresolvedPropertyPrototypeReflection diff --git a/src/Type/ObjectType.php b/src/Type/ObjectType.php index 390295d511..42d0bd4f7a 100644 --- a/src/Type/ObjectType.php +++ b/src/Type/ObjectType.php @@ -41,6 +41,17 @@ class ObjectType implements TypeWithClassName, SubtractableType /** @var array> */ private static array $superTypes = []; + private ?self $cachedParent = null; + + /** @var self[]|null */ + private ?array $cachedInterfaces = null; + + /** @var array>> */ + private static array $methods = []; + + /** @var array>> */ + private static array $properties = []; + public function __construct( string $className, ?Type $subtractedType = null, @@ -98,6 +109,17 @@ public function getProperty(string $propertyName, ClassMemberAccessAnswerer $sco public function getUnresolvedPropertyPrototype(string $propertyName, ClassMemberAccessAnswerer $scope): UnresolvedPropertyPrototypeReflection { + if (!$scope->isInClass()) { + $canAccessProperty = 'no'; + } else { + $canAccessProperty = $scope->getClassReflection()->getName(); + } + $description = $this->describeCache(); + + if (isset(self::$properties[$description][$propertyName][$canAccessProperty])) { + return self::$properties[$description][$propertyName][$canAccessProperty]; + } + $nakedClassReflection = $this->getNakedClassReflection(); if ($nakedClassReflection === null) { throw new \PHPStan\Broker\ClassNotFoundException($this->className); @@ -112,6 +134,7 @@ public function getUnresolvedPropertyPrototype(string $propertyName, ClassMember } $property = $nakedClassReflection->getProperty($propertyName, $scope); + $ancestor = $this->getAncestorWithClassName($property->getDeclaringClass()->getName()); $resolvedClassReflection = null; if ($ancestor !== null) { @@ -124,7 +147,7 @@ public function getUnresolvedPropertyPrototype(string $propertyName, ClassMember $resolvedClassReflection = $property->getDeclaringClass(); } - return new CalledOnTypeUnresolvedPropertyPrototypeReflection( + return self::$properties[$description][$propertyName][$canAccessProperty] = new CalledOnTypeUnresolvedPropertyPrototypeReflection( $property, $resolvedClassReflection, true, @@ -185,14 +208,9 @@ public function accepts(Type $type, bool $strictTypes): TrinaryLogic public function isSuperTypeOf(Type $type): TrinaryLogic { - if (static::class === self::class) { - $thisDescription = $this->describeCache(); - } else { - $thisDescription = $this->describe(VerbosityLevel::cache()); - } + $thisDescription = $this->describeCache(); - if (get_class($type) === self::class) { - /** @var self $type */ + if ($type instanceof self) { $description = $type->describeCache(); } else { $description = $type->describe(VerbosityLevel::cache()); @@ -301,7 +319,7 @@ public function equals(Type $type): bool return $this->subtractedType->equals($type->subtractedType); } - protected function checkSubclassAcceptability(string $thatClass): TrinaryLogic + private function checkSubclassAcceptability(string $thatClass): TrinaryLogic { if ($this->className === $thatClass) { return TrinaryLogic::createYes(); @@ -358,6 +376,10 @@ function () use ($level): string { private function describeCache(): string { + if (static::class !== self::class) { + return $this->describe(VerbosityLevel::cache()); + } + $description = $this->className; if ($this->subtractedType !== null) { $description .= sprintf('~%s', $this->subtractedType->describe(VerbosityLevel::cache())); @@ -516,6 +538,16 @@ public function getMethod(string $methodName, ClassMemberAccessAnswerer $scope): public function getUnresolvedMethodPrototype(string $methodName, ClassMemberAccessAnswerer $scope): UnresolvedMethodPrototypeReflection { + if (!$scope->isInClass()) { + $canCallMethod = 'no'; + } else { + $canCallMethod = $scope->getClassReflection()->getName(); + } + $description = $this->describeCache(); + if (isset(self::$methods[$description][$methodName][$canCallMethod])) { + return self::$methods[$description][$methodName][$canCallMethod]; + } + $nakedClassReflection = $this->getNakedClassReflection(); if ($nakedClassReflection === null) { throw new \PHPStan\Broker\ClassNotFoundException($this->className); @@ -530,6 +562,7 @@ public function getUnresolvedMethodPrototype(string $methodName, ClassMemberAcce } $method = $nakedClassReflection->getMethod($methodName, $scope); + $ancestor = $this->getAncestorWithClassName($method->getDeclaringClass()->getName()); $resolvedClassReflection = null; if ($ancestor !== null) { @@ -542,7 +575,7 @@ public function getUnresolvedMethodPrototype(string $methodName, ClassMemberAcce $resolvedClassReflection = $method->getDeclaringClass(); } - return new CalledOnTypeUnresolvedMethodPrototypeReflection( + return self::$methods[$description][$methodName][$canCallMethod] = new CalledOnTypeUnresolvedMethodPrototypeReflection( $method, $resolvedClassReflection, true, @@ -982,6 +1015,9 @@ public function getAncestorWithClassName(string $className): ?TypeWithClassName private function getParent(): ?ObjectType { + if ($this->cachedParent !== null) { + return $this->cachedParent; + } $thisReflection = $this->getClassReflection(); if ($thisReflection === null) { return null; @@ -992,18 +1028,21 @@ private function getParent(): ?ObjectType return null; } - return self::createFromReflection($parentReflection); + return $this->cachedParent = self::createFromReflection($parentReflection); } /** @return ObjectType[] */ private function getInterfaces(): array { + if ($this->cachedInterfaces !== null) { + return $this->cachedInterfaces; + } $thisReflection = $this->getClassReflection(); if ($thisReflection === null) { - return []; + return $this->cachedInterfaces = []; } - return array_map(static function (ClassReflection $interfaceReflection): self { + return $this->cachedInterfaces = array_map(static function (ClassReflection $interfaceReflection): self { return self::createFromReflection($interfaceReflection); }, $thisReflection->getInterfaces()); }