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: /**
51: * @param list<ConstantReflection> $constants
52: */
53: public function check(array $constants): AllowedConstantsResult
54: {
55: $bitmaskNotAllowed = !$this->isBitmask() && count($constants) > 1;
56:
57: $disallowed = [];
58: $names = [];
59:
60: foreach ($constants as $constant) {
61: if ($constant->isBuiltin()->no()) {
62: continue;
63: }
64:
65: $name = $constant->describe();
66: $names[] = $name;
67:
68: if (in_array($name, $this->constants, true)) {
69: continue;
70: }
71:
72: $disallowed[] = $constant;
73: }
74:
75: $violated = [];
76: if ($this->isBitmask()) {
77: foreach ($this->exclusiveGroups as $group) {
78: $matched = [];
79: foreach ($names as $name) {
80: if (!in_array($name, $group, true)) {
81: continue;
82: }
83:
84: $matched[] = $name;
85: }
86:
87: if (count($matched) < 2) {
88: continue;
89: }
90:
91: $violated[] = $matched;
92: }
93: }
94:
95: return new AllowedConstantsResult($disallowed, $violated, $bitmaskNotAllowed);
96: }
97:
98: }
99: