Completed
Push — master ( 65b9cd...fbdded )
by Philip
01:34
created

BinaryUtilities::readAndReturnFromOffset()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 4
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);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
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);
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) {
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