492d74a402434aa33ea7c4d6c7a75a9660ca0c81
[GitHub/WoltLab/WCF.git] /
1 <?php
2
3 declare(strict_types=1);
4
5 namespace CuyZ\Valinor\Mapper\Tree\Builder;
6
7 use CuyZ\Valinor\Mapper\Tree\Exception\SourceMustBeIterable;
8 use CuyZ\Valinor\Mapper\Tree\Exception\UnexpectedShapedArrayKeys;
9 use CuyZ\Valinor\Mapper\Tree\Shell;
10 use CuyZ\Valinor\Type\Types\ShapedArrayType;
11
12 use function array_key_exists;
13 use function assert;
14 use function count;
15 use function is_array;
16
17 /** @internal */
18 final class ShapedArrayNodeBuilder implements NodeBuilder
19 {
20 public function __construct(private bool $allowSuperfluousKeys) {}
21
22 public function build(Shell $shell, RootNodeBuilder $rootBuilder): TreeNode
23 {
24 $type = $shell->type();
25 $value = $shell->value();
26
27 assert($type instanceof ShapedArrayType);
28
29 if (! is_array($value)) {
30 throw new SourceMustBeIterable($value, $type);
31 }
32
33 $children = $this->children($type, $shell, $rootBuilder);
34
35 $array = $this->buildArray($children);
36
37 $node = TreeNode::branch($shell, $array, $children);
38
39 if (! $this->allowSuperfluousKeys && count($value) > count($children)) {
40 $node = $node->withMessage(new UnexpectedShapedArrayKeys($value, $children));
41 }
42
43 return $node;
44 }
45
46 /**
47 * @return array<TreeNode>
48 */
49 private function children(ShapedArrayType $type, Shell $shell, RootNodeBuilder $rootBuilder): array
50 {
51 /** @var array<mixed> $value */
52 $value = $shell->value();
53 $elements = $type->elements();
54 $children = [];
55
56 foreach ($elements as $element) {
57 $key = $element->key()->value();
58
59 $child = $shell->child((string)$key, $element->type());
60
61 if (array_key_exists($key, $value)) {
62 $child = $child->withValue($value[$key]);
63 } elseif ($element->isOptional()) {
64 continue;
65 }
66
67 $children[$key] = $rootBuilder->build($child);
68
69 unset($value[$key]);
70 }
71
72 return $children;
73 }
74
75 /**
76 * @param array<TreeNode> $children
77 * @return mixed[]|null
78 */
79 private function buildArray(array $children): ?array
80 {
81 $array = [];
82
83 foreach ($children as $key => $child) {
84 if (! $child->isValid()) {
85 return null;
86 }
87
88 $array[$key] = $child->value();
89 }
90
91 return $array;
92 }
93 }