` * * The type `\Cake\ORM\Association\BelongsTo&\App\Model\Table\UsersTable` is considered invalid (NeverType) by PHPStan */ class TableAssociationTypeNodeResolverExtension implements TypeNodeResolverExtension, TypeNodeResolverAwareExtension { private TypeNodeResolver $typeNodeResolver; /** * @var array */ protected array $associationTypes = [ BelongsTo::class, BelongsToMany::class, HasMany::class, HasOne::class, Association::class, ]; /** * @param \PHPStan\PhpDoc\TypeNodeResolver $typeNodeResolver * @return void */ public function setTypeNodeResolver(TypeNodeResolver $typeNodeResolver): void { $this->typeNodeResolver = $typeNodeResolver; } /** * @param \PHPStan\PhpDocParser\Ast\Type\TypeNode $typeNode * @param \PHPStan\Analyser\NameScope $nameScope * @return \PHPStan\Type\Type|null */ public function resolve(TypeNode $typeNode, NameScope $nameScope): ?Type { if (!$typeNode instanceof IntersectionTypeNode) { return null; } $types = $this->typeNodeResolver->resolveMultiple($typeNode->types, $nameScope); $config = [ 'association' => null, 'table' => null, ]; foreach ($types as $type) { if (!$type instanceof ObjectType) { continue; } $className = $type->getClassName(); if ($config['association'] === null && in_array($className, $this->associationTypes)) { $config['association'] = $type; } elseif ($config['table'] === null && str_ends_with($className, 'Table')) { $config['table'] = $type; } } if ($config['table'] && $config['association']) { return new GenericObjectType( $config['association']->getClassName(), [$config['table']] ); } return null; } }