Completed
Push — master ( bbb742...e4897b )
by smiley
02:38
created

ByteArrayDispenser::fromJSON()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 1
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * Trait ByteArrayDispenser
4
 *
5
 * @filesource   ByteArrayDispenser.php
6
 * @created      05.12.2017
7
 * @package      chillerlan\Traits\ArrayHelpers
8
 * @author       Smiley <[email protected]>
9
 * @copyright    2017 Smiley
10
 * @license      MIT
11
 */
12
13
namespace chillerlan\Traits\ArrayHelpers;
14
15
use chillerlan\Traits\TraitException;
16
use Exception;
17
use SplFixedArray;
18
use Traversable;
19
20
/**
21
 */
22
class ByteArrayDispenser{
23
24
	/**
25
	 * @var string
26
	 */
27
	protected $byteArrayClass = ByteArray::class;
28
29
	/**
30
	 * @param int $int
31
	 *
32
	 * @return bool
33
	 */
34
	public function isAllowedInt(int $int):bool{
35
		return $int > 0 && $int <= PHP_INT_MAX;
36
	}
37
38
	/**
39
	 * @param int $size
40
	 *
41
	 * @return \chillerlan\Traits\ArrayHelpers\ByteArray
42
	 * @throws \chillerlan\Traits\TraitException
43
	 */
44
	public function fromIntSize(int $size):ByteArray{
45
46
		if(!$this->isAllowedInt($size)){
47
			throw new TraitException('invalid size');
48
		}
49
50
		return new $this->byteArrayClass($size);
51
	}
52
53
	/**
54
	 * @param array $array
55
	 * @param bool  $save_indexes
56
	 *
57
	 * @return \chillerlan\Traits\ArrayHelpers\ByteArray
58
	 * @throws \chillerlan\Traits\TraitException
59
	 */
60
	public function fromArray($array, $save_indexes = null):ByteArray{ // @todo \Traversable, $target, allow variable arrays, iterators etc
61
62
		try{
63
			$out = $this->fromIntSize(count($array));
64
65
			$array = ($save_indexes !== null ? $save_indexes : true) ? $array : array_values($array);
66
67
			foreach($array as $k => $v){
68
				$out[$k] = $v;
69
			}
70
71
			return $out;
72
		}
73
		catch(Exception $e){
74
			throw new TraitException($e->getMessage());
75
		}
76
77
	}
78
79
	/**
80
	 * @param int $start
81
	 * @param int $len
82
	 *
83
	 * @return \chillerlan\Traits\ArrayHelpers\ByteArray
84
	 * @throws \chillerlan\Traits\TraitException
85
	 */
86
	public function fromRange(int $start, int $len):ByteArray{
87
88
		if(!$this->isAllowedInt($start) || $start < 0){
89
			throw new TraitException('invalid start');
90
		}
91
92
		return $this->fromArray(array_fill($start, $len, 0));
93
	}
94
95
	/**
96
	 * @param string $str
97
	 *
98
	 * @return \chillerlan\Traits\ArrayHelpers\ByteArray
99
	 */
100
	public function fromString(string $str):ByteArray{
101
		return $this->fromArray(unpack('C*', $str), false);
0 ignored issues
show
Bug introduced by
The call to unpack() has too few arguments starting with offset. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

101
		return $this->fromArray(/** @scrutinizer ignore-call */ unpack('C*', $str), false);

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
102
	}
103
104
	/**
105
	 * checks if the given string is a hex string: ab12cd34 (case insensitive, whitespace allowed)
106
	 *
107
	 * @param string $hex
108
	 *
109
	 * @return bool
110
	 */
111
	public function isAllowedHex(string $hex):bool{
112
		return preg_match('/^[\s\r\n\t \da-f]+$/i', $hex) && strlen($hex) % 2 === 0;
113
	}
114
115
	/**
116
	 * @param string $hex
117
	 *
118
	 * @return \chillerlan\Traits\ArrayHelpers\ByteArray|mixed
119
	 * @throws \chillerlan\Traits\TraitException
120
	 */
121 View Code Duplication
	public function fromHex(string $hex):ByteArray{
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
122
		$hex = preg_replace('/[\s\r\n\t ]/', '', $hex);
123
124
		if(!$this->isAllowedHex($hex)){
125
			throw new TraitException('invalid hex string');
126
		}
127
128
		return $this->fromString(pack('H*', $hex));
129
	}
130
131
	/**
132
	 * checks if the given (trimmed) JSON string is a an array that contains numbers: [1, 2, 3]
133
	 *
134
	 * @param string $json
135
	 *
136
	 * @return bool
137
	 */
138
	public function isAllowedJSON(string $json):bool{
139
		return preg_match('/^\\[[\s\d,]+\\]$/', $json) > 0;
140
	}
141
142
	/**
143
	 * @param string $json
144
	 *
145
	 * @return \chillerlan\Traits\ArrayHelpers\ByteArray|mixed
146
	 * @throws \chillerlan\Traits\TraitException
147
	 */
148
	public function fromJSON(string $json):ByteArray{
149
		$json = trim($json);
150
151
		if(!$this->isAllowedJSON($json)){
152
			throw new TraitException('invalid JSON array');
153
		}
154
155
		return $this->fromArray(json_decode(trim($json)));
156
	}
157
158
	/**
159
	 * checks if the given (trimmed) string is base64 encoded binary
160
	 *
161
	 * @param string $base64
162
	 *
163
	 * @return bool
164
	 */
165
	public function isAllowedBase64(string $base64):bool{
166
		return preg_match('#^[a-z\d/]*={0,2}$#i', $base64) > 0;
167
	}
168
169
	/**
170
	 * @param string $base64
171
	 *
172
	 * @return \chillerlan\Traits\ArrayHelpers\ByteArray|mixed
173
	 * @throws \chillerlan\Traits\TraitException
174
	 */
175 View Code Duplication
	public function fromBase64(string $base64):ByteArray{
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
176
		$base64 = trim($base64);
177
178
		if(!$this->isAllowedBase64($base64)){
179
			throw new TraitException('invalid base64 string');
180
		}
181
182
		return $this->fromString(base64_decode($base64));
183
	}
184
185
	/**
186
	 * checks if the given (trimmed) string is a binary string: [01] in multiples of 8
187
	 *
188
	 * @param string $bin
189
	 *
190
	 * @return bool
191
	 */
192
	public function isAllowedBin(string $bin):bool{
193
		return preg_match('/^[01]+$/', $bin) > 0 && strlen($bin) % 8 === 0;
194
	}
195
196
	/**
197
	 * @param string $bin
198
	 *
199
	 * @return \chillerlan\Traits\ArrayHelpers\ByteArray
200
	 */
201
	public function fromBin(string $bin):ByteArray{
202
		return $this->fromArray(array_map('bindec', str_split($bin, 8)));
203
	}
204
205
	/**
206
	 * @param string|array|\SplFixedArray $data
207
	 *
208
	 * @return \chillerlan\Traits\ArrayHelpers\ByteArray
209
	 * @throws \chillerlan\Traits\TraitException
210
	 */
211
	public function guessFrom($data):ByteArray{
212
213
		if($data instanceof Traversable){
214
			return $this->fromArray(iterator_to_array($data));
215
		}
216
217
		if(is_array($data)){
218
			return $this->fromArray($data);
219
		}
220
221
		if(is_string($data)){
222
223
			foreach(['Hex', 'Bin', 'JSON', 'Base64'] as $type){
224
225
				if($this->{'isAllowed'.$type}($data)){
226
					return $this->{'from'.$type}($data);
227
				}
228
229
			}
230
231
			return $this->fromString($data);
232
		}
233
234
		throw new TraitException('invalid input');
235
	}
236
237
}
238