PluginProbe
Packeta / 1.6.1
Packeta v1.6.1
2.3.2 2.3.1 trunk 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.3.0 1.3.1 1.3.2 1.4 1.4.1 1.4.2 1.4.3 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 All 56 releases
packeta / deps / nette / schema / src / Schema / Elements / Structure.php

Structure.php in Packeta 1.6.1, at deps/nette/schema/src/Schema/Elements/Structure.php

158 lines 5.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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 declare (strict_types=1);
8 namespace Packetery\Nette\Schema\Elements;
9
10 use Packetery\Nette;
11 use Packetery\Nette\Schema\Context;
12 use Packetery\Nette\Schema\Helpers;
13 use Packetery\Nette\Schema\Schema;
14 final class Structure implements Schema
15 {
16 use Base;
17 use \Packetery\Nette\SmartObject;
18 /** @var Schema[] */
19 private $items;
20 /** @var Schema|null for array|list */
21 private $otherItems;
22 /** @var array{?int, ?int} */
23 private $range = [null, null];
24 /** @var bool */
25 private $skipDefaults = \false;
26 /**
27 * @param Schema[] $items
28 */
29 public function __construct(array $items)
30 {
31 (function (Schema ...$items) {
32 })(...\array_values($items));
33 $this->items = $items;
34 $this->castTo = 'object';
35 $this->required = \true;
36 }
37 public function default($value) : self
38 {
39 throw new \Packetery\Nette\InvalidStateException('Structure cannot have default value.');
40 }
41 public function min(?int $min) : self
42 {
43 $this->range[0] = $min;
44 return $this;
45 }
46 public function max(?int $max) : self
47 {
48 $this->range[1] = $max;
49 return $this;
50 }
51 /**
52 * @param string|Schema $type
53 */
54 public function otherItems($type = 'mixed') : self
55 {
56 $this->otherItems = $type instanceof Schema ? $type : new Type($type);
57 return $this;
58 }
59 public function skipDefaults(bool $state = \true) : self
60 {
61 $this->skipDefaults = $state;
62 return $this;
63 }
64 /********************* processing ****************d*g**/
65 public function normalize($value, Context $context)
66 {
67 if ($prevent = \is_array($value) && isset($value[Helpers::PREVENT_MERGING])) {
68 unset($value[Helpers::PREVENT_MERGING]);
69 }
70 $value = $this->doNormalize($value, $context);
71 if (\is_object($value)) {
72 $value = (array) $value;
73 }
74 if (\is_array($value)) {
75 foreach ($value as $key => $val) {
76 $itemSchema = $this->items[$key] ?? $this->otherItems;
77 if ($itemSchema) {
78 $context->path[] = $key;
79 $value[$key] = $itemSchema->normalize($val, $context);
80 \array_pop($context->path);
81 }
82 }
83 if ($prevent) {
84 $value[Helpers::PREVENT_MERGING] = \true;
85 }
86 }
87 return $value;
88 }
89 public function merge($value, $base)
90 {
91 if (\is_array($value) && isset($value[Helpers::PREVENT_MERGING])) {
92 unset($value[Helpers::PREVENT_MERGING]);
93 $base = null;
94 }
95 if (\is_array($value) && \is_array($base)) {
96 $index = 0;
97 foreach ($value as $key => $val) {
98 if ($key === $index) {
99 $base[] = $val;
100 $index++;
101 } elseif (\array_key_exists($key, $base)) {
102 $itemSchema = $this->items[$key] ?? $this->otherItems;
103 $base[$key] = $itemSchema ? $itemSchema->merge($val, $base[$key]) : Helpers::merge($val, $base[$key]);
104 } else {
105 $base[$key] = $val;
106 }
107 }
108 return $base;
109 }
110 return Helpers::merge($value, $base);
111 }
112 public function complete($value, Context $context)
113 {
114 if ($value === null) {
115 $value = [];
116 // is unable to distinguish null from array in NEON
117 }
118 $this->doDeprecation($context);
119 if (!$this->doValidate($value, 'array', $context) || !$this->doValidateRange($value, $this->range, $context)) {
120 return;
121 }
122 $errCount = \count($context->errors);
123 $items = $this->items;
124 if ($extraKeys = \array_keys(\array_diff_key($value, $items))) {
125 if ($this->otherItems) {
126 $items += \array_fill_keys($extraKeys, $this->otherItems);
127 } else {
128 $keys = \array_map('strval', \array_keys($items));
129 foreach ($extraKeys as $key) {
130 $hint = \Packetery\Nette\Utils\ObjectHelpers::getSuggestion($keys, (string) $key);
131 $context->addError('Unexpected item %path%' . ($hint ? ", did you mean '%hint%'?" : '.'), \Packetery\Nette\Schema\Message::UNEXPECTED_ITEM, ['hint' => $hint])->path[] = $key;
132 }
133 }
134 }
135 foreach ($items as $itemKey => $itemVal) {
136 $context->path[] = $itemKey;
137 if (\array_key_exists($itemKey, $value)) {
138 $value[$itemKey] = $itemVal->complete($value[$itemKey], $context);
139 } else {
140 $default = $itemVal->completeDefault($context);
141 // checks required item
142 if (!$context->skipDefaults && !$this->skipDefaults) {
143 $value[$itemKey] = $default;
144 }
145 }
146 \array_pop($context->path);
147 }
148 if (\count($context->errors) > $errCount) {
149 return;
150 }
151 return $this->doFinalize($value, $context);
152 }
153 public function completeDefault(Context $context)
154 {
155 return $this->required ? $this->complete([], $context) : null;
156 }
157 }
158