|
| 1 | +<?php declare(strict_types = 1); |
| 2 | + |
| 3 | +namespace PHPStan\Analyser; |
| 4 | + |
| 5 | +use PhpParser\Node\Expr; |
| 6 | +use PHPStan\Rules\Properties\FoundPropertyReflection; |
| 7 | +use PHPStan\ShouldNotHappenException; |
| 8 | +use PHPStan\TrinaryLogic; |
| 9 | +use PHPStan\Type\Type; |
| 10 | + |
| 11 | +/** |
| 12 | + * One resolved link of an isset/empty/?? chain. IssetabilityDescriptor::resolve() |
| 13 | + * walks the chain once and produces these (the expensive part: types, offset |
| 14 | + * existence, property reflection are resolved here and never again), so the engine |
| 15 | + * (IssetabilityResolution::isSet) and the rule (PHPStan\Rules\IssetCheck) read the |
| 16 | + * facts instead of re-walking and re-resolving. |
| 17 | + */ |
| 18 | +final class IssetabilityLinkInfo |
| 19 | +{ |
| 20 | + |
| 21 | + private const KIND_VARIABLE = 'variable'; |
| 22 | + private const KIND_OFFSET = 'offset'; |
| 23 | + private const KIND_PROPERTY = 'property'; |
| 24 | + private const KIND_LEAF = 'leaf'; |
| 25 | + |
| 26 | + private function __construct( |
| 27 | + private string $kind, |
| 28 | + private ?string $variableName = null, |
| 29 | + private ?TrinaryLogic $hasVariable = null, |
| 30 | + private ?TrinaryLogic $isOffsetAccessible = null, |
| 31 | + private ?TrinaryLogic $hasOffsetValue = null, |
| 32 | + private bool $hasExpressionTypeOfExpr = false, |
| 33 | + private ?Type $varType = null, |
| 34 | + private ?Type $dimType = null, |
| 35 | + private ?Type $valueType = null, |
| 36 | + private ?FoundPropertyReflection $propertyReflection = null, |
| 37 | + private ?Expr $propertyFetch = null, |
| 38 | + private bool $reflectionNative = false, |
| 39 | + private bool $hasNativeType = false, |
| 40 | + private ?TrinaryLogic $isVirtual = null, |
| 41 | + private ?Type $nativeType = null, |
| 42 | + private bool $hasExpressionTypeOfFetch = false, |
| 43 | + private bool $initializedThisProperty = false, |
| 44 | + private bool $nativeReflectionExists = false, |
| 45 | + private bool $nativeIsPromoted = false, |
| 46 | + private bool $nativeIsReadOnly = false, |
| 47 | + private bool $nativeIsHooked = false, |
| 48 | + private bool $nativeHasDefaultValue = false, |
| 49 | + private ?Expr $leafExpr = null, |
| 50 | + private bool $leafIsNullsafePropertyFetch = false, |
| 51 | + ) |
| 52 | + { |
| 53 | + } |
| 54 | + |
| 55 | + public static function variable(string $variableName, TrinaryLogic $hasVariable, Type $valueType): self |
| 56 | + { |
| 57 | + return new self(self::KIND_VARIABLE, variableName: $variableName, hasVariable: $hasVariable, valueType: $valueType); |
| 58 | + } |
| 59 | + |
| 60 | + public static function offset(TrinaryLogic $isOffsetAccessible, TrinaryLogic $hasOffsetValue, bool $hasExpressionTypeOfExpr, Type $varType, Type $dimType, Type $valueType): self |
| 61 | + { |
| 62 | + return new self( |
| 63 | + self::KIND_OFFSET, |
| 64 | + isOffsetAccessible: $isOffsetAccessible, |
| 65 | + hasOffsetValue: $hasOffsetValue, |
| 66 | + hasExpressionTypeOfExpr: $hasExpressionTypeOfExpr, |
| 67 | + varType: $varType, |
| 68 | + dimType: $dimType, |
| 69 | + valueType: $valueType, |
| 70 | + ); |
| 71 | + } |
| 72 | + |
| 73 | + public static function property( |
| 74 | + ?FoundPropertyReflection $propertyReflection, |
| 75 | + Expr $propertyFetch, |
| 76 | + bool $reflectionNative, |
| 77 | + bool $hasNativeType, |
| 78 | + TrinaryLogic $isVirtual, |
| 79 | + Type $writableType, |
| 80 | + Type $nativeType, |
| 81 | + bool $hasExpressionTypeOfFetch, |
| 82 | + bool $initializedThisProperty, |
| 83 | + bool $nativeReflectionExists, |
| 84 | + bool $nativeIsPromoted, |
| 85 | + bool $nativeIsReadOnly, |
| 86 | + bool $nativeIsHooked, |
| 87 | + bool $nativeHasDefaultValue, |
| 88 | + ): self |
| 89 | + { |
| 90 | + return new self( |
| 91 | + self::KIND_PROPERTY, |
| 92 | + valueType: $writableType, |
| 93 | + propertyReflection: $propertyReflection, |
| 94 | + propertyFetch: $propertyFetch, |
| 95 | + reflectionNative: $reflectionNative, |
| 96 | + hasNativeType: $hasNativeType, |
| 97 | + isVirtual: $isVirtual, |
| 98 | + nativeType: $nativeType, |
| 99 | + hasExpressionTypeOfFetch: $hasExpressionTypeOfFetch, |
| 100 | + initializedThisProperty: $initializedThisProperty, |
| 101 | + nativeReflectionExists: $nativeReflectionExists, |
| 102 | + nativeIsPromoted: $nativeIsPromoted, |
| 103 | + nativeIsReadOnly: $nativeIsReadOnly, |
| 104 | + nativeIsHooked: $nativeIsHooked, |
| 105 | + nativeHasDefaultValue: $nativeHasDefaultValue, |
| 106 | + ); |
| 107 | + } |
| 108 | + |
| 109 | + public static function leaf(Type $valueType, Expr $leafExpr, bool $leafIsNullsafePropertyFetch): self |
| 110 | + { |
| 111 | + return new self(self::KIND_LEAF, valueType: $valueType, leafExpr: $leafExpr, leafIsNullsafePropertyFetch: $leafIsNullsafePropertyFetch); |
| 112 | + } |
| 113 | + |
| 114 | + public function isVariable(): bool |
| 115 | + { |
| 116 | + return $this->kind === self::KIND_VARIABLE; |
| 117 | + } |
| 118 | + |
| 119 | + public function isOffset(): bool |
| 120 | + { |
| 121 | + return $this->kind === self::KIND_OFFSET; |
| 122 | + } |
| 123 | + |
| 124 | + public function isProperty(): bool |
| 125 | + { |
| 126 | + return $this->kind === self::KIND_PROPERTY; |
| 127 | + } |
| 128 | + |
| 129 | + public function getVariableName(): string |
| 130 | + { |
| 131 | + if ($this->variableName === null) { |
| 132 | + throw new ShouldNotHappenException(); |
| 133 | + } |
| 134 | + |
| 135 | + return $this->variableName; |
| 136 | + } |
| 137 | + |
| 138 | + public function getHasVariable(): TrinaryLogic |
| 139 | + { |
| 140 | + if ($this->hasVariable === null) { |
| 141 | + throw new ShouldNotHappenException(); |
| 142 | + } |
| 143 | + |
| 144 | + return $this->hasVariable; |
| 145 | + } |
| 146 | + |
| 147 | + /** The type the operator's callback inspects: variable type, offset value type, property writable type, or leaf type. */ |
| 148 | + public function getValueType(): Type |
| 149 | + { |
| 150 | + if ($this->valueType === null) { |
| 151 | + throw new ShouldNotHappenException(); |
| 152 | + } |
| 153 | + |
| 154 | + return $this->valueType; |
| 155 | + } |
| 156 | + |
| 157 | + public function getIsOffsetAccessible(): TrinaryLogic |
| 158 | + { |
| 159 | + if ($this->isOffsetAccessible === null) { |
| 160 | + throw new ShouldNotHappenException(); |
| 161 | + } |
| 162 | + |
| 163 | + return $this->isOffsetAccessible; |
| 164 | + } |
| 165 | + |
| 166 | + public function getHasOffsetValue(): TrinaryLogic |
| 167 | + { |
| 168 | + if ($this->hasOffsetValue === null) { |
| 169 | + throw new ShouldNotHappenException(); |
| 170 | + } |
| 171 | + |
| 172 | + return $this->hasOffsetValue; |
| 173 | + } |
| 174 | + |
| 175 | + public function hasExpressionTypeOfExpr(): bool |
| 176 | + { |
| 177 | + return $this->hasExpressionTypeOfExpr; |
| 178 | + } |
| 179 | + |
| 180 | + public function getVarType(): Type |
| 181 | + { |
| 182 | + if ($this->varType === null) { |
| 183 | + throw new ShouldNotHappenException(); |
| 184 | + } |
| 185 | + |
| 186 | + return $this->varType; |
| 187 | + } |
| 188 | + |
| 189 | + public function getDimType(): Type |
| 190 | + { |
| 191 | + if ($this->dimType === null) { |
| 192 | + throw new ShouldNotHappenException(); |
| 193 | + } |
| 194 | + |
| 195 | + return $this->dimType; |
| 196 | + } |
| 197 | + |
| 198 | + public function getPropertyReflection(): ?FoundPropertyReflection |
| 199 | + { |
| 200 | + return $this->propertyReflection; |
| 201 | + } |
| 202 | + |
| 203 | + /** |
| 204 | + * @return Expr\PropertyFetch|Expr\StaticPropertyFetch |
| 205 | + */ |
| 206 | + public function getPropertyFetch(): Expr |
| 207 | + { |
| 208 | + if (!$this->propertyFetch instanceof Expr\PropertyFetch && !$this->propertyFetch instanceof Expr\StaticPropertyFetch) { |
| 209 | + throw new ShouldNotHappenException(); |
| 210 | + } |
| 211 | + |
| 212 | + return $this->propertyFetch; |
| 213 | + } |
| 214 | + |
| 215 | + public function isReflectionNative(): bool |
| 216 | + { |
| 217 | + return $this->reflectionNative; |
| 218 | + } |
| 219 | + |
| 220 | + public function hasNativeType(): bool |
| 221 | + { |
| 222 | + return $this->hasNativeType; |
| 223 | + } |
| 224 | + |
| 225 | + public function isVirtual(): TrinaryLogic |
| 226 | + { |
| 227 | + if ($this->isVirtual === null) { |
| 228 | + throw new ShouldNotHappenException(); |
| 229 | + } |
| 230 | + |
| 231 | + return $this->isVirtual; |
| 232 | + } |
| 233 | + |
| 234 | + public function getNativeType(): Type |
| 235 | + { |
| 236 | + if ($this->nativeType === null) { |
| 237 | + throw new ShouldNotHappenException(); |
| 238 | + } |
| 239 | + |
| 240 | + return $this->nativeType; |
| 241 | + } |
| 242 | + |
| 243 | + public function hasExpressionTypeOfFetch(): bool |
| 244 | + { |
| 245 | + return $this->hasExpressionTypeOfFetch; |
| 246 | + } |
| 247 | + |
| 248 | + public function isInitializedThisProperty(): bool |
| 249 | + { |
| 250 | + return $this->initializedThisProperty; |
| 251 | + } |
| 252 | + |
| 253 | + public function nativeReflectionExists(): bool |
| 254 | + { |
| 255 | + return $this->nativeReflectionExists; |
| 256 | + } |
| 257 | + |
| 258 | + public function nativeIsPromoted(): bool |
| 259 | + { |
| 260 | + return $this->nativeIsPromoted; |
| 261 | + } |
| 262 | + |
| 263 | + public function nativeIsReadOnly(): bool |
| 264 | + { |
| 265 | + return $this->nativeIsReadOnly; |
| 266 | + } |
| 267 | + |
| 268 | + public function nativeIsHooked(): bool |
| 269 | + { |
| 270 | + return $this->nativeIsHooked; |
| 271 | + } |
| 272 | + |
| 273 | + public function nativeHasDefaultValue(): bool |
| 274 | + { |
| 275 | + return $this->nativeHasDefaultValue; |
| 276 | + } |
| 277 | + |
| 278 | + public function getLeafExpr(): Expr |
| 279 | + { |
| 280 | + if ($this->leafExpr === null) { |
| 281 | + throw new ShouldNotHappenException(); |
| 282 | + } |
| 283 | + |
| 284 | + return $this->leafExpr; |
| 285 | + } |
| 286 | + |
| 287 | + public function leafIsNullsafePropertyFetch(): bool |
| 288 | + { |
| 289 | + return $this->leafIsNullsafePropertyFetch; |
| 290 | + } |
| 291 | + |
| 292 | +} |
0 commit comments