Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
98.51% covered (success)
98.51%
66 / 67
83.33% covered (warning)
83.33%
5 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
Json
98.51% covered (success)
98.51%
66 / 67
83.33% covered (warning)
83.33%
5 / 6
33
0.00% covered (danger)
0.00%
0 / 1
 isValid
75.00% covered (warning)
75.00%
3 / 4
0.00% covered (danger)
0.00%
0 / 1
2.06
 validateTemplate
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
4
 createAllViablePaths
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
3
 hasTemplateDefinition
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
7
 isCompleteSource
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
7
 isValidSource
100.00% covered (success)
100.00%
19 / 19
100.00% covered (success)
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 */
13declare(strict_types=1);
14
15namespace phpOMS\Validation\Base;
16
17use phpOMS\Utils\StringUtils;
18use 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 */
28abstract 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}