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