nodeNameResolver = $nodeNameResolver; $this->traitNodeScopeCollector = $traitNodeScopeCollector; $this->reflectionProvider = $reflectionProvider; } /** * @required */ public function autowirePropertyFetchTypeResolver(NodeTypeResolver $nodeTypeResolver): void { $this->nodeTypeResolver = $nodeTypeResolver; } /** * @return array> */ public function getNodeClasses(): array { return [PropertyFetch::class]; } /** * @param PropertyFetch $node */ public function resolve(Node $node): Type { // compensate 3rd party non-analysed property reflection $vendorPropertyType = $this->getVendorPropertyFetchType($node); if (! $vendorPropertyType instanceof MixedType) { return $vendorPropertyType; } /** @var Scope|null $scope */ $scope = $node->getAttribute(AttributeKey::SCOPE); if (! $scope instanceof Scope) { $classNode = $node->getAttribute(AttributeKey::CLASS_NODE); if ($classNode instanceof Trait_) { /** @var string $traitName */ $traitName = $classNode->getAttribute(AttributeKey::CLASS_NAME); $scope = $this->traitNodeScopeCollector->getScopeForTraitAndNode($traitName, $node); } } if (! $scope instanceof Scope) { $classNode = $node->getAttribute(AttributeKey::CLASS_NODE); // fallback to class, since property fetches are not scoped by PHPStan if ($classNode instanceof ClassLike) { $scope = $classNode->getAttribute(AttributeKey::SCOPE); } if (! $scope instanceof Scope) { return new MixedType(); } } return $scope->getType($node); } private function getVendorPropertyFetchType(PropertyFetch $propertyFetch): Type { // 3rd party code $propertyName = $this->nodeNameResolver->getName($propertyFetch->name); if ($propertyName === null) { return new MixedType(); } $varType = $this->nodeTypeResolver->resolve($propertyFetch->var); if (! $varType instanceof ObjectType) { return new MixedType(); } if (! $this->reflectionProvider->hasClass($varType->getClassName())) { return new MixedType(); } $classReflection = $this->reflectionProvider->getClass($varType->getClassName()); if (! $classReflection->hasProperty($propertyName)) { return new MixedType(); } $propertyFetchScope = $propertyFetch->getAttribute(AttributeKey::SCOPE); $propertyReflection = $classReflection->getProperty($propertyName, $propertyFetchScope); return $propertyReflection->getReadableType(); } }