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 — master ( 4f62fd...0a00a9 )
by Chris
9s
created

JsonSerializer::serializeData()   D

Complexity

Conditions 10
Paths 7

Size

Total Lines 26
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 7
Bugs 1 Features 2
Metric Value
c 7
b 1
f 2
dl 0
loc 26
rs 4.8196
cc 10
eloc 16
nc 7
nop 1

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
		$this->closureSerializer = $closureSerializer;
0 ignored issues
show
Documentation Bug introduced by
It seems like $closureSerializer can also be of type object<SuperClosure\SerializerInterface>. However, the property $closureSerializer is declared as type object<Zumba\Util\SuperC...re\SerializerInterface>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

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