1: <?php declare(strict_types = 1);
2:
3: namespace PHPStan\Type;
4:
5: use PHPStan\Turbo\ReferencedByTurboExtension;
6:
7: /**
8: * Controls how aggressively Type::generalize() widens a type.
9: *
10: * Generalization is the process of widening a specific type to a broader one.
11: * For example, generalizing ConstantStringType('hello') yields StringType.
12: * This is used when PHPStan needs to merge types across loop iterations or
13: * branches where tracking precise constant values is impractical.
14: *
15: * Three levels of precision:
16: * - **lessSpecific**: Aggressive generalization — constant values become their
17: * general type (e.g. 'hello' → string, array{foo: int} → array<string, int>)
18: * - **moreSpecific**: Preserves more detail — e.g. non-empty-string stays
19: * non-empty-string instead of widening to string
20: * - **templateArgument**: Used when generalizing template type arguments,
21: * preserving template-specific structure
22: *
23: * Used as a parameter to Type::generalize():
24: *
25: * $type->generalize(GeneralizePrecision::lessSpecific())
26: */
27: #[ReferencedByTurboExtension(key: 'generalizePrecision')]
28: final class GeneralizePrecision
29: {
30:
31: private const LESS_SPECIFIC = 1;
32: private const MORE_SPECIFIC = 2;
33: private const TEMPLATE_ARGUMENT = 3;
34:
35: /** @var self[] */
36: private static array $registry;
37:
38: private function __construct(private int $value)
39: {
40: }
41:
42: private static function create(int $value): self
43: {
44: self::$registry[$value] ??= new self($value);
45: return self::$registry[$value];
46: }
47:
48: /** @api */
49: public static function lessSpecific(): self
50: {
51: return self::create(self::LESS_SPECIFIC);
52: }
53:
54: /** @api */
55: public static function moreSpecific(): self
56: {
57: return self::create(self::MORE_SPECIFIC);
58: }
59:
60: /** @api */
61: public static function templateArgument(): self
62: {
63: return self::create(self::TEMPLATE_ARGUMENT);
64: }
65:
66: public function isMoreSpecific(): bool
67: {
68: return $this->value === self::MORE_SPECIFIC;
69: }
70:
71: public function isTemplateArgument(): bool
72: {
73: return $this->value === self::TEMPLATE_ARGUMENT;
74: }
75:
76: }
77: