Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
63.93% |
39 / 61 |
|
38.46% |
5 / 13 |
CRAP | |
0.00% |
0 / 1 |
OptionalField | |
63.93% |
39 / 61 |
|
38.46% |
5 / 13 |
115.06 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
hasDefaultValue | |
66.67% |
2 / 3 |
|
0.00% |
0 / 1 |
4.59 | |||
getDefaultValue | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
12 | |||
allowsNull | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
isRequired | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
3 | |||
isField | |
66.67% |
2 / 3 |
|
0.00% |
0 / 1 |
3.33 | |||
appliesToContext | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getFieldPriority | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
setValue | |
75.00% |
3 / 4 |
|
0.00% |
0 / 1 |
3.14 | |||
markValueAsMissing | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
12 | |||
getValue | |
40.00% |
2 / 5 |
|
0.00% |
0 / 1 |
4.94 | |||
getTypehint | |
78.26% |
18 / 23 |
|
0.00% |
0 / 1 |
11.03 | |||
getAttributes | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 |
1 | <?php |
2 | namespace Apie\Core\Metadata\Fields; |
3 | |
4 | use Apie\Core\Context\ApieContext; |
5 | use Apie\Core\Exceptions\InvalidTypeException; |
6 | use Apie\Core\Metadata\GetterInterface; |
7 | use Apie\Core\Metadata\SetterInterface; |
8 | use Apie\TypeConverter\ReflectionTypeFactory; |
9 | use ReflectionType; |
10 | |
11 | class OptionalField implements FieldWithPossibleDefaultValue, GetterInterface, SetterInterface |
12 | { |
13 | public function __construct(private FieldInterface $field1, private ?FieldInterface $field2 = null) |
14 | { |
15 | } |
16 | |
17 | public function hasDefaultValue(): bool |
18 | { |
19 | if ($this->field1 instanceof FieldWithPossibleDefaultValue && $this->field1->hasDefaultValue()) { |
20 | return true; |
21 | } |
22 | |
23 | return $this->field2 instanceof FieldWithPossibleDefaultValue && $this->field2->hasDefaultValue(); |
24 | } |
25 | |
26 | public function getDefaultValue(): mixed |
27 | { |
28 | if ($this->field1 instanceof FieldWithPossibleDefaultValue && $this->field1->hasDefaultValue()) { |
29 | return $this->field1->getDefaultValue(); |
30 | } |
31 | assert($this->field2 instanceof FieldWithPossibleDefaultValue); |
32 | return $this->field2->getDefaultValue(); |
33 | } |
34 | |
35 | public function allowsNull(): bool |
36 | { |
37 | if ($this->field2?->allowsNull()) { |
38 | return true; |
39 | } |
40 | return $this->field1->allowsNull(); |
41 | } |
42 | |
43 | public function isRequired(): bool |
44 | { |
45 | if ($this->field2 !== null && !$this->field2->isRequired()) { |
46 | return false; |
47 | } |
48 | |
49 | return $this->field1->isRequired(); |
50 | } |
51 | |
52 | public function isField(): bool |
53 | { |
54 | if ($this->field2 !== null && !$this->field2->isField()) { |
55 | return false; |
56 | } |
57 | return $this->field1->isField(); |
58 | } |
59 | |
60 | public function appliesToContext(ApieContext $apieContext): bool |
61 | { |
62 | return $this->field1->appliesToContext($apieContext); |
63 | } |
64 | |
65 | public function getFieldPriority(): ?int |
66 | { |
67 | if ($this->field2 === null) { |
68 | return $this->field1->getFieldPriority(); |
69 | } |
70 | return min($this->field1->getFieldPriority(), $this->field2->getFieldPriority()); |
71 | } |
72 | |
73 | public function setValue(object $object, mixed $value, ApieContext $apieContext): void |
74 | { |
75 | if ($this->field1 instanceof SetterInterface) { |
76 | $this->field1->setValue($object, $value, $apieContext); |
77 | } |
78 | if ($this->field2 instanceof SetterInterface) { |
79 | $this->field2->setValue($object, $value, $apieContext); |
80 | } |
81 | } |
82 | |
83 | public function markValueAsMissing(): void |
84 | { |
85 | if ($this->field1 instanceof SetterInterface) { |
86 | $this->field1->markValueAsMissing(); |
87 | } |
88 | if ($this->field2 instanceof SetterInterface) { |
89 | $this->field2->markValueAsMissing(); |
90 | } |
91 | } |
92 | |
93 | public function getValue(object $object, ApieContext $apieContext): mixed |
94 | { |
95 | if ($this->field1 instanceof GetterInterface) { |
96 | return $this->field1->getValue($object, $apieContext); |
97 | } |
98 | if ($this->field2 instanceof GetterInterface) { |
99 | return $this->field2->getValue($object, $apieContext); |
100 | } |
101 | |
102 | return null; |
103 | } |
104 | |
105 | public function getTypehint(): ?ReflectionType |
106 | { |
107 | $type1 = $this->field1->getTypehint(); |
108 | $type2 = $this->field2?->getTypehint(); |
109 | |
110 | if (null === $this->field2) { |
111 | return $this->field1->getTypehint(); |
112 | } |
113 | |
114 | if ($type1 === null || $type2 === null) { |
115 | return null; |
116 | } |
117 | $types = []; |
118 | $todo = [$type1, $type2]; |
119 | while (!empty($todo)) { |
120 | $type = array_shift($todo); |
121 | if ($type instanceof \ReflectionUnionType) { |
122 | foreach ($type->getTypes() as $inner) { |
123 | $todo[] = $inner; |
124 | } |
125 | } elseif ($type instanceof \ReflectionNamedType) { |
126 | $name = $type->getName(); |
127 | if ($name === 'mixed') { |
128 | return ReflectionTypeFactory::createReflectionType('mixed'); |
129 | } |
130 | if ($type->allowsNull()) { |
131 | $types['null'] = 'null'; |
132 | } |
133 | $types[$name] = $name; |
134 | } else { |
135 | throw new InvalidTypeException($type, 'ReflectionNamedType|ReflectionUnionType'); |
136 | } |
137 | } |
138 | |
139 | $merged = implode('|', array_keys($types)); |
140 | |
141 | return ReflectionTypeFactory::createReflectionType($merged); |
142 | } |
143 | |
144 | public function getAttributes(string $attributeClass, bool $classDocBlock = true, bool $propertyDocblock = true, bool $argumentDocBlock = true): array |
145 | { |
146 | $attributes = $this->field1->getAttributes($attributeClass, $classDocBlock, $propertyDocblock, $argumentDocBlock); |
147 | if (!empty($attributes)) { |
148 | return $attributes; |
149 | } |
150 | return $this->field2?->getAttributes($attributeClass, $classDocBlock, $propertyDocblock, $argumentDocBlock) ?? []; |
151 | } |
152 | } |