1: <?php declare(strict_types = 1);
2:
3: namespace PHPStan\Type;
4:
5: use PHPStan\PhpDocParser\Ast\Type\ThisTypeNode;
6: use PHPStan\PhpDocParser\Ast\Type\TypeNode;
7: use PHPStan\Reflection\ClassReflection;
8: use PHPStan\Reflection\ReflectionProvider;
9: use PHPStan\Type\Constant\ConstantStringType;
10: use function sprintf;
11:
12: /** @api */
13: class ThisType extends StaticType
14: {
15:
16: /**
17: * @api
18: */
19: public function __construct(
20: ClassReflection $classReflection,
21: ?Type $subtractedType = null,
22: )
23: {
24: parent::__construct($classReflection, $subtractedType);
25: }
26:
27: public function changeBaseClass(ClassReflection $classReflection): StaticType
28: {
29: return new self($classReflection, $this->getSubtractedType());
30: }
31:
32: public function describe(VerbosityLevel $level): string
33: {
34: return sprintf('$this(%s)', $this->getStaticObjectType()->describe($level));
35: }
36:
37: public function isSuperTypeOf(Type $type): IsSuperTypeOfResult
38: {
39: if ($type instanceof self) {
40: return $this->getStaticObjectType()->isSuperTypeOf($type);
41: }
42:
43: if ($type instanceof CompoundType) {
44: return $type->isSubTypeOf($this);
45: }
46:
47: $parent = new parent($this->getClassReflection(), $this->getSubtractedType());
48:
49: return $parent->isSuperTypeOf($type)->and(IsSuperTypeOfResult::createMaybe());
50: }
51:
52: public function changeSubtractedType(?Type $subtractedType): Type
53: {
54: $type = parent::changeSubtractedType($subtractedType);
55: if ($type instanceof parent) {
56: return new self($type->getClassReflection(), $subtractedType);
57: }
58:
59: return $type;
60: }
61:
62: public function traverse(callable $cb): Type
63: {
64: $subtractedType = $this->getSubtractedType() !== null ? $cb($this->getSubtractedType()) : null;
65:
66: if ($subtractedType !== $this->getSubtractedType()) {
67: return new self(
68: $this->getClassReflection(),
69: $subtractedType,
70: );
71: }
72:
73: return $this;
74: }
75:
76: public function traverseSimultaneously(Type $right, callable $cb): Type
77: {
78: if ($this->getSubtractedType() === null) {
79: return $this;
80: }
81:
82: return new self($this->getClassReflection());
83: }
84:
85: public function toPhpDocNode(): TypeNode
86: {
87: return new ThisTypeNode();
88: }
89:
90: public function toClassConstantType(ReflectionProvider $reflectionProvider): Type
91: {
92: // `$this` in a `final` class is pinned to that one class, so
93: // `$this::class` collapses to its literal name. For non-final
94: // classes `$this` could still be a subclass, so fall back to the
95: // `class-string<$this>` projection from the parent.
96: $reflection = $this->getClassReflection();
97: if ($reflection->isFinalByKeyword()) {
98: return new ConstantStringType($reflection->getName(), true);
99: }
100:
101: return parent::toClassConstantType($reflectionProvider);
102: }
103:
104: }
105: