Passed
Push — developer ( e69f4b...3ddfd0 )
by Никита
01:55
created

Serializer::is_binary_string()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 4.0312

Importance

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

Using a closing tag in PHP files that only contain PHP code is not recommended as you might accidentally add whitespace after the closing tag which would then be output by PHP. This can cause severe problems, for example headers cannot be sent anymore.

A simple precaution is to leave off the closing tag as it is not required, and it also has no negative effects whatsoever.

Loading history...