• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nette / schema / 3615460175

pending completion
3615460175

push

github

David
constants are PascalCase

26 of 26 new or added lines in 5 files covered. (100.0%)

439 of 450 relevant lines covered (97.56%)

0.98 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.35
/src/Schema/Elements/Structure.php
1
<?php
2

3
/**
4
 * This file is part of the Nette Framework (https://nette.org)
5
 * Copyright (c) 2004 David Grudl (https://davidgrudl.com)
6
 */
7

8
declare(strict_types=1);
9

10
namespace Nette\Schema\Elements;
11

12
use Nette;
13
use Nette\Schema\Context;
14
use Nette\Schema\Helpers;
15
use Nette\Schema\Schema;
16

17

18
final class Structure implements Schema
19
{
20
        use Base;
21
        use Nette\SmartObject;
22

23
        /** @var Schema[] */
24
        private $items;
25

26
        /** @var Schema|null  for array|list */
27
        private $otherItems;
28

29
        /** @var array{?int, ?int} */
30
        private $range = [null, null];
31

32
        /** @var bool */
33
        private $skipDefaults = false;
34

35

36
        /**
37
         * @param  Schema[]  $items
38
         */
39
        public function __construct(array $items)
1✔
40
        {
41
                (function (Schema ...$items) {})(...array_values($items));
1✔
42
                $this->items = $items;
1✔
43
                $this->castTo = 'object';
1✔
44
                $this->required = true;
1✔
45
        }
1✔
46

47

48
        public function default($value): self
49
        {
50
                throw new Nette\InvalidStateException('Structure cannot have default value.');
1✔
51
        }
52

53

54
        public function min(?int $min): self
1✔
55
        {
56
                $this->range[0] = $min;
1✔
57
                return $this;
1✔
58
        }
59

60

61
        public function max(?int $max): self
1✔
62
        {
63
                $this->range[1] = $max;
1✔
64
                return $this;
1✔
65
        }
66

67

68
        /**
69
         * @param  string|Schema  $type
70
         */
71
        public function otherItems($type = 'mixed'): self
72
        {
73
                $this->otherItems = $type instanceof Schema ? $type : new Type($type);
1✔
74
                return $this;
1✔
75
        }
76

77

78
        public function skipDefaults(bool $state = true): self
1✔
79
        {
80
                $this->skipDefaults = $state;
1✔
81
                return $this;
1✔
82
        }
83

84

85
        /********************* processing ****************d*g**/
86

87

88
        public function normalize($value, Context $context)
1✔
89
        {
90
                if ($prevent = (is_array($value) && isset($value[Helpers::PreventMerging]))) {
1✔
91
                        unset($value[Helpers::PreventMerging]);
×
92
                }
93

94
                $value = $this->doNormalize($value, $context);
1✔
95
                if (is_object($value)) {
1✔
96
                        $value = (array) $value;
1✔
97
                }
98

99
                if (is_array($value)) {
1✔
100
                        foreach ($value as $key => $val) {
1✔
101
                                $itemSchema = $this->items[$key] ?? $this->otherItems;
1✔
102
                                if ($itemSchema) {
1✔
103
                                        $context->path[] = $key;
1✔
104
                                        $value[$key] = $itemSchema->normalize($val, $context);
1✔
105
                                        array_pop($context->path);
1✔
106
                                }
107
                        }
108

109
                        if ($prevent) {
1✔
110
                                $value[Helpers::PreventMerging] = true;
×
111
                        }
112
                }
113

114
                return $value;
1✔
115
        }
116

117

118
        public function merge($value, $base)
119
        {
120
                if (is_array($value) && isset($value[Helpers::PreventMerging])) {
1✔
121
                        unset($value[Helpers::PreventMerging]);
×
122
                        $base = null;
×
123
                }
124

125
                if (is_array($value) && is_array($base)) {
1✔
126
                        $index = 0;
1✔
127
                        foreach ($value as $key => $val) {
1✔
128
                                if ($key === $index) {
1✔
129
                                        $base[] = $val;
1✔
130
                                        $index++;
1✔
131
                                } elseif (array_key_exists($key, $base)) {
1✔
132
                                        $itemSchema = $this->items[$key] ?? $this->otherItems;
1✔
133
                                        $base[$key] = $itemSchema
1✔
134
                                                ? $itemSchema->merge($val, $base[$key])
1✔
135
                                                : Helpers::merge($val, $base[$key]);
1✔
136
                                } else {
137
                                        $base[$key] = $val;
1✔
138
                                }
139
                        }
140

141
                        return $base;
1✔
142
                }
143

144
                return Helpers::merge($value, $base);
1✔
145
        }
146

147

148
        public function complete($value, Context $context)
1✔
149
        {
150
                if ($value === null) {
1✔
151
                        $value = []; // is unable to distinguish null from array in NEON
1✔
152
                }
153

154
                $this->doDeprecation($context);
1✔
155

156
                if (!$this->doValidate($value, 'array', $context)
1✔
157
                        || !$this->doValidateRange($value, $this->range, $context)
1✔
158
                ) {
159
                        return;
1✔
160
                }
161

162
                $errCount = count($context->errors);
1✔
163
                $items = $this->items;
1✔
164
                if ($extraKeys = array_keys(array_diff_key($value, $items))) {
1✔
165
                        if ($this->otherItems) {
1✔
166
                                $items += array_fill_keys($extraKeys, $this->otherItems);
1✔
167
                        } else {
168
                                $keys = array_map('strval', array_keys($items));
1✔
169
                                foreach ($extraKeys as $key) {
1✔
170
                                        $hint = Nette\Utils\ObjectHelpers::getSuggestion($keys, (string) $key);
1✔
171
                                        $context->addError(
1✔
172
                                                'Unexpected item %path%' . ($hint ? ", did you mean '%hint%'?" : '.'),
1✔
173
                                                Nette\Schema\Message::UnexpectedItem,
1✔
174
                                                ['hint' => $hint]
1✔
175
                                        )->path[] = $key;
1✔
176
                                }
177
                        }
178
                }
179

180
                foreach ($items as $itemKey => $itemVal) {
1✔
181
                        $context->path[] = $itemKey;
1✔
182
                        if (array_key_exists($itemKey, $value)) {
1✔
183
                                $value[$itemKey] = $itemVal->complete($value[$itemKey], $context);
1✔
184
                        } else {
185
                                $default = $itemVal->completeDefault($context); // checks required item
1✔
186
                                if (!$context->skipDefaults && !$this->skipDefaults) {
1✔
187
                                        $value[$itemKey] = $default;
1✔
188
                                }
189
                        }
190

191
                        array_pop($context->path);
1✔
192
                }
193

194
                if (count($context->errors) > $errCount) {
1✔
195
                        return;
1✔
196
                }
197

198
                return $this->doFinalize($value, $context);
1✔
199
        }
200

201

202
        public function completeDefault(Context $context)
1✔
203
        {
204
                return $this->required
1✔
205
                        ? $this->complete([], $context)
1✔
206
                        : null;
1✔
207
        }
208
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc