Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
98.51% |
66 / 67 |
|
83.33% |
5 / 6 |
CRAP | |
0.00% |
0 / 1 |
Json | |
98.51% |
66 / 67 |
|
83.33% |
5 / 6 |
33 | |
0.00% |
0 / 1 |
isValid | |
75.00% |
3 / 4 |
|
0.00% |
0 / 1 |
2.06 | |||
validateTemplate | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
4 | |||
createAllViablePaths | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
3 | |||
hasTemplateDefinition | |
100.00% |
14 / 14 |
|
100.00% |
1 / 1 |
7 | |||
isCompleteSource | |
100.00% |
13 / 13 |
|
100.00% |
1 / 1 |
7 | |||
isValidSource | |
100.00% |
19 / 19 |
|
100.00% |
1 / 1 |
10 |
1 | <?php |
2 | /** |
3 | * Jingga |
4 | * |
5 | * PHP Version 8.1 |
6 | * |
7 | * @package phpOMS\Validation\Base |
8 | * @copyright Dennis Eichhorn |
9 | * @license OMS License 2.0 |
10 | * @version 1.0.0 |
11 | * @link https://jingga.app |
12 | */ |
13 | declare(strict_types=1); |
14 | |
15 | namespace phpOMS\Validation\Base; |
16 | |
17 | use phpOMS\Utils\StringUtils; |
18 | use phpOMS\Validation\ValidatorAbstract; |
19 | |
20 | /** |
21 | * Validate json. |
22 | * |
23 | * @package phpOMS\Validation\Base |
24 | * @license OMS License 2.0 |
25 | * @link https://jingga.app |
26 | * @since 1.0.0 |
27 | */ |
28 | abstract class Json extends ValidatorAbstract |
29 | { |
30 | /** |
31 | * {@inheritdoc} |
32 | */ |
33 | public static function isValid(mixed $value, array $constraints = null) : bool |
34 | { |
35 | if (!\is_string($value)) { |
36 | return false; |
37 | } |
38 | |
39 | \json_decode($value); |
40 | |
41 | return \json_last_error() == \JSON_ERROR_NONE; |
42 | } |
43 | |
44 | /** |
45 | * Validate array against a template array. |
46 | * |
47 | * @param array $template Template structure |
48 | * @param array $source Source structure |
49 | * @param bool $perfect No additional elements in source allowed |
50 | * |
51 | * @return bool Returns true if the template validates the source otherwise false |
52 | * |
53 | * @since 1.0.0 |
54 | */ |
55 | public static function validateTemplate(array $template, array $source, bool $perfect = false) : bool |
56 | { |
57 | $templatePaths = self::createAllViablePaths($template, ''); |
58 | $sourcePaths = self::createAllViablePaths($source, ''); |
59 | |
60 | $isComplete = self::isCompleteSource($templatePaths, $sourcePaths); |
61 | if (!$isComplete) { |
62 | return false; |
63 | } |
64 | |
65 | if ($perfect) { |
66 | $perfectFit = self::hasTemplateDefinition($templatePaths, $sourcePaths); |
67 | if (!$perfectFit) { |
68 | return false; |
69 | } |
70 | } |
71 | |
72 | return self::isValidSource($templatePaths, $sourcePaths); |
73 | } |
74 | |
75 | /** |
76 | * Create all viable paths and their values |
77 | * |
78 | * @param array $arr Array |
79 | * @param string $path Current path |
80 | * |
81 | * @return array |
82 | * |
83 | * @since 1.0.0 |
84 | */ |
85 | private static function createAllViablePaths(array $arr, string $path = '') : array |
86 | { |
87 | $paths = []; |
88 | foreach ($arr as $key => $value) { |
89 | $tempPath = $path . '/' . $key; |
90 | |
91 | if (\is_array($value)) { |
92 | $paths += self::createAllViablePaths($value, $tempPath); |
93 | } else { |
94 | $paths[$tempPath] = $value; |
95 | } |
96 | } |
97 | |
98 | return $paths; |
99 | } |
100 | |
101 | /** |
102 | * Check if source array has additional elements. |
103 | * |
104 | * @param array $template Template structure |
105 | * @param array $source Source structure |
106 | * |
107 | * @return bool Returns false in case of undefined elements |
108 | * |
109 | * @since 1.0.0 |
110 | */ |
111 | private static function hasTemplateDefinition(array $template, array $source) : bool |
112 | { |
113 | $completePaths = []; |
114 | foreach ($template as $key => $value) { |
115 | $key = \str_replace('/0', '/.*', $key); |
116 | $completePaths[$key] = $value; |
117 | } |
118 | |
119 | foreach ($source as $sPath => $sValue) { |
120 | $hasDefinition = false; |
121 | |
122 | foreach ($completePaths as $tPath => $tValue) { |
123 | if ($tPath === $sPath |
124 | || \preg_match('~' . \str_replace('/', '\\/', $tPath) . '~', $sPath) === 1 |
125 | ) { |
126 | $hasDefinition = true; |
127 | break; |
128 | } |
129 | } |
130 | |
131 | if (!$hasDefinition) { |
132 | return false; |
133 | } |
134 | } |
135 | |
136 | return true; |
137 | } |
138 | |
139 | /** |
140 | * Check if source array is complete |
141 | * |
142 | * @param array $template Template structure |
143 | * @param array $source Source structure |
144 | * |
145 | * @return bool Returns true if the source implements all required elements otherwise false is returned |
146 | * |
147 | * @since 1.0.0 |
148 | */ |
149 | private static function isCompleteSource(array $template, array $source) : bool |
150 | { |
151 | $completePaths = []; |
152 | foreach ($template as $key => $value) { |
153 | $key = \str_replace('/0', '/.*', $key); |
154 | |
155 | if (\stripos($key, '/.*') !== false) { |
156 | continue; |
157 | } |
158 | |
159 | $completePaths[$key] = $value; |
160 | } |
161 | |
162 | foreach ($completePaths as $tPath => $_) { |
163 | foreach ($source as $sPath => $_) { |
164 | if ($tPath === $sPath |
165 | || \preg_match('~' . \str_replace('/', '\\/', $tPath) . '~', $sPath) === 1 |
166 | ) { |
167 | unset($completePaths[$tPath]); |
168 | break; |
169 | } |
170 | } |
171 | } |
172 | |
173 | return empty($completePaths); |
174 | } |
175 | |
176 | /** |
177 | * Check if source array is correct |
178 | * |
179 | * @param array $template Template structure |
180 | * @param array $source Source structure |
181 | * |
182 | * @return bool Returns true if the source is correct in relation to the template otherwise false is returned |
183 | * |
184 | * @since 1.0.0 |
185 | */ |
186 | private static function isValidSource(array $template, array $source) : bool |
187 | { |
188 | $validPaths = []; |
189 | foreach ($template as $key => $value) { |
190 | $key = \str_replace('/0', '/\d*', $key); |
191 | $validPaths[$key] = $value; |
192 | } |
193 | |
194 | foreach ($source as $sPath => $sValue) { |
195 | $isValidValue = false; |
196 | $pathFound = false; |
197 | |
198 | foreach ($validPaths as $tPath => $tValue) { |
199 | if ($tPath === $sPath |
200 | || \preg_match('~' . \str_replace('/', '\\/', $tPath) . '~', $sPath) === 1 |
201 | ) { |
202 | $pathFound = true; |
203 | $sValue = StringUtils::stringify($sValue); |
204 | |
205 | if (($tValue === $sValue |
206 | || \preg_match('~' . ((string) $tValue) . '~', (string) $sValue) === 1) |
207 | ) { |
208 | $isValidValue = true; |
209 | break; |
210 | } |
211 | } |
212 | } |
213 | |
214 | if (!$isValidValue && $pathFound) { |
215 | return false; |
216 | } |
217 | } |
218 | |
219 | return true; |
220 | } |
221 | } |