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
Pull Request — master (#15)
by Juan
01:54
created

JsonSerializer::unserialize()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 2
Metric Value
c 3
b 0
f 2
dl 0
loc 4
rs 10
cc 1
eloc 3
nc 1
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
			$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...
64
		}
65
	}
66
67
	/**
68
	 * Serialize the value in JSON
69
	 *
70
	 * @param mixed $value
71
	 * @return string JSON encoded
72
	 * @throws Zumba\Exception\JsonSerializerException
73
	 */
74
	public function serialize($value) {
75
		$this->reset();
76
		$encoded = json_encode($this->serializeData($value), $this->calculateEncodeOptions());
77
		return $this->processEncodedValue($encoded);
78
	}
79
80
	/**
81
	 * Calculate encoding options
82
	 *
83
	 * @return integer
84
	 */
85
	protected function calculateEncodeOptions() {
86
		$options = JSON_UNESCAPED_UNICODE;
87
		if ($this->preserveZeroFractionSupport) {
88
			$options |= JSON_PRESERVE_ZERO_FRACTION;
89
		}
90
		return $options;
91
	}
92
93
	/**
94
	 * Execute post-encoding actions
95
	 *
96
	 * @param string $encoded
97
	 * @return string
98
	 */
99
	protected function processEncodedValue($encoded) {
100
		if (!$this->preserveZeroFractionSupport) {
101
			$encoded = preg_replace('/"' . static::FLOAT_ADAPTER . '\((.*?)\)"/', '\1', $encoded);
102
		}
103
		return $encoded;
104
	}
105
106
	/**
107
	 * Unserialize the value from JSON
108
	 *
109
	 * @param string $value
110
	 * @return mixed
111
	 */
112
	public function unserialize($value) {
113
		$this->reset();
114
		return $this->unserializeData(json_decode($value, true));
115
	}
116
117
	/**
118
	 * Parse the data to be json encoded
119
	 *
120
	 * @param mixed $value
121
	 * @return mixed
122
	 * @throws Zumba\Exception\JsonSerializerException
123
	 */
124
	protected function serializeData($value) {
125
		if (is_scalar($value) || $value === null) {
126
			if (!$this->preserveZeroFractionSupport && is_float($value) && strpos((string)$value, '.') === false) {
127
				// Because the PHP bug #50224, the float numbers with no
128
				// precision numbers are converted to integers when encoded
129
				$value = static::FLOAT_ADAPTER . '(' . $value . '.0)';
130
			}
131
			return $value;
132
		}
133
		if (is_resource($value)) {
134
			throw new JsonSerializerException('Resource is not supported in JsonSerializer');
135
		}
136
		if (is_array($value)) {
137
			return array_map(array($this, __FUNCTION__), $value);
138
		}
139
		if ($value instanceof \Closure) {
140
			if ($this->closureSerializer === null) {
141
				throw new JsonSerializerException('Closure serializer not given. Unable to serialize closure.');
142
			}
143
			return array(
144
				static::CLOSURE_IDENTIFIER_KEY => true,
145
				'value' => $this->closureSerializer->serialize($value)
146
			);
147
		}
148
		return $this->serializeObject($value);
149
	}
150
151
	/**
152
	 * Extract the data from an object
153
	 *
154
	 * @param object $value
155
	 * @return array
156
	 */
157
	protected function serializeObject($value) {
158
		$ref = new ReflectionClass($value);
159
160
		if ($this->objectStorage->contains($value)) {
161
			return array(static::CLASS_IDENTIFIER_KEY => '@' . $this->objectStorage[$value]);
162
		}
163
		$this->objectStorage->attach($value, $this->objectMappingIndex++);
164
165
		$paramsToSerialize = $this->getObjectProperties($ref, $value);
166
		$data = array(static::CLASS_IDENTIFIER_KEY => $ref->getName());
167
		$data += array_map(array($this, 'serializeData'), $this->extractObjectData($value, $ref, $paramsToSerialize));
168
		return $data;
169
	}
170
171
	/**
172
	 * Return the list of properties to be serialized
173
	 *
174
	 * @param ReflectionClass $ref
175
	 * @param object $value
176
	 * @return array
177
	 */
178
	protected function getObjectProperties($ref, $value) {
179
		if (method_exists($value, '__sleep')) {
180
			return $value->__sleep();
181
		}
182
183
		$props = array();
184
		foreach ($ref->getProperties() as $prop) {
185
			$props[] = $prop->getName();
186
		}
187
		return array_unique(array_merge($props, array_keys(get_object_vars($value))));
188
	}
189
190
	/**
191
	 * Extract the object data
192
	 *
193
	 * @param object $value
194
	 * @param ReflectionClass $ref
195
	 * @param array $properties
196
	 * @return array
197
	 */
198
	protected function extractObjectData($value, $ref, $properties) {
199
		$data = array();
200
		foreach ($properties as $property) {
201
			try {
202
				$propRef = $ref->getProperty($property);
203
				$propRef->setAccessible(true);
204
				$data[$property] = $propRef->getValue($value);
205
			} catch (ReflectionException $e) {
206
				$data[$property] = $value->$property;
207
			}
208
		}
209
		return $data;
210
	}
211
212
	/**
213
	 * Parse the json decode to convert to objects again
214
	 *
215
	 * @param mixed $value
216
	 * @return mixed
217
	 */
218
	protected function unserializeData($value) {
219
		if (is_scalar($value) || $value === null) {
220
			return $value;
221
		}
222
223
		if (isset($value[static::CLASS_IDENTIFIER_KEY])) {
224
			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...
225
		}
226
227
		if (!empty($value[static::CLOSURE_IDENTIFIER_KEY])) {
228
			if (!$this->closureSerializer) {
229
				throw new JsonSerializerException('Closure serializer not provided to unserialize closure');
230
			}
231
			return $this->closureSerializer->unserialize($value['value']);
232
		}
233
234
		return array_map(array($this, __FUNCTION__), $value);
235
	}
236
237
	/**
238
	 * Convert the serialized array into an object
239
	 *
240
	 * @param aray $value
241
	 * @return object
242
	 * @throws Zumba\Exception\JsonSerializerException
243
	 */
244
	protected function unserializeObject($value) {
245
		$className = $value[static::CLASS_IDENTIFIER_KEY];
246
		unset($value[static::CLASS_IDENTIFIER_KEY]);
247
248
		if ($className[0] === '@') {
249
			$index = substr($className, 1);
250
			return $this->objectMapping[$index];
251
		}
252
253
		if (!class_exists($className)) {
254
			throw new JsonSerializerException('Unable to find class ' . $className);
255
		}
256
257
		if ($className === 'DateTime') {
258
			$obj = $this->restoreUsingUnserialize($className, $value);
259
			$this->objectMapping[$this->objectMappingIndex++] = $obj;
260
			return $obj;
261
		}
262
263
		$ref = new ReflectionClass($className);
264
		$obj = $ref->newInstanceWithoutConstructor();
265
		$this->objectMapping[$this->objectMappingIndex++] = $obj;
266
		foreach ($value as $property => $propertyValue) {
267
			try {
268
				$propRef = $ref->getProperty($property);
269
				$propRef->setAccessible(true);
270
				$propRef->setValue($obj, $this->unserializeData($propertyValue));
271
			} catch (ReflectionException $e) {
272
				$obj->$property = $this->unserializeData($propertyValue);
273
			}
274
		}
275
		if (method_exists($obj, '__wakeup')) {
276
			$obj->__wakeup();
277
		}
278
		return $obj;
279
	}
280
281
	protected function restoreUsingUnserialize($className, $attributes) {
282
		$obj = (object)$attributes;
283
		$serialized = preg_replace('|^O:\d+:"\w+":|', 'O:' . strlen($className) . ':"' . $className . '":', serialize($obj));
284
		return unserialize($serialized);
285
	}
286
287
	/**
288
	 * Reset variables
289
	 *
290
	 * @return void
291
	 */
292
	protected function reset() {
293
		$this->objectStorage = new SplObjectStorage();
294
		$this->objectMapping = array();
295
		$this->objectMappingIndex = 0;
296
	}
297
298
}
299