This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | namespace NokitaKaze\Serializer; |
||
4 | |||
5 | class Serializer { |
||
6 | const TYPE_INT = 0; |
||
7 | const TYPE_DOUBLE = 1; |
||
8 | const TYPE_STRING = 2; |
||
9 | const TYPE_ARRAY_INDEX = 3; |
||
10 | const TYPE_ARRAY_KEY_VALUE = 4; |
||
11 | const TYPE_OBJECT_KEY_VALUE = 5; |
||
12 | const TYPE_SERIALIZABLE = ISerializable::TYPE; |
||
13 | const TYPE_RESOURCE = 7; |
||
14 | const TYPE_BOOLEAN = 7; |
||
15 | const TYPE_NULL = 8; |
||
16 | |||
17 | const DEFAULT_DEPTH = 255; |
||
18 | |||
19 | /** |
||
20 | * @param mixed $data |
||
21 | * @param integer $depth |
||
22 | * |
||
23 | * @return string |
||
24 | */ |
||
25 | 23 | public static function serialize($data, $depth = self::DEFAULT_DEPTH) { |
|
26 | 23 | return json_encode(static::serialize_to_variant($data, $depth)); |
|
27 | } |
||
28 | |||
29 | /** |
||
30 | * @param mixed $text |
||
31 | * @param boolean $is_valid |
||
32 | * @param boolean $safe |
||
33 | * |
||
34 | * @return mixed |
||
35 | */ |
||
36 | 28 | public static function unserialize($text, &$is_valid, $safe = true) { |
|
37 | 28 | $is_valid = true; |
|
38 | 28 | if ($text === 'null') { |
|
39 | 1 | return null; |
|
40 | } |
||
41 | 27 | $data = json_decode($text); |
|
42 | 27 | if (is_null($data)) { |
|
43 | 1 | $is_valid = false; |
|
44 | |||
45 | 1 | return null; |
|
46 | } |
||
47 | |||
48 | try { |
||
49 | 26 | $object = static::unserialize_variant_to_value($data, $safe); |
|
50 | 4 | } catch (UnserializeException $e) { |
|
51 | 4 | $is_valid = false; |
|
52 | |||
53 | 4 | return null; |
|
54 | } |
||
55 | |||
56 | 22 | return $object; |
|
57 | } |
||
58 | |||
59 | /** |
||
60 | * @param mixed $data |
||
61 | * @param integer $depth |
||
62 | * |
||
63 | * @return Variant|object|boolean|null |
||
64 | */ |
||
65 | 23 | public static function serialize_to_variant($data, $depth = self::DEFAULT_DEPTH) { |
|
66 | 23 | if ($depth < 0) { |
|
67 | 1 | return null; |
|
68 | } |
||
69 | 23 | if (is_double($data)) { |
|
70 | 8 | return static::serialize_double($data); |
|
71 | 17 | } elseif (is_int($data)) { |
|
72 | 4 | return static::serialize_int($data); |
|
73 | 16 | } elseif (is_string($data)) { |
|
74 | 5 | return static::serialize_string($data); |
|
75 | 14 | } elseif (is_array($data) and static::is_indexed_array($data)) { |
|
76 | 4 | return static::serialize_indexed_array($data, $depth - 1); |
|
77 | 12 | } elseif (is_array($data)) { |
|
78 | 2 | return static::serialize_array_key_value($data, $depth - 1); |
|
79 | 11 | } elseif (is_object($data) and ($data instanceof ISerializable)) { |
|
80 | /** |
||
81 | * @var ISerializable $data |
||
82 | */ |
||
83 | 3 | return $data->ns_serialize(); |
|
84 | 8 | } elseif (is_object($data)) { |
|
85 | 4 | return static::serialize_object_key_value($data); |
|
86 | 4 | } elseif (is_resource($data)) { |
|
87 | // @todo Придумать что делать с ресурсами |
||
88 | return null; |
||
89 | 4 | } elseif (is_bool($data)) { |
|
90 | 3 | return $data; |
|
91 | 2 | } elseif (is_null($data)) { |
|
92 | 2 | return null; |
|
93 | } else { |
||
94 | return null; |
||
95 | } |
||
96 | } |
||
97 | |||
98 | protected static $_binary_codes = [ |
||
99 | 0, 1, 2, 3, 4, 5, 6, 7, 11, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 128, 129, 130, |
||
100 | 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, |
||
101 | 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, |
||
102 | 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, |
||
103 | 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, |
||
104 | 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, |
||
105 | 246, 247, 248, 249, 250, 251, 252, 253, 254, |
||
106 | ]; |
||
107 | |||
108 | protected static $_binary_codes_flip = null; |
||
109 | |||
110 | /** |
||
111 | * @param string $value |
||
112 | * |
||
113 | * @return boolean |
||
114 | */ |
||
115 | 19 | public static function is_binary_string($value) { |
|
116 | 19 | if (is_null(self::$_binary_codes_flip)) { |
|
117 | 1 | self::$_binary_codes_flip = array_flip(self::$_binary_codes); |
|
118 | } |
||
119 | |||
120 | // @todo Оптимизировать через > & < |
||
121 | 19 | for ($i = 0; $i < strlen($value); $i++) { |
|
122 | 19 | $ord = ord(substr($value, $i, 1)); |
|
123 | 19 | if (isset(self::$_binary_codes_flip[$ord])) { |
|
124 | 1 | return true; |
|
125 | } |
||
126 | } |
||
127 | |||
128 | 19 | return false; |
|
129 | } |
||
130 | |||
131 | /** |
||
132 | * @param double $value |
||
133 | * |
||
134 | * @return Variant|object |
||
135 | */ |
||
136 | 8 | public static function serialize_double($value) { |
|
137 | return (object) [ |
||
138 | 8 | 'type' => self::TYPE_DOUBLE, |
|
139 | 8 | 'value' => serialize($value), |
|
140 | ]; |
||
141 | } |
||
142 | |||
143 | /** |
||
144 | * @param integer $value |
||
145 | * |
||
146 | * @return Variant|object |
||
147 | */ |
||
148 | 4 | public static function serialize_int($value) { |
|
149 | return (object) [ |
||
150 | 4 | 'type' => self::TYPE_INT, |
|
151 | 4 | 'value' => $value, |
|
152 | ]; |
||
153 | } |
||
154 | |||
155 | /** |
||
156 | * @param array $value |
||
157 | * @param integer $depth |
||
158 | * |
||
159 | * @return Variant|object |
||
160 | */ |
||
161 | 4 | public static function serialize_indexed_array(array $value, $depth) { |
|
162 | 4 | $output = []; |
|
163 | 4 | foreach ($value as $sub_value) { |
|
164 | 3 | $output[] = self::serialize_to_variant($sub_value, $depth - 1); |
|
165 | } |
||
166 | |||
167 | return (object) [ |
||
168 | 4 | 'type' => self::TYPE_ARRAY_INDEX, |
|
169 | 4 | 'value' => $output, |
|
170 | ]; |
||
171 | } |
||
172 | |||
173 | /** |
||
174 | * @param array $value |
||
175 | * @param integer $depth |
||
176 | * |
||
177 | * @return Variant|object |
||
178 | */ |
||
179 | 2 | public static function serialize_array_key_value(array $value, $depth) { |
|
180 | 2 | $output = []; |
|
181 | 2 | $keys = []; |
|
182 | 2 | foreach ($value as $key => $sub_value) { |
|
183 | 2 | $keys[] = self::serialize_to_variant($key, $depth - 1); |
|
184 | 2 | $output[] = self::serialize_to_variant($sub_value, $depth - 1); |
|
185 | } |
||
186 | |||
187 | return (object) [ |
||
188 | 2 | 'type' => self::TYPE_ARRAY_KEY_VALUE, |
|
189 | 2 | 'value' => $output, |
|
190 | 2 | 'keys' => $keys, |
|
191 | ]; |
||
192 | } |
||
193 | |||
194 | /** |
||
195 | * @param \stdClass|object $value |
||
196 | * |
||
197 | * @return Variant|object |
||
198 | */ |
||
199 | 4 | public static function serialize_object_key_value($value) { |
|
200 | 4 | $output = []; |
|
201 | 4 | $keys = []; |
|
202 | |||
203 | 4 | foreach (get_object_vars($value) as $key => $value) { |
|
204 | 1 | $keys[] = self::serialize_to_variant($key); |
|
205 | 1 | $output[] = self::serialize_to_variant($value); |
|
206 | } |
||
207 | |||
208 | return (object) [ |
||
209 | 4 | 'type' => self::TYPE_OBJECT_KEY_VALUE, |
|
210 | 4 | 'value' => $output, |
|
211 | 4 | 'keys' => $keys, |
|
212 | ]; |
||
213 | } |
||
214 | |||
215 | /** |
||
216 | * @param string $value |
||
217 | * |
||
218 | * @return Variant|object |
||
219 | */ |
||
220 | 5 | public static function serialize_string($value) { |
|
221 | return (object) [ |
||
222 | 5 | 'type' => self::TYPE_STRING, |
|
223 | 5 | 'value' => base64_encode(gzcompress($value)), |
|
224 | ]; |
||
225 | } |
||
226 | |||
227 | /** |
||
228 | * @param array $input |
||
229 | * |
||
230 | * @return boolean |
||
231 | */ |
||
232 | 6 | public static function is_indexed_array(array $input) { |
|
233 | 6 | if (empty($input)) { |
|
234 | 1 | return true; |
|
235 | } |
||
236 | |||
237 | 5 | $keys = array_keys($input); |
|
238 | 5 | for ($i = 0; $i < count($keys); $i++) { |
|
0 ignored issues
–
show
|
|||
239 | 5 | if ($keys[$i] !== $i) { |
|
240 | 2 | return false; |
|
241 | } |
||
242 | } |
||
243 | |||
244 | 3 | return true; |
|
245 | } |
||
246 | |||
247 | /** |
||
248 | * @param \stdClass|Variant $variant |
||
249 | * |
||
250 | * @return string |
||
251 | */ |
||
252 | 5 | public static function unserialize_string($variant) { |
|
253 | 5 | return gzuncompress(base64_decode($variant->value)); |
|
254 | } |
||
255 | |||
256 | /** |
||
257 | * @param \stdClass|Variant $variant |
||
258 | * @param boolean $safe |
||
259 | * |
||
260 | * @return array |
||
261 | * @throws UnserializeException |
||
262 | */ |
||
263 | 4 | public static function unserialize_array_index($variant, $safe) { |
|
264 | 4 | $output = []; |
|
265 | 4 | foreach ($variant->value as $sub_value) { |
|
266 | 3 | $output[] = self::unserialize_variant_to_value($sub_value, $safe); |
|
267 | } |
||
268 | |||
269 | 4 | return $output; |
|
270 | } |
||
271 | |||
272 | /** |
||
273 | * @param \stdClass|KeyValueVariant $variant |
||
274 | * @param boolean $safe |
||
275 | * |
||
276 | * @return array |
||
277 | * @throws UnserializeException |
||
278 | */ |
||
279 | 6 | public static function unserialize_array_key_value($variant, $safe) { |
|
280 | 6 | $count = count($variant->keys); |
|
281 | 6 | if ($count != count($variant->value)) { |
|
282 | throw new UnserializeException(sprintf('Array keys count (%d) does not equal value count (%d)', |
||
283 | $count, |
||
284 | count($variant->value) |
||
285 | ), 2); |
||
286 | } |
||
287 | |||
288 | 6 | $output = []; |
|
289 | 6 | for ($i = 0; $i < count($variant->keys); $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
}
![]() |
|||
290 | 3 | $output[self::unserialize_variant_to_value($variant->keys[$i], $safe)] |
|
291 | 3 | = self::unserialize_variant_to_value($variant->value[$i], $safe); |
|
292 | } |
||
293 | |||
294 | 6 | return $output; |
|
295 | } |
||
296 | |||
297 | /** |
||
298 | * @param SerializableVariant|boolean|null|object $variant |
||
299 | * @param boolean $safe |
||
300 | * |
||
301 | * @return mixed |
||
302 | * @throws UnserializeException |
||
303 | */ |
||
304 | 6 | public static function unserialize_object($variant, $safe) { |
|
305 | 6 | if (!isset($variant->class_FQDN)) { |
|
306 | 1 | throw new UnserializeException('Variant does not contain field with FQDN', 3); |
|
307 | } |
||
308 | 5 | if (!class_exists($variant->class_FQDN)) { |
|
309 | 1 | throw new UnserializeException(sprintf('Class "%s" does not exist', $variant->class_FQDN), 4); |
|
310 | } |
||
311 | 4 | if (!is_subclass_of($variant->class_FQDN, 'NokitaKaze\\Serializer\\ISerializable')) { |
|
312 | 1 | throw new UnserializeException(sprintf('Class "%s" does not implement ISerializable', $variant->class_FQDN), 5); |
|
313 | } |
||
314 | 3 | if ($safe and !is_subclass_of($variant->class_FQDN, 'NokitaKaze\\Serializer\\ISafeSerializable')) { |
|
315 | // Не поддерживает safe-десериализацию |
||
316 | 1 | return null; |
|
317 | } |
||
318 | |||
319 | /** |
||
320 | * @var ISerializable $class |
||
321 | */ |
||
322 | 2 | $class = $variant->class_FQDN; |
|
323 | |||
324 | 2 | return $class::ns_unserialize($variant); |
|
325 | } |
||
326 | |||
327 | /** |
||
328 | * @param Variant|boolean|null|object $variant |
||
329 | * @param boolean $safe |
||
330 | * |
||
331 | * @return mixed |
||
332 | * @throws UnserializeException |
||
333 | */ |
||
334 | 26 | public static function unserialize_variant_to_value($variant, $safe) { |
|
335 | 26 | if (is_null($variant)) { |
|
336 | 2 | return null; |
|
337 | 26 | } elseif (is_bool($variant)) { |
|
338 | 3 | return $variant; |
|
339 | } else { |
||
340 | 26 | switch ($variant->type) { |
|
341 | 26 | case self::TYPE_INT: |
|
342 | 4 | return $variant->value; |
|
343 | 25 | case self::TYPE_DOUBLE: |
|
344 | 8 | return \unserialize($variant->value); |
|
345 | 19 | case self::TYPE_STRING: |
|
346 | 5 | return self::unserialize_string($variant); |
|
347 | 17 | case self::TYPE_ARRAY_INDEX: |
|
348 | 4 | return self::unserialize_array_index($variant, $safe); |
|
349 | 13 | case self::TYPE_ARRAY_KEY_VALUE: |
|
350 | 2 | return self::unserialize_array_key_value($variant, $safe); |
|
351 | 11 | case self::TYPE_OBJECT_KEY_VALUE: |
|
352 | 4 | return (object) self::unserialize_array_key_value($variant, $safe); |
|
353 | 7 | case self::TYPE_SERIALIZABLE: |
|
354 | 6 | return self::unserialize_object($variant, $safe); |
|
355 | default: |
||
356 | 1 | throw new UnserializeException('Malformed type '.$variant->type, 1); |
|
357 | } |
||
358 | } |
||
359 | } |
||
360 | |||
361 | } |
||
362 | |||
363 | ?> |
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: