Passed
Push — master ( d9e5dd...36764d )
by Spuds
01:07 queued 26s
created

_safe_unserialize()   F

Complexity

Conditions 40
Paths 392

Size

Total Lines 156
Code Lines 86

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 40
eloc 86
dl 0
loc 156
rs 0.9333
c 0
b 0
f 0
nc 392
nop 1

How to fix   Long Method    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
 * @author anthon (dpt) pang (at) gmail (dot) com
4
 * @license Public Domain
5
 */
6
7
namespace ElkArte\ext\upgradephp;
8
use \Exception;
0 ignored issues
show
Bug introduced by
The type \Exception was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
9
10
/*
11
 * Used to impose practical limits for safe_unserialize()
12
 */
13
if (!defined('MAX_SERIALIZED_INPUT_LENGTH'))
14
{
15
	define('MAX_SERIALIZED_INPUT_LENGTH', 4194304);
16
}
17
if (!defined('MAX_SERIALIZED_ARRAY_LENGTH'))
18
{
19
	define('MAX_SERIALIZED_ARRAY_LENGTH', 2048);
20
}
21
if (!defined('MAX_SERIALIZED_ARRAY_DEPTH'))
22
{
23
	define('MAX_SERIALIZED_ARRAY_DEPTH', 24);
24
}
25
26
27
/**
28
 * safe_serialize implementation
29
 *
30
 * @param mixed $value
31
 * @return string
32
 * @throw Exception if $value is malformed or contains unsupported types (e.g., resources, objects)
33
 */
34
function _safe_serialize( $value )
35
{
36
	if(is_null($value))
37
	{
38
		return 'N;';
39
	}
40
	if(is_bool($value))
41
	{
42
		return 'b:'.(int)$value.';';
43
	}
44
	if(is_int($value))
45
	{
46
		return 'i:'.$value.';';
47
	}
48
	if(is_float($value))
49
	{
50
		return 'd:'.$value.';';
51
	}
52
	if(is_string($value))
53
	{
54
		return 's:'.strlen($value).':"'.$value.'";';
55
	}
56
	if(is_array($value))
57
	{
58
		$out = '';
59
		foreach($value as $k => $v)
60
		{
61
			$out .= _safe_serialize($k) . _safe_serialize($v);
62
		}
63
64
		return 'a:'.count($value).':{'.$out.'}';
65
	}
66
	if(is_resource($value))
67
	{
68
		// built-in returns 'i:0;'
69
		throw new \Exception('safe_serialize: resources not supported');
70
	}
71
	if(is_object($value) || gettype($value) == 'object')
72
	{
73
		throw new \Exception('safe_serialize: objects not supported');
74
	}
75
	throw new \Exception('safe_serialize cannot serialize: '.gettype($value));
76
}
77
78
/**
79
 * Safe serialize() replacement
80
 * - output a strict subset of PHP's native serialized representation
81
 *
82
 * @param mixed $value
83
 * @return string
84
 */
85
function safe_serialize( $value )
86
{
87
	// ensure we use the byte count for strings even when strlen() is overloaded by mb_strlen()
88
	if (function_exists('mb_internal_encoding') &&
89
		(((int) ini_get('mbstring.func_overload')) & 2))
90
	{
91
		$mbIntEnc = mb_internal_encoding();
92
		mb_internal_encoding('ASCII');
93
	}
94
95
	try {
96
		$out = _safe_serialize($value);
97
	} catch(Exception $e) {
98
		$out = false;
99
	}
100
101
	if (isset($mbIntEnc))
102
	{
103
		mb_internal_encoding($mbIntEnc);
104
	}
105
	return $out;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $out could also return false which is incompatible with the documented return type string. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
106
}
107
108
/**
109
 * safe_unserialize implementation
110
 *
111
 * @param string $str
112
 * @return mixed
113
 * @throw Exception if $str is malformed or contains unsupported types (e.g., resources, objects)
114
 */
115
function _safe_unserialize($str)
116
{
117
	if(strlen($str) > MAX_SERIALIZED_INPUT_LENGTH)
118
	{
119
		throw new \Exception('safe_unserialize: input exceeds ' . MAX_SERIALIZED_INPUT_LENGTH);
120
	}
121
122
	if(empty($str) || !is_string($str))
123
	{
124
		return false;
125
	}
126
127
	$stack = array();
128
	$expected = array();
129
	$state = 0;
130
131
	while($state != 1)
132
	{
133
		$type = isset($str[0]) ? $str[0] : '';
134
135
		if($type == '}')
136
		{
137
			$str = substr($str, 1);
138
		}
139
		else if($type == 'N' && $str[1] == ';')
140
		{
141
			$value = null;
142
			$str = substr($str, 2);
143
		}
144
		else if($type == 'b' && preg_match('/^b:([01]);/', $str, $matches))
145
		{
146
			$value = $matches[1] == '1' ? true : false;
147
			$str = substr($str, 4);
148
		}
149
		else if($type == 'i' && preg_match('/^i:(-?[0-9]+);(.*)/s', $str, $matches))
150
		{
151
			$value = (int)$matches[1];
152
			$str = $matches[2];
153
		}
154
		else if($type == 'd' && preg_match('/^d:(-?[0-9]+\.?[0-9]*(E[+-][0-9]+)?);(.*)/s', $str, $matches))
155
		{
156
			$value = (float)$matches[1];
157
			$str = $matches[3];
158
		}
159
		else if($type == 's' && preg_match('/^s:([0-9]+):"(.*)/s', $str, $matches) && substr($matches[2], (int)$matches[1], 2) == '";')
160
		{
161
			$value = substr($matches[2], 0, (int)$matches[1]);
162
			$str = substr($matches[2], (int)$matches[1] + 2);
163
		}
164
		else if($type == 'a' && preg_match('/^a:([0-9]+):{(.*)/s', $str, $matches) && $matches[1] < MAX_SERIALIZED_ARRAY_LENGTH)
165
		{
166
			$expectedLength = (int)$matches[1];
167
			$str = $matches[2];
168
		}
169
		else if($type == 'O')
170
		{
171
			throw new \Exception('safe_unserialize: objects not supported');
172
		}
173
		else
174
		{
175
			throw new \Exception('safe_unserialize: unknown/malformed type: '.$type);
176
		}
177
178
		switch($state)
179
		{
180
			case 3: // in array, expecting value or another array
181
				if($type == 'a')
182
				{
183
					if(count($stack) >= MAX_SERIALIZED_ARRAY_DEPTH)
184
					{
185
						throw new \Exception('safe_unserialize: array nesting exceeds ' . MAX_SERIALIZED_ARRAY_DEPTH);
186
					}
187
188
					$stack[] = &$list;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $list does not seem to be defined for all execution paths leading up to this point.
Loading history...
189
					$list[$key] = array();
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $key does not seem to be defined for all execution paths leading up to this point.
Loading history...
190
					$list = &$list[$key];
191
					$expected[] = $expectedLength;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $expectedLength does not seem to be defined for all execution paths leading up to this point.
Loading history...
192
					$state = 2;
193
					break;
194
				}
195
				if($type != '}')
196
				{
197
					$list[$key] = $value;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $value does not seem to be defined for all execution paths leading up to this point.
Loading history...
198
					$state = 2;
199
					break;
200
				}
201
202
				throw new \Exception('safe_unserialize: missing array value');
203
204
			case 2: // in array, expecting end of array or a key
205
				if($type == '}')
206
				{
207
					if(count($list) < end($expected))
208
					{
209
						throw new \Exception('safe_unserialize: array size less than expected ' . $expected[0]);
210
					}
211
212
					unset($list);
213
					$list = &$stack[count($stack)-1];
214
					array_pop($stack);
215
216
					// go to terminal state if we're at the end of the root array
217
					array_pop($expected);
218
					if(count($expected) == 0) {
219
						$state = 1;
220
					}
221
					break;
222
				}
223
				if($type == 'i' || $type == 's')
224
				{
225
					if(count($list) >= MAX_SERIALIZED_ARRAY_LENGTH)
226
					{
227
						throw new \Exception('safe_unserialize: array size exceeds ' . MAX_SERIALIZED_ARRAY_LENGTH);
228
					}
229
					if(count($list) >= end($expected))
230
					{
231
						throw new \Exception('safe_unserialize: array size exceeds expected length');
232
					}
233
234
					$key = $value;
235
					$state = 3;
236
					break;
237
				}
238
239
				throw new \Exception('safe_unserialize: illegal array index type');
240
241
			case 0: // expecting array or value
242
				if($type == 'a')
243
				{
244
					if(count($stack) >= MAX_SERIALIZED_ARRAY_DEPTH)
245
					{
246
						throw new \Exception('safe_unserialize: array nesting exceeds ' . MAX_SERIALIZED_ARRAY_DEPTH);
247
					}
248
249
					$data = array();
250
					$list = &$data;
251
					$expected[] = $expectedLength;
252
					$state = 2;
253
					break;
254
				}
255
				if($type != '}')
256
				{
257
					$data = $value;
258
					$state = 1;
259
					break;
260
				}
261
262
				throw new \Exception('safe_unserialize: not in array');
263
		}
264
	}
265
266
	if(!empty($str))
267
	{
268
		throw new \Exception('safe_unserialize: trailing data in input');
269
	}
270
	return $data;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $data does not seem to be defined for all execution paths leading up to this point.
Loading history...
271
}
272
273
/**
274
 * Safe unserialize() replacement
275
 * - accepts a strict subset of PHP's native serialized representation
276
 *
277
 * @param string $str
278
 * @return mixed
279
 */
280
function safe_unserialize( $str )
281
{
282
	// ensure we use the byte count for strings even when strlen() is overloaded by mb_strlen()
283
	if (function_exists('mb_internal_encoding') &&
284
		(((int) ini_get('mbstring.func_overload')) & 2))
285
	{
286
		$mbIntEnc = mb_internal_encoding();
287
		mb_internal_encoding('ASCII');
288
	}
289
290
	try {
291
		$out = _safe_unserialize($str);
292
	} catch(Exception $e) {
293
		$out = '';
294
	}
295
296
	if (isset($mbIntEnc))
297
	{
298
		mb_internal_encoding($mbIntEnc);
299
	}
300
	return $out;
301
}
302
303
?>
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...