1: <?php declare(strict_types = 1);
2:
3: namespace PHPStan\Reflection;
4:
5: use function count;
6: use function in_array;
7:
8: /**
9: * Describes which constants a function/method parameter accepts.
10: *
11: * Parameters are either 'single' (exactly one constant, e.g. `array_unique($flags)`)
12: * or 'bitmask' (constants combinable with `|`, e.g. `json_encode($flags)`).
13: * Bitmask parameters may have exclusive groups — subsets of constants
14: * that are mutually exclusive even within a bitmask.
15: *
16: * Populated from resources/constantToFunctionParameterMap.php and
17: * available via ExtendedParameterReflection::getAllowedConstants().
18: *
19: * @api
20: */
21: final class ParameterAllowedConstants
22: {
23:
24: /**
25: * @param 'single'|'bitmask' $type
26: * @param list<string> $constants
27: * @param list<list<string>> $exclusiveGroups
28: */
29: public function __construct(
30: private string $type,
31: private array $constants,
32: private array $exclusiveGroups,
33: )
34: {
35: }
36:
37: public function isBitmask(): bool
38: {
39: return $this->type === 'bitmask';
40: }
41:
42: /**
43: * @return list<list<string>>
44: */
45: public function getExclusiveGroups(): array
46: {
47: return $this->exclusiveGroups;
48: }
49:
50: public function equals(self $other): bool
51: {
52: return $this->type === $other->type
53: && $this->constants === $other->constants
54: && $this->exclusiveGroups === $other->exclusiveGroups;
55: }
56:
57: /**
58: * @param list<ConstantReflection> $constants
59: */
60: public function check(array $constants): AllowedConstantsResult
61: {
62: $bitmaskNotAllowed = !$this->isBitmask() && count($constants) > 1;
63:
64: $disallowed = [];
65: $names = [];
66:
67: foreach ($constants as $constant) {
68: if ($constant->isBuiltin()->no()) {
69: continue;
70: }
71:
72: $name = $constant->describe();
73: $names[] = $name;
74:
75: if (in_array($name, $this->constants, true)) {
76: continue;
77: }
78:
79: $disallowed[] = $constant;
80: }
81:
82: $violated = [];
83: if ($this->isBitmask()) {
84: foreach ($this->exclusiveGroups as $group) {
85: $matched = [];
86: foreach ($names as $name) {
87: if (!in_array($name, $group, true)) {
88: continue;
89: }
90:
91: $matched[] = $name;
92: }
93:
94: if (count($matched) < 2) {
95: continue;
96: }
97:
98: $violated[] = $matched;
99: }
100: }
101:
102: return new AllowedConstantsResult($disallowed, $violated, $bitmaskNotAllowed);
103: }
104:
105: }
106: