1 | <?php |
||
2 | |||
3 | namespace EmanueleMinotto\JsonSchemaFaker; |
||
4 | |||
5 | use Faker\Provider\Base; |
||
6 | use Faker\Provider\Lorem; |
||
7 | |||
8 | class JsonSchemaProvider |
||
9 | { |
||
10 | 111 | public static function jsonSchemaContent(string $content) |
|
11 | { |
||
12 | 111 | $schema = json_decode($content, true); |
|
13 | |||
14 | 111 | return static::jsonSchema($schema); |
|
15 | } |
||
16 | |||
17 | 111 | public static function jsonSchema(array $schema) |
|
18 | { |
||
19 | 111 | if (!isset($schema['type']) && !empty($schema['enum'])) { |
|
20 | 3 | return array_rand(array_flip($schema['enum'])); |
|
21 | } |
||
22 | |||
23 | 111 | switch ($schema['type']) { |
|
24 | 111 | case 'string': |
|
25 | 41 | return static::fromString($schema); |
|
26 | 90 | case 'number': |
|
27 | 33 | return static::fromNumber($schema); |
|
28 | 69 | case 'integer': |
|
29 | 31 | return static::fromInteger($schema); |
|
30 | 66 | case 'boolean': |
|
31 | 9 | return static::fromBoolean($schema); |
|
32 | 63 | case 'array': |
|
33 | 27 | return static::fromArray($schema); |
|
34 | 39 | case 'object': |
|
35 | 36 | return static::fromObject($schema); |
|
36 | 4 | case 'null': |
|
37 | default: |
||
38 | 4 | return null; |
|
39 | } |
||
40 | } |
||
41 | |||
42 | 41 | private static function fromString(array $schema): string |
|
43 | { |
||
44 | 41 | $output = ''; |
|
45 | 41 | if (isset($schema['pattern'])) { |
|
46 | 6 | $output = Base::regexify($schema['pattern']); |
|
47 | } |
||
48 | |||
49 | 41 | $minLength = max($schema['minLength'] ?? 1, 1); |
|
50 | 41 | $maxLength = min($schema['maxLength'] ?? 100, 100); |
|
51 | |||
52 | 41 | $max = max($minLength, $maxLength); |
|
53 | 41 | $output = $output ?: Lorem::text(max($max, 5)); |
|
54 | 41 | $output = substr($output, 0, $max); |
|
55 | |||
56 | 41 | $contentMediaType = $schema['contentMediaType'] ?? 'text/plain'; |
|
57 | 41 | $contentMediaType = in_array($contentMediaType, ['text/plain', 'application/json']) |
|
58 | 41 | ? $contentMediaType |
|
59 | 41 | : 'text/plain'; |
|
60 | |||
61 | 41 | if ('application/json' === $contentMediaType) { |
|
62 | 6 | $output = json_encode($output); |
|
63 | } |
||
64 | |||
65 | 41 | $contentEncoding = $schema['contentEncoding'] ?? 'binary'; |
|
66 | 41 | $contentEncoding = in_array($contentEncoding, ['binary', 'base64']) |
|
67 | 41 | ? $contentEncoding |
|
68 | 41 | : 'binary'; |
|
69 | |||
70 | 41 | if ('base64' === $contentEncoding) { |
|
71 | 6 | $output = base64_encode($output); |
|
72 | } |
||
73 | |||
74 | 41 | return $output; |
|
75 | } |
||
76 | |||
77 | 59 | private static function fromNumber(array $schema): float |
|
78 | { |
||
79 | 59 | $minimum = $schema['minimum'] ?? 0; |
|
80 | 59 | if (isset($schema['exclusiveMinimum'])) { |
|
81 | 6 | if (true === $schema['exclusiveMinimum']) { |
|
82 | 3 | $minimum += 0.001; |
|
83 | } else { |
||
84 | 3 | $minimum = $schema['exclusiveMinimum']; |
|
85 | } |
||
86 | } |
||
87 | |||
88 | 59 | $maximum = $schema['maximum'] ?? PHP_INT_MAX; |
|
89 | 59 | if (isset($schema['exclusiveMaximum'])) { |
|
90 | 3 | if (true === $schema['exclusiveMaximum']) { |
|
91 | $maximum -= 0.001; |
||
92 | } else { |
||
93 | 3 | $maximum = $schema['exclusiveMaximum']; |
|
94 | } |
||
95 | } |
||
96 | |||
97 | 59 | $multipleOf = $schema['multipleOf'] ?? 1; |
|
98 | |||
99 | 59 | $output = Base::randomFloat(null, $minimum, $maximum / max($multipleOf, 1)); |
|
100 | 59 | if (1 !== $multipleOf) { |
|
101 | 6 | $output = $multipleOf * round($output); |
|
102 | } |
||
103 | |||
104 | 59 | return min(max($output, $minimum), $maximum); |
|
105 | } |
||
106 | |||
107 | 31 | private static function fromInteger(array $schema): int |
|
108 | { |
||
109 | 31 | return round(static::fromNumber($schema)); |
|
110 | } |
||
111 | |||
112 | 9 | private static function fromBoolean(array $schema): bool |
|
0 ignored issues
–
show
|
|||
113 | { |
||
114 | 9 | return 1 === mt_rand(0, 1); |
|
115 | } |
||
116 | |||
117 | 27 | private static function fromArray(array $schema): array |
|
118 | { |
||
119 | 27 | $minItems = max($schema['minItems'] ?? 0, 0); |
|
120 | 27 | $maxItems = min($schema['maxItems'] ?? 50, 50); |
|
121 | 27 | $uniqueItems = $schema['uniqueItems'] ?? false; |
|
122 | |||
123 | 27 | $items = $schema['items'] ?? []; |
|
124 | 27 | if (!isset($items[0]) && !empty($items)) { |
|
125 | 6 | $items = array_fill(0, max($minItems, 1), $items); |
|
126 | |||
127 | 6 | unset($schema['additionalItems'], $schema['contains']); |
|
128 | } |
||
129 | |||
130 | 27 | if (isset($schema['contains'])) { |
|
131 | 3 | $items = array_fill(0, max($minItems, 1), $schema['contains']); |
|
132 | } |
||
133 | |||
134 | 27 | if (isset($schema['additionalItems'])) { |
|
135 | 3 | $items += array_fill(count($items), max(0, $maxItems - count($items)), $schema['additionalItems']); |
|
136 | } |
||
137 | |||
138 | 27 | if (empty($items)) { |
|
139 | 12 | $type = array_rand(array_flip(['string', 'number', 'integer'])); |
|
140 | 12 | $items = array_fill(0, mt_rand($minItems, $maxItems), [ |
|
141 | 12 | 'type' => $type, |
|
142 | ]); |
||
143 | } |
||
144 | |||
145 | 27 | $data = []; |
|
146 | 27 | for ($i = 0; $i < count($items); ++$i) { |
|
147 | 26 | $value = static::jsonSchema($items[$i]); |
|
148 | |||
149 | 26 | if ($uniqueItems && in_array($value, $data, true)) { |
|
150 | --$i; |
||
151 | continue; |
||
152 | } |
||
153 | |||
154 | 26 | $data[] = $value; |
|
155 | } |
||
156 | |||
157 | 27 | return $data; |
|
158 | } |
||
159 | |||
160 | 36 | private static function fromObject(array $schema): \stdClass |
|
161 | { |
||
162 | 36 | $object = new \stdClass(); |
|
163 | |||
164 | 36 | if (!empty($schema['properties'])) { |
|
165 | 12 | foreach ($schema['properties'] as $property => $subschema) { |
|
166 | 12 | if (0 === mt_rand(0, 1) && !in_array($property, $schema['required'] ?? [])) { |
|
167 | 9 | continue; |
|
168 | } |
||
169 | |||
170 | 9 | $object->{$property} = !is_bool($subschema) |
|
171 | 5 | ? static::jsonSchema($subschema) |
|
172 | 6 | : static::getFromPatternProperties($property, $schema); |
|
173 | } |
||
174 | } |
||
175 | |||
176 | 36 | $minProperties = max($schema['minProperties'] ?? 0, 0); |
|
177 | |||
178 | 36 | if (0 !== $minProperties && count(get_object_vars($object)) < $minProperties) { |
|
179 | do { |
||
180 | 3 | $propertyName = isset($schema['propertyNames']) |
|
181 | ? static::jsonSchema($schema['propertyNames']) |
||
182 | 3 | : Lorem::word(); |
|
183 | |||
184 | 3 | $object->{$propertyName} = static::getFromPatternProperties($propertyName, $schema); |
|
185 | 3 | } while (count(get_object_vars($object)) < $minProperties); |
|
186 | } |
||
187 | |||
188 | 36 | if (isset($schema['maxProperties']) && count(get_object_vars($object)) < $schema['maxProperties']) { |
|
189 | do { |
||
190 | 3 | $propertyName = isset($schema['propertyNames']) |
|
191 | ? static::jsonSchema($schema['propertyNames']) |
||
192 | 3 | : Lorem::word(); |
|
193 | |||
194 | 3 | $object->{$propertyName} = static::getFromPatternProperties($propertyName, $schema); |
|
195 | 3 | } while (count(get_object_vars($object)) < mt_rand($minProperties, $schema['maxProperties'])); |
|
196 | } |
||
197 | |||
198 | 36 | foreach ($schema['required'] ?? [] as $property) { |
|
199 | 6 | if (!isset($object->{$property})) { |
|
200 | 4 | $object->{$property} = static::getFromPatternProperties($property, $schema); |
|
201 | } |
||
202 | } |
||
203 | |||
204 | 36 | return $object; |
|
205 | } |
||
206 | |||
207 | 13 | private static function getFromPatternProperties(string $property, array $schema) |
|
208 | { |
||
209 | 13 | if (isset($schema['patternProperties'])) { |
|
210 | 2 | foreach ($schema['patternProperties'] as $key => $value) { |
|
211 | 2 | if (preg_match('/'.$key.'/', $property)) { |
|
212 | return static::jsonSchema($schema); |
||
213 | } |
||
214 | } |
||
215 | } |
||
216 | |||
217 | 13 | if (isset($schema['additionalProperties']) && !is_bool($schema['additionalProperties'])) { |
|
218 | 2 | return static::jsonSchema($schema['additionalProperties']); |
|
219 | } |
||
220 | |||
221 | 11 | return static::fromRandom(); |
|
222 | } |
||
223 | |||
224 | 11 | private static function fromRandom() |
|
225 | { |
||
226 | 11 | $type = array_rand(array_flip(['string', 'number', 'integer'])); |
|
227 | |||
228 | 11 | return static::jsonSchema(['type' => $type]); |
|
229 | } |
||
230 | } |
||
231 |
This check looks for parameters that have been defined for a function or method, but which are not used in the method body.