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) { |
|
0 ignored issues
–
show
It seems like you are calling the size function
count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}
// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
![]() |
|||
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.