1: <?php declare(strict_types = 1);
2:
3: namespace PHPStan\Rules;
4:
5: use PHPStan\ShouldNotHappenException;
6: use function class_exists;
7: use function sprintf;
8:
9: /** @api */
10: class RuleErrorBuilder
11: {
12:
13: private const TYPE_MESSAGE = 1;
14: private const TYPE_LINE = 2;
15: private const TYPE_FILE = 4;
16: private const TYPE_TIP = 8;
17: private const TYPE_IDENTIFIER = 16;
18: private const TYPE_METADATA = 32;
19: private const TYPE_NON_IGNORABLE = 64;
20:
21: private int $type;
22:
23: /** @var mixed[] */
24: private array $properties;
25:
26: private function __construct(string $message)
27: {
28: $this->properties['message'] = $message;
29: $this->type = self::TYPE_MESSAGE;
30: }
31:
32: /**
33: * @return array<int, array{string, string|null, string|null, string|null}>
34: */
35: public static function getRuleErrorTypes(): array
36: {
37: return [
38: self::TYPE_MESSAGE => [
39: RuleError::class,
40: 'message',
41: 'string',
42: 'string',
43: ],
44: self::TYPE_LINE => [
45: LineRuleError::class,
46: 'line',
47: 'int',
48: 'int',
49: ],
50: self::TYPE_FILE => [
51: FileRuleError::class,
52: 'file',
53: 'string',
54: 'string',
55: ],
56: self::TYPE_TIP => [
57: TipRuleError::class,
58: 'tip',
59: 'string',
60: 'string',
61: ],
62: self::TYPE_IDENTIFIER => [
63: IdentifierRuleError::class,
64: 'identifier',
65: 'string',
66: 'string',
67: ],
68: self::TYPE_METADATA => [
69: MetadataRuleError::class,
70: 'metadata',
71: 'array',
72: 'mixed[]',
73: ],
74: self::TYPE_NON_IGNORABLE => [
75: NonIgnorableRuleError::class,
76: null,
77: null,
78: null,
79: ],
80: ];
81: }
82:
83: public static function message(string $message): self
84: {
85: return new self($message);
86: }
87:
88: public function line(int $line): self
89: {
90: $this->properties['line'] = $line;
91: $this->type |= self::TYPE_LINE;
92:
93: return $this;
94: }
95:
96: public function file(string $file): self
97: {
98: $this->properties['file'] = $file;
99: $this->type |= self::TYPE_FILE;
100:
101: return $this;
102: }
103:
104: public function tip(string $tip): self
105: {
106: $this->properties['tip'] = $tip;
107: $this->type |= self::TYPE_TIP;
108:
109: return $this;
110: }
111:
112: public function discoveringSymbolsTip(): self
113: {
114: return $this->tip('Learn more at https://phpstan.org/user-guide/discovering-symbols');
115: }
116:
117: public function identifier(string $identifier): self
118: {
119: $this->properties['identifier'] = $identifier;
120: $this->type |= self::TYPE_IDENTIFIER;
121:
122: return $this;
123: }
124:
125: /**
126: * @param mixed[] $metadata
127: */
128: public function metadata(array $metadata): self
129: {
130: $this->properties['metadata'] = $metadata;
131: $this->type |= self::TYPE_METADATA;
132:
133: return $this;
134: }
135:
136: public function nonIgnorable(): self
137: {
138: $this->type |= self::TYPE_NON_IGNORABLE;
139:
140: return $this;
141: }
142:
143: public function build(): RuleError
144: {
145: /** @var class-string<RuleError> $className */
146: $className = sprintf('PHPStan\\Rules\\RuleErrors\\RuleError%d', $this->type);
147: if (!class_exists($className)) {
148: throw new ShouldNotHappenException(sprintf('Class %s does not exist.', $className));
149: }
150:
151: $ruleError = new $className();
152: foreach ($this->properties as $propertyName => $value) {
153: $ruleError->{$propertyName} = $value;
154: }
155:
156: return $ruleError;
157: }
158:
159: }
160: