|   1:  | <?php declare(strict_types = 1); | 
|   2:  |  | 
|   3:  | namespace PHPStan\Type; | 
|   4:  |  | 
|   5:  | use PHPStan\Type\Accessory\AccessoryArrayListType; | 
|   6:  | use PHPStan\Type\Accessory\AccessoryLiteralStringType; | 
|   7:  | use PHPStan\Type\Accessory\AccessoryLowercaseStringType; | 
|   8:  | use PHPStan\Type\Accessory\AccessoryNonEmptyStringType; | 
|   9:  | use PHPStan\Type\Accessory\AccessoryNonFalsyStringType; | 
|  10:  | use PHPStan\Type\Accessory\AccessoryNumericStringType; | 
|  11:  | use PHPStan\Type\Accessory\AccessoryUppercaseStringType; | 
|  12:  | use PHPStan\Type\Accessory\NonEmptyArrayType; | 
|  13:  | use PHPStan\Type\Generic\GenericObjectType; | 
|  14:  | use PHPStan\Type\Generic\TemplateType; | 
|  15:  |  | 
|  16:  | final class VerbosityLevel | 
|  17:  | { | 
|  18:  |  | 
|  19:  | 	private const TYPE_ONLY = 1; | 
|  20:  | 	private const VALUE = 2; | 
|  21:  | 	private const PRECISE = 3; | 
|  22:  | 	private const CACHE = 4; | 
|  23:  |  | 
|  24:  | 	 | 
|  25:  | 	private static array $registry; | 
|  26:  |  | 
|  27:  | 	 | 
|  28:  |  | 
|  29:  |  | 
|  30:  | 	private function __construct(private int $value) | 
|  31:  | 	{ | 
|  32:  | 	} | 
|  33:  |  | 
|  34:  | 	 | 
|  35:  |  | 
|  36:  |  | 
|  37:  | 	private static function create(int $value): self | 
|  38:  | 	{ | 
|  39:  | 		self::$registry[$value] ??= new self($value); | 
|  40:  | 		return self::$registry[$value]; | 
|  41:  | 	} | 
|  42:  |  | 
|  43:  | 	 | 
|  44:  | 	public function getLevelValue(): int | 
|  45:  | 	{ | 
|  46:  | 		return $this->value; | 
|  47:  | 	} | 
|  48:  |  | 
|  49:  | 	 | 
|  50:  | 	public static function typeOnly(): self | 
|  51:  | 	{ | 
|  52:  | 		return self::create(self::TYPE_ONLY); | 
|  53:  | 	} | 
|  54:  |  | 
|  55:  | 	 | 
|  56:  | 	public static function value(): self | 
|  57:  | 	{ | 
|  58:  | 		return self::create(self::VALUE); | 
|  59:  | 	} | 
|  60:  |  | 
|  61:  | 	 | 
|  62:  | 	public static function precise(): self | 
|  63:  | 	{ | 
|  64:  | 		return self::create(self::PRECISE); | 
|  65:  | 	} | 
|  66:  |  | 
|  67:  | 	 | 
|  68:  | 	public static function cache(): self | 
|  69:  | 	{ | 
|  70:  | 		return self::create(self::CACHE); | 
|  71:  | 	} | 
|  72:  |  | 
|  73:  | 	public function isTypeOnly(): bool | 
|  74:  | 	{ | 
|  75:  | 		return $this->value === self::TYPE_ONLY; | 
|  76:  | 	} | 
|  77:  |  | 
|  78:  | 	public function isValue(): bool | 
|  79:  | 	{ | 
|  80:  | 		return $this->value === self::VALUE; | 
|  81:  | 	} | 
|  82:  |  | 
|  83:  | 	public function isPrecise(): bool | 
|  84:  | 	{ | 
|  85:  | 		return $this->value === self::PRECISE; | 
|  86:  | 	} | 
|  87:  |  | 
|  88:  | 	 | 
|  89:  | 	public static function getRecommendedLevelByType(Type $acceptingType, ?Type $acceptedType = null): self | 
|  90:  | 	{ | 
|  91:  | 		$moreVerboseCallback = static function (Type $type, callable $traverse) use (&$moreVerbose, &$veryVerbose): Type { | 
|  92:  | 			if ($type->isCallable()->yes()) { | 
|  93:  | 				$moreVerbose = true; | 
|  94:  | 				return $type; | 
|  95:  | 			} | 
|  96:  | 			if ($type->isConstantValue()->yes() && $type->isNull()->no()) { | 
|  97:  | 				$moreVerbose = true; | 
|  98:  | 				return $type; | 
|  99:  | 			} | 
| 100:  | 			if ( | 
| 101:  | 				 | 
| 102:  | 				$type instanceof AccessoryNonEmptyStringType | 
| 103:  | 				|| $type instanceof AccessoryNonFalsyStringType | 
| 104:  | 				|| $type instanceof AccessoryLiteralStringType | 
| 105:  | 				|| $type instanceof AccessoryNumericStringType | 
| 106:  | 				|| $type instanceof NonEmptyArrayType | 
| 107:  | 				|| $type instanceof AccessoryArrayListType | 
| 108:  | 			) { | 
| 109:  | 				$moreVerbose = true; | 
| 110:  | 				return $type; | 
| 111:  | 			} | 
| 112:  | 			if ( | 
| 113:  | 				$type instanceof AccessoryLowercaseStringType | 
| 114:  | 				|| $type instanceof AccessoryUppercaseStringType | 
| 115:  | 			) { | 
| 116:  | 				$moreVerbose = true; | 
| 117:  | 				$veryVerbose = true; | 
| 118:  | 				return $type; | 
| 119:  | 			} | 
| 120:  | 			if ($type instanceof IntegerRangeType) { | 
| 121:  | 				$moreVerbose = true; | 
| 122:  | 				return $type; | 
| 123:  | 			} | 
| 124:  | 			return $traverse($type); | 
| 125:  | 		}; | 
| 126:  |  | 
| 127:  | 		 | 
| 128:  | 		$moreVerbose = false; | 
| 129:  | 		 | 
| 130:  | 		$veryVerbose = false; | 
| 131:  | 		TypeTraverser::map($acceptingType, $moreVerboseCallback); | 
| 132:  |  | 
| 133:  | 		if ($veryVerbose) { | 
| 134:  | 			return self::precise(); | 
| 135:  | 		} | 
| 136:  |  | 
| 137:  | 		if ($moreVerbose) { | 
| 138:  | 			$verbosity = self::value(); | 
| 139:  | 		} | 
| 140:  |  | 
| 141:  | 		if ($acceptedType === null) { | 
| 142:  | 			return $verbosity ?? self::typeOnly(); | 
| 143:  | 		} | 
| 144:  |  | 
| 145:  | 		$containsInvariantTemplateType = false; | 
| 146:  | 		TypeTraverser::map($acceptingType, static function (Type $type, callable $traverse) use (&$containsInvariantTemplateType): Type { | 
| 147:  | 			if ($type instanceof GenericObjectType) { | 
| 148:  | 				$reflection = $type->getClassReflection(); | 
| 149:  | 				if ($reflection !== null) { | 
| 150:  | 					$templateTypeMap = $reflection->getTemplateTypeMap(); | 
| 151:  | 					foreach ($templateTypeMap->getTypes() as $templateType) { | 
| 152:  | 						if (!$templateType instanceof TemplateType) { | 
| 153:  | 							continue; | 
| 154:  | 						} | 
| 155:  |  | 
| 156:  | 						if (!$templateType->getVariance()->invariant()) { | 
| 157:  | 							continue; | 
| 158:  | 						} | 
| 159:  |  | 
| 160:  | 						$containsInvariantTemplateType = true; | 
| 161:  | 						return $type; | 
| 162:  | 					} | 
| 163:  | 				} | 
| 164:  | 			} | 
| 165:  |  | 
| 166:  | 			return $traverse($type); | 
| 167:  | 		}); | 
| 168:  |  | 
| 169:  | 		if (!$containsInvariantTemplateType) { | 
| 170:  | 			return $verbosity ?? self::typeOnly(); | 
| 171:  | 		} | 
| 172:  |  | 
| 173:  | 		 | 
| 174:  | 		$moreVerbose = false; | 
| 175:  | 		 | 
| 176:  | 		$veryVerbose = false; | 
| 177:  | 		TypeTraverser::map($acceptedType, $moreVerboseCallback); | 
| 178:  |  | 
| 179:  | 		if ($veryVerbose) { | 
| 180:  | 			return self::precise(); | 
| 181:  | 		} | 
| 182:  |  | 
| 183:  | 		return $moreVerbose ? self::value() : $verbosity ?? self::typeOnly(); | 
| 184:  | 	} | 
| 185:  |  | 
| 186:  | 	 | 
| 187:  |  | 
| 188:  |  | 
| 189:  |  | 
| 190:  |  | 
| 191:  |  | 
| 192:  | 	public function handle( | 
| 193:  | 		callable $typeOnlyCallback, | 
| 194:  | 		callable $valueCallback, | 
| 195:  | 		?callable $preciseCallback = null, | 
| 196:  | 		?callable $cacheCallback = null, | 
| 197:  | 	): string | 
| 198:  | 	{ | 
| 199:  | 		if ($this->value === self::TYPE_ONLY) { | 
| 200:  | 			return $typeOnlyCallback(); | 
| 201:  | 		} | 
| 202:  |  | 
| 203:  | 		if ($this->value === self::VALUE) { | 
| 204:  | 			return $valueCallback(); | 
| 205:  | 		} | 
| 206:  |  | 
| 207:  | 		if ($this->value === self::PRECISE) { | 
| 208:  | 			if ($preciseCallback !== null) { | 
| 209:  | 				return $preciseCallback(); | 
| 210:  | 			} | 
| 211:  |  | 
| 212:  | 			return $valueCallback(); | 
| 213:  | 		} | 
| 214:  |  | 
| 215:  | 		if ($cacheCallback !== null) { | 
| 216:  | 			return $cacheCallback(); | 
| 217:  | 		} | 
| 218:  |  | 
| 219:  | 		if ($preciseCallback !== null) { | 
| 220:  | 			return $preciseCallback(); | 
| 221:  | 		} | 
| 222:  |  | 
| 223:  | 		return $valueCallback(); | 
| 224:  | 	} | 
| 225:  |  | 
| 226:  | } | 
| 227:  |  |