Completed
Branch master (fbdded)
by Philip
02:17
created

BinaryUtilities::setEndian()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 2
c 0
b 0
f 0
dl 0
loc 5
rs 10
cc 1
nc 1
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace PBurggraf\BinaryUtilities;
6
7
use PBurggraf\BinaryUtilities\DataType\AbstractDataType;
8
use PBurggraf\BinaryUtilities\EndianType\AbstractEndianType;
9
use PBurggraf\BinaryUtilities\EndianType\BigEndian;
10
use PBurggraf\BinaryUtilities\Exception\ContentOnlyException;
11
use PBurggraf\BinaryUtilities\Exception\DataTypeDoesNotExistsException;
12
use PBurggraf\BinaryUtilities\Exception\EndianTypeDoesNotExistsException;
13
use PBurggraf\BinaryUtilities\Exception\FileDoesNotExistsException;
14
use PBurggraf\BinaryUtilities\Exception\FileErrorException;
15
use PBurggraf\BinaryUtilities\Exception\FileNotAccessableException;
16
use PBurggraf\BinaryUtilities\Exception\InvalidDataTypeException;
17
18
/**
19
 * @author Philip Burggraf <[email protected]>
20
 */
21
class BinaryUtilities
22
{
23
    public const BASE_BINARY = 2;
24
    public const BASE_OCTAL = 7;
25
    public const BASE_DECIMAL = 10;
26
    public const BASE_HEXADECIMAL = 16;
27
28
    /**
29
     * @var string|null
30
     */
31
    protected $file;
32
33
    /**
34
     * @var string
35
     */
36
    protected $content;
37
38
    /**
39
     * @var int
40
     */
41
    protected $endOfFile;
42
43
    /**
44
     * @var int
45
     */
46
    protected $base = self::BASE_DECIMAL;
47
48
    /**
49
     * @var array
50
     */
51
    protected $buffer = [];
52
53
    /**
54
     * @var int
55
     */
56
    protected $currentBit;
57
58
    /**
59
     * @var int
60
     */
61
    protected $offset = 0;
62
63
    /**
64
     * @var string
65
     */
66
    protected $endian;
67
68
    /**
69
     * @var AbstractDataType[]
70
     */
71
    protected $dataTypeClasses = [];
72
73
    /**
74
     * @param string $file
75
     *
76
     * @throws FileErrorException
77
     * @throws FileNotAccessableException
78
     * @throws FileNotAccessableException
79
     * @throws FileDoesNotExistsException
80
     *
81
     * @return BinaryUtilities
82
     */
83
    public function setFile(string $file): BinaryUtilities
84
    {
85
        if (!file_exists($file)) {
86
            throw new FileDoesNotExistsException();
87
        }
88
89
        $this->file = $file;
90
91
        $filesize = filesize($this->file);
92
93
        if ($filesize === false) {
94
            throw new FileErrorException();
95
        }
96
97
        $this->endOfFile = $filesize;
98
        $handle = fopen($this->file, 'rb');
99
100
        if ($handle === false) {
101
            throw new FileNotAccessableException();
102
        }
103
104
        $content = fread($handle, $this->endOfFile);
105
106
        if ($content === false) {
107
            throw new FileErrorException();
108
        }
109
110
        $this->setData($content);
111
112
        fclose($handle);
113
114
        return $this;
115
    }
116
117
    /**
118
     * @param string $content
119
     *
120
     * @return BinaryUtilities
121
     */
122
    public function setContent(string $content): BinaryUtilities
123
    {
124
        $this->file = null;
125
126
        $this->endOfFile = strlen($content);
127
128
        $this->setData($content);
129
130
        return $this;
131
    }
132
133
    /**
134
     * @param string $mode
135
     *
136
     * @return BinaryUtilities
137
     */
138
    public function setEndian(string $mode): BinaryUtilities
139
    {
140
        $this->endian = $mode;
141
142
        return $this;
143
    }
144
145
    /**
146
     * @param int $offset
147
     *
148
     * @return BinaryUtilities
149
     */
150
    public function setOffset(int $offset): BinaryUtilities
151
    {
152
        $this->offset = $offset;
153
154
        return $this;
155
    }
156
157
    /**
158
     * @param int $offset
159
     *
160
     * @return BinaryUtilities
161
     *
162
     * @deprecated since v0.5.0, use "setOffset" instead
163
     */
164
    public function offset(int $offset): BinaryUtilities
165
    {
166
        @trigger_error(sprintf('The "%s()" method is deprecated since v0.5.0, use "setOffset" instead.', __METHOD__), E_USER_DEPRECATED);
167
168
        return $this->setOffset($offset);
169
    }
170
171
    /**
172
     * @return int
173
     */
174
    public function returnOffset(): int
175
    {
176
        return $this->offset;
177
    }
178
179
    /**
180
     * @param int $base
181
     *
182
     * @return BinaryUtilities
183
     */
184
    public function setBase(int $base = self::BASE_DECIMAL): BinaryUtilities
185
    {
186
        $this->base = $base;
187
188
        return $this;
189
    }
190
191
    /**
192
     * @param bool $clearBuffer
193
     * @param bool $applyConvertToBase
194
     *
195
     * @return array
196
     */
197
    public function returnBuffer(bool $clearBuffer = true, bool $applyConvertToBase = true): array
198
    {
199
        $buffer = $this->buffer;
200
201
        if ($clearBuffer) {
202
            $this->buffer = [];
203
        }
204
205
        if ($applyConvertToBase === true) {
206
            return array_map([$this, 'convertToBase'], $buffer);
207
        }
208
209
        return $buffer;
210
    }
211
212
    /**
213
     * @param string $dataClass
214
     * @param bool   $clearBuffer
215
     * @param bool   $applyConvertToBase
216
     *
217
     * @throws EndianTypeDoesNotExistsException
218
     * @throws InvalidDataTypeException
219
     * @throws DataTypeDoesNotExistsException
220
     *
221
     * @return string
222
     */
223
    public function readReturn(string $dataClass, bool $clearBuffer = true, bool $applyConvertToBase = true): string
224
    {
225
        return $this->read($dataClass)->returnBuffer($clearBuffer, $applyConvertToBase)[0];
226
    }
227
228
    /**
229
     * @param int    $offset
230
     * @param string $dataClass
231
     * @param bool   $clearBuffer
232
     * @param bool   $applyConvertToBase
233
     *
234
     * @throws EndianTypeDoesNotExistsException
235
     * @throws InvalidDataTypeException
236
     * @throws DataTypeDoesNotExistsException
237
     *
238
     * @return string
239
     */
240
    public function readAndReturnFromOffset(int $offset, string $dataClass, bool $clearBuffer = true, bool $applyConvertToBase = true): string
241
    {
242
        return $this->setOffset($offset)->readReturn($dataClass, $clearBuffer, $applyConvertToBase);
243
    }
244
245
    /**
246
     * @param string $dataClass
247
     *
248
     * @throws EndianTypeDoesNotExistsException
249
     * @throws InvalidDataTypeException
250
     * @throws DataTypeDoesNotExistsException
251
     *
252
     * @return BinaryUtilities
253
     */
254
    public function read(string $dataClass): BinaryUtilities
255
    {
256
        $dataType = $this->getDataType($dataClass);
257
258
        $dataType->setContent($this->content);
259
        $dataType->setOffset($this->offset);
260
        $dataType->setEndianMode($this->getEndianType($this->endian));
261
262
        $this->buffer = array_merge($this->buffer, $dataType->read());
263
264
        $this->offset = $dataType->newOffset();
265
266
        return $this;
267
    }
268
269
    /**
270
     * @param string $dataClass
271
     * @param int    $length
272
     *
273
     * @throws EndianTypeDoesNotExistsException
274
     * @throws InvalidDataTypeException
275
     * @throws DataTypeDoesNotExistsException
276
     *
277
     * @return BinaryUtilities
278
     */
279
    public function readArray(string $dataClass, int $length): BinaryUtilities
280
    {
281
        $dataType = $this->getDataType($dataClass);
282
283
        $dataType->setContent($this->content);
284
        $dataType->setOffset($this->offset);
285
        $dataType->setEndianMode($this->getEndianType($this->endian));
286
287
        $this->buffer = array_merge($this->buffer, $dataType->readArray($length));
288
289
        $this->offset = $dataType->newOffset();
290
291
        return $this;
292
    }
293
294
    /**
295
     * @param string           $dataClass
296
     * @param float|int|string $data
297
     *
298
     * @throws EndianTypeDoesNotExistsException
299
     * @throws InvalidDataTypeException
300
     * @throws DataTypeDoesNotExistsException
301
     *
302
     * @return BinaryUtilities
303
     */
304
    public function write(string $dataClass, $data): BinaryUtilities
305
    {
306
        $dataType = $this->getDataType($dataClass);
307
308
        $dataType->setContent($this->content);
309
        $dataType->setOffset($this->offset);
310
        $dataType->setEndianMode($this->getEndianType($this->endian));
311
312
        $dataType->write($data);
0 ignored issues
show
Bug introduced by
It seems like $data can also be of type double and string; however, parameter $data of PBurggraf\BinaryUtilitie...stractDataType::write() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

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

312
        $dataType->write(/** @scrutinizer ignore-type */ $data);
Loading history...
313
314
        $this->content = $dataType->newContent();
315
        $this->offset = $dataType->newOffset();
316
317
        return $this;
318
    }
319
320
    /**
321
     * @param string $dataClass
322
     * @param array  $data
323
     *
324
     * @throws EndianTypeDoesNotExistsException
325
     * @throws InvalidDataTypeException
326
     * @throws DataTypeDoesNotExistsException
327
     *
328
     * @return BinaryUtilities
329
     */
330
    public function writeArray(string $dataClass, array $data): BinaryUtilities
331
    {
332
        $dataType = $this->getDataType($dataClass);
333
334
        $dataType->setContent($this->content);
335
        $dataType->setOffset($this->offset);
336
        $dataType->setEndianMode($this->getEndianType($this->endian));
337
338
        $dataType->writeArray($data);
339
340
        $this->content = $dataType->newContent();
341
        $this->offset = $dataType->newOffset();
342
343
        return $this;
344
    }
345
346
    /**
347
     * @throws ContentOnlyException
348
     * @throws FileNotAccessableException
349
     */
350
    public function save(): void
351
    {
352
        if ($this->file === null) {
353
            throw new ContentOnlyException();
354
        }
355
356
        $handle = fopen($this->file, 'wb');
357
358
        if ($handle === false) {
359
            throw new FileNotAccessableException();
360
        }
361
362
        fwrite($handle, $this->content);
363
        fclose($handle);
364
    }
365
366
    /**
367
     * @param string $dataClass
368
     *
369
     * @throws DataTypeDoesNotExistsException
370
     * @throws InvalidDataTypeException
371
     *
372
     * @return AbstractDataType
373
     */
374
    private function getDataType(string $dataClass): AbstractDataType
375
    {
376
        if (!class_exists($dataClass)) {
377
            throw new DataTypeDoesNotExistsException();
378
        }
379
380
        if (array_key_exists($dataClass, $this->dataTypeClasses)) {
381
            /** @var AbstractDataType $type */
382
            $type = $this->dataTypeClasses[$dataClass];
383
        } else {
384
            /** @var AbstractDataType $type */
385
            $type = new $dataClass();
386
387
            if (!$type instanceof AbstractDataType) {
0 ignored issues
show
introduced by
$type is always a sub-type of PBurggraf\BinaryUtilitie...taType\AbstractDataType.
Loading history...
388
                throw new InvalidDataTypeException();
389
            }
390
391
            $this->dataTypeClasses[$dataClass] = $type;
392
        }
393
394
        return $type;
395
    }
396
397
    /**
398
     * @param string|null $entianType
399
     *
400
     * @throws EndianTypeDoesNotExistsException
401
     *
402
     * @return AbstractEndianType
403
     */
404
    private function getEndianType(?string $entianType): AbstractEndianType
405
    {
406
        if ($entianType === null) {
407
            $entianType = BigEndian::class;
408
        }
409
410
        if (!class_exists($entianType)) {
411
            throw new EndianTypeDoesNotExistsException();
412
        }
413
414
        return new $entianType();
415
    }
416
417
    /**
418
     * @param int $value
419
     *
420
     * @return string
421
     */
422
    private function convertToBase(int $value): string
423
    {
424
        return base_convert((string) $value, 10, $this->base);
425
    }
426
427
    /**
428
     * @param string $content
429
     */
430
    private function setData(string $content): void
431
    {
432
        $this->currentBit = 0;
433
        $this->offset = 0;
434
        $this->content = $content;
435
    }
436
}
437