1: <?php declare(strict_types = 1);
2:
3: namespace PHPStan\Type\Helper;
4:
5: use PHPStan\PhpDocParser\Ast\ConstExpr\QuoteAwareConstExprStringNode;
6: use PHPStan\PhpDocParser\Ast\Type\ConstTypeNode;
7: use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode;
8: use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
9: use PHPStan\PhpDocParser\Ast\Type\TypeNode;
10: use PHPStan\Type\CompoundType;
11: use PHPStan\Type\Generic\TemplateTypeVariance;
12: use PHPStan\Type\LateResolvableType;
13: use PHPStan\Type\Traits\LateResolvableTypeTrait;
14: use PHPStan\Type\Traits\NonGeneralizableTypeTrait;
15: use PHPStan\Type\Type;
16: use PHPStan\Type\TypeUtils;
17: use PHPStan\Type\VerbosityLevel;
18: use function sprintf;
19:
20: /** @api */
21: final class GetTemplateTypeType implements CompoundType, LateResolvableType
22: {
23:
24: use LateResolvableTypeTrait;
25: use NonGeneralizableTypeTrait;
26:
27: /**
28: * @param class-string $ancestorClassName
29: */
30: public function __construct(private Type $type, private string $ancestorClassName, private string $templateTypeName)
31: {
32: }
33:
34: public function getReferencedClasses(): array
35: {
36: return $this->type->getReferencedClasses();
37: }
38:
39: public function getReferencedTemplateTypes(TemplateTypeVariance $positionVariance): array
40: {
41: return $this->type->getReferencedTemplateTypes($positionVariance);
42: }
43:
44: public function equals(Type $type): bool
45: {
46: return $type instanceof self
47: && $this->type->equals($type->type);
48: }
49:
50: public function describe(VerbosityLevel $level): string
51: {
52: return sprintf('template-type<%s, %s, %s>', $this->type->describe($level), $this->ancestorClassName, $this->templateTypeName);
53: }
54:
55: public function isResolvable(): bool
56: {
57: return !TypeUtils::containsTemplateType($this->type);
58: }
59:
60: protected function getResult(): Type
61: {
62: return $this->type->getTemplateType($this->ancestorClassName, $this->templateTypeName);
63: }
64:
65: /**
66: * @param callable(Type): Type $cb
67: */
68: public function traverse(callable $cb): Type
69: {
70: $type = $cb($this->type);
71:
72: if ($this->type === $type) {
73: return $this;
74: }
75:
76: return new self($type, $this->ancestorClassName, $this->templateTypeName);
77: }
78:
79: public function traverseSimultaneously(Type $right, callable $cb): Type
80: {
81: if (!$right instanceof self) {
82: return $this;
83: }
84:
85: $type = $cb($this->type, $right->type);
86:
87: if ($this->type === $type) {
88: return $this;
89: }
90:
91: return new self($type, $this->ancestorClassName, $this->templateTypeName);
92: }
93:
94: public function toPhpDocNode(): TypeNode
95: {
96: return new GenericTypeNode(
97: new IdentifierTypeNode('template-type'),
98: [
99: $this->type->toPhpDocNode(),
100: new IdentifierTypeNode($this->ancestorClassName),
101: new ConstTypeNode(new QuoteAwareConstExprStringNode($this->templateTypeName, QuoteAwareConstExprStringNode::SINGLE_QUOTED)),
102: ],
103: );
104: }
105:
106: /**
107: * @param mixed[] $properties
108: */
109: public static function __set_state(array $properties): Type
110: {
111: return new self(
112: $properties['type'],
113: $properties['ancestorClassName'],
114: $properties['templateTypeName'],
115: );
116: }
117:
118: }
119: