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: * ```php
26: * $type->generalize(GeneralizePrecision::lessSpecific())
27: * ```
28: */
29: #[ReferencedByTurboExtension(key: 'generalizePrecision')]
30: final class GeneralizePrecision
31: {
32:
33: private const LESS_SPECIFIC = 1;
34: private const MORE_SPECIFIC = 2;
35: private const TEMPLATE_ARGUMENT = 3;
36:
37: /** @var self[] */
38: private static array $registry;
39:
40: private function __construct(private int $value)
41: {
42: }
43:
44: private static function create(int $value): self
45: {
46: self::$registry[$value] ??= new self($value);
47: return self::$registry[$value];
48: }
49:
50: /** @api */
51: public static function lessSpecific(): self
52: {
53: return self::create(self::LESS_SPECIFIC);
54: }
55:
56: /** @api */
57: public static function moreSpecific(): self
58: {
59: return self::create(self::MORE_SPECIFIC);
60: }
61:
62: /** @api */
63: public static function templateArgument(): self
64: {
65: return self::create(self::TEMPLATE_ARGUMENT);
66: }
67:
68: public function isMoreSpecific(): bool
69: {
70: return $this->value === self::MORE_SPECIFIC;
71: }
72:
73: public function isTemplateArgument(): bool
74: {
75: return $this->value === self::TEMPLATE_ARGUMENT;
76: }
77:
78: }
79: