GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — serialize-closure ( f5af48 )
by Juan
03:21
created

JsonSerializer::unserializeData()   B

Complexity

Conditions 6
Paths 5

Size

Total Lines 18
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 1
Metric Value
c 3
b 0
f 1
dl 0
loc 18
rs 8.8571
cc 6
eloc 10
nc 5
nop 1
1
<?php
2
3
namespace Zumba\Util;
4
5
use ReflectionClass;
6
use ReflectionException;
7
use SplObjectStorage;
8
use Zumba\Exception\JsonSerializerException;
9
use SuperClosure\SerializerInterface as ClosureSerializerInterface;
10
11
class JsonSerializer {
12
13
	const CLASS_IDENTIFIER_KEY = '@type';
14
	const CLOSURE_IDENTIFIER_KEY = '@closure';
15
	const FLOAT_ADAPTER = 'JsonSerializerFloatAdapter';
16
17
	/**
18
	 * Storage for object
19
	 *
20
	 * Used for recursion
21
	 *
22
	 * @var SplObjectStorage
23
	 */
24
	protected $objectStorage;
25
26
	/**
27
	 * Object mapping for recursion
28
	 *
29
	 * @var array
30
	 */
31
	protected $objectMapping = array();
32
33
	/**
34
	 * Object mapping index
35
	 *
36
	 * @var integer
37
	 */
38
	protected $objectMappingIndex = 0;
39
40
	/**
41
	 * Support PRESERVE_ZERO_FRACTION json option
42
	 *
43
	 * @var boolean
44
	 */
45
	protected $preserveZeroFractionSupport;
46
47
	/**
48
	 * Closure serializer instance
49
	 *
50
	 * @var SuperClosure\SerializerInterface
51
	 */
52
	protected $closureSerializer;
53
54
	/**
55
	 * Constructor.
56
	 *
57
	 * @param SuperClosure\SerializerInterface $closureSerializer
58
	 */
59
	public function __construct(ClosureSerializerInterface $closureSerializer = null) {
60
		$this->preserveZeroFractionSupport = defined('JSON_PRESERVE_ZERO_FRACTION');
61
62
		if ($closureSerializer !== null) {
63
			if (!$closureSerializer instanceof ClosureSerializerInterface) {
0 ignored issues
show
Bug introduced by
The class SuperClosure\SerializerInterface does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
64
				throw new JsonSerializerException('Invalid closure serializer given.');
65
			}
66
			$this->closureSerializer = $closureSerializer;
0 ignored issues
show
Documentation Bug introduced by
It seems like $closureSerializer of type object<SuperClosure\SerializerInterface> is incompatible with the declared type object<Zumba\Util\SuperC...re\SerializerInterface> of property $closureSerializer.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
67
		}
68
	}
69
70
	/**
71
	 * Serialize the value in JSON
72
	 *
73
	 * @param mixed $value
74
	 * @return string JSON encoded
75
	 * @throws Zumba\Exception\JsonSerializerException
76
	 */
77
	public function serialize($value) {
78
		$this->reset();
79
		$encoded = json_encode($this->serializeData($value), $this->calculateEncodeOptions());
80
		return $this->processEncodedValue($encoded);
81
	}
82
83
	/**
84
	 * Calculate encoding options
85
	 *
86
	 * @return integer
87
	 */
88
	protected function calculateEncodeOptions() {
89
		$options = JSON_UNESCAPED_UNICODE;
90
		if ($this->preserveZeroFractionSupport) {
91
			$options |= JSON_PRESERVE_ZERO_FRACTION;
92
		}
93
		return $options;
94
	}
95
96
	/**
97
	 * Execute post-encoding actions
98
	 *
99
	 * @param string $encoded
100
	 * @return string
101
	 */
102
	protected function processEncodedValue($encoded) {
103
		if (!$this->preserveZeroFractionSupport) {
104
			$encoded = preg_replace('/"' . static::FLOAT_ADAPTER . '\((.*?)\)"/', '\1', $encoded);
105
		}
106
		return $encoded;
107
	}
108
109
	/**
110
	 * Unserialize the value from JSON
111
	 *
112
	 * @param string $value
113
	 * @return mixed
114
	 */
115
	public function unserialize($value) {
116
		$this->reset();
117
		return $this->unserializeData(json_decode($value, true));
118
	}
119
120
	/**
121
	 * Parse the data to be json encoded
122
	 *
123
	 * @param mixed $value
124
	 * @return mixed
125
	 * @throws Zumba\Exception\JsonSerializerException
126
	 */
127
	protected function serializeData($value) {
128
		if (is_scalar($value) || $value === null) {
129
			if (!$this->preserveZeroFractionSupport && is_float($value) && strpos((string)$value, '.') === false) {
130
				// Because the PHP bug #50224, the float numbers with no
131
				// precision numbers are converted to integers when encoded
132
				$value = static::FLOAT_ADAPTER . '(' . $value . '.0)';
133
			}
134
			return $value;
135
		}
136
		if (is_resource($value)) {
137
			throw new JsonSerializerException('Resource is not supported in JsonSerializer');
138
		}
139
		if (is_array($value)) {
140
			return array_map(array($this, __FUNCTION__), $value);
141
		}
142
		if ($value instanceof \Closure) {
143
			if ($this->closureSerializer === null) {
144
				throw new JsonSerializerException('Closure serializer not given. Unable to serialize closure.');
145
			}
146
			return array(
147
				static::CLOSURE_IDENTIFIER_KEY => true,
148
				'value' => $this->closureSerializer->serialize($value)
149
			);
150
		}
151
		return $this->serializeObject($value);
152
	}
153
154
	/**
155
	 * Extract the data from an object
156
	 *
157
	 * @param object $value
158
	 * @return array
159
	 */
160
	protected function serializeObject($value) {
161
		$ref = new ReflectionClass($value);
162
163
		if ($this->objectStorage->contains($value)) {
164
			return array(static::CLASS_IDENTIFIER_KEY => '@' . $this->objectStorage[$value]);
165
		}
166
		$this->objectStorage->attach($value, $this->objectMappingIndex++);
167
168
		$paramsToSerialize = $this->getObjectProperties($ref, $value);
169
		$data = array(static::CLASS_IDENTIFIER_KEY => $ref->getName());
170
		$data += array_map(array($this, 'serializeData'), $this->extractObjectData($value, $ref, $paramsToSerialize));
171
		return $data;
172
	}
173
174
	/**
175
	 * Return the list of properties to be serialized
176
	 *
177
	 * @param ReflectionClass $ref
178
	 * @param object $value
179
	 * @return array
180
	 */
181
	protected function getObjectProperties($ref, $value) {
182
		if (method_exists($value, '__sleep')) {
183
			return $value->__sleep();
184
		}
185
186
		$props = array();
187
		foreach ($ref->getProperties() as $prop) {
188
			$props[] = $prop->getName();
189
		}
190
		return array_unique(array_merge($props, array_keys(get_object_vars($value))));
191
	}
192
193
	/**
194
	 * Extract the object data
195
	 *
196
	 * @param object $value
197
	 * @param ReflectionClass $ref
198
	 * @param array $properties
199
	 * @return array
200
	 */
201
	protected function extractObjectData($value, $ref, $properties) {
202
		$data = array();
203
		foreach ($properties as $property) {
204
			try {
205
				$propRef = $ref->getProperty($property);
206
				$propRef->setAccessible(true);
207
				$data[$property] = $propRef->getValue($value);
208
			} catch (ReflectionException $e) {
209
				$data[$property] = $value->$property;
210
			}
211
		}
212
		return $data;
213
	}
214
215
	/**
216
	 * Parse the json decode to convert to objects again
217
	 *
218
	 * @param mixed $value
219
	 * @return mixed
220
	 */
221
	protected function unserializeData($value) {
222
		if (is_scalar($value) || $value === null) {
223
			return $value;
224
		}
225
226
		if (isset($value[static::CLASS_IDENTIFIER_KEY])) {
227
			return $this->unserializeObject($value);
0 ignored issues
show
Documentation introduced by
$value is of type object|array, but the function expects a object<Zumba\Util\aray>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
228
		}
229
230
		if (!empty($value[static::CLOSURE_IDENTIFIER_KEY])) {
231
			if (!$this->closureSerializer) {
232
				throw new JsonSerializerException('Closure serializer not provided to unserialize closure');
233
			}
234
			return $this->closureSerializer->unserialize($value['value']);
235
		}
236
237
		return array_map(array($this, __FUNCTION__), $value);
238
	}
239
240
	/**
241
	 * Convert the serialized array into an object
242
	 *
243
	 * @param aray $value
244
	 * @return object
245
	 * @throws Zumba\Exception\JsonSerializerException
246
	 */
247
	protected function unserializeObject($value) {
248
		$className = $value[static::CLASS_IDENTIFIER_KEY];
249
		unset($value[static::CLASS_IDENTIFIER_KEY]);
250
251
		if ($className[0] === '@') {
252
			$index = substr($className, 1);
253
			return $this->objectMapping[$index];
254
		}
255
256
		if (!class_exists($className)) {
257
			throw new JsonSerializerException('Unable to find class ' . $className);
258
		}
259
260
		if ($className === 'DateTime') {
261
			$obj = $this->restoreUsingUnserialize($className, $value);
262
			$this->objectMapping[$this->objectMappingIndex++] = $obj;
263
			return $obj;
264
		}
265
266
		$ref = new ReflectionClass($className);
267
		$obj = $ref->newInstanceWithoutConstructor();
268
		$this->objectMapping[$this->objectMappingIndex++] = $obj;
269
		foreach ($value as $property => $propertyValue) {
270
			try {
271
				$propRef = $ref->getProperty($property);
272
				$propRef->setAccessible(true);
273
				$propRef->setValue($obj, $this->unserializeData($propertyValue));
274
			} catch (ReflectionException $e) {
275
				$obj->$property = $this->unserializeData($propertyValue);
276
			}
277
		}
278
		if (method_exists($obj, '__wakeup')) {
279
			$obj->__wakeup();
280
		}
281
		return $obj;
282
	}
283
284
	protected function restoreUsingUnserialize($className, $attributes) {
285
		$obj = (object)$attributes;
286
		$serialized = preg_replace('|^O:\d+:"\w+":|', 'O:' . strlen($className) . ':"' . $className . '":', serialize($obj));
287
		return unserialize($serialized);
288
	}
289
290
	/**
291
	 * Reset variables
292
	 *
293
	 * @return void
294
	 */
295
	protected function reset() {
296
		$this->objectStorage = new SplObjectStorage();
297
		$this->objectMapping = array();
298
		$this->objectMappingIndex = 0;
299
	}
300
301
}
302