Completed
Push — master ( ca17e8...6f4a17 )
by Philip
01:17
created

BinaryUtilities::readReturn()   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 3
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\DataTypeDoesNotExistsException;
11
use PBurggraf\BinaryUtilities\Exception\EndianTypeDoesNotExistsException;
12
use PBurggraf\BinaryUtilities\Exception\FileDoesNotExistsException;
13
use PBurggraf\BinaryUtilities\Exception\FileErrorException;
14
use PBurggraf\BinaryUtilities\Exception\FileNotAccessableException;
15
use PBurggraf\BinaryUtilities\Exception\InvalidDataTypeException;
16
17
/**
18
 * @author Philip Burggraf <[email protected]>
19
 */
20
class BinaryUtilities
21
{
22
    public const BASE_BINARY = 2;
23
    public const BASE_OCTAL = 7;
24
    public const BASE_DECIMAL = 10;
25
    public const BASE_HEXADECIMAL = 16;
26
27
    /**
28
     * @var string
29
     */
30
    protected $file;
31
32
    /**
33
     * @var string
34
     */
35
    protected $content;
36
37
    /**
38
     * @var int
39
     */
40
    protected $endOfFile;
41
42
    /**
43
     * @var int
44
     */
45
    protected $base = self::BASE_DECIMAL;
46
47
    /**
48
     * @var array
49
     */
50
    protected $buffer = [];
51
52
    /**
53
     * @var int
54
     */
55
    protected $currentBit;
56
57
    /**
58
     * @var int
59
     */
60
    protected $offset = 0;
61
62
    /**
63
     * @var string
64
     */
65
    protected $endian;
66
67
    /**
68
     * @var AbstractDataType[]
69
     */
70
    protected $dataTypeClasses = [];
71
72
    /**
73
     * @param string $file
74
     *
75
     * @throws FileNotAccessableException
76
     * @throws FileDoesNotExistsException
77
     * @throws FileErrorException
78
     *
79
     * @return BinaryUtilities
80
     */
81
    public function setFile(string $file): BinaryUtilities
82
    {
83
        if (!file_exists($file)) {
84
            throw new FileDoesNotExistsException();
85
        }
86
        $this->file = $file;
87
        $this->setData();
88
89
        return $this;
90
    }
91
92
    /**
93
     * @param string $mode
94
     *
95
     * @return BinaryUtilities
96
     */
97
    public function setEndian(string $mode): BinaryUtilities
98
    {
99
        $this->endian = $mode;
100
101
        return $this;
102
    }
103
104
    /**
105
     * @param int $offset
106
     *
107
     * @return BinaryUtilities
108
     */
109
    public function offset(int $offset): BinaryUtilities
110
    {
111
        $this->offset = $offset;
112
113
        return $this;
114
    }
115
116
    /**
117
     * @param int $base
118
     *
119
     * @return BinaryUtilities
120
     */
121
    public function setBase(int $base = self::BASE_DECIMAL): BinaryUtilities
122
    {
123
        $this->base = $base;
124
125
        return $this;
126
    }
127
128
    /**
129
     * @param bool $clearBuffer
130
     * @param bool $applyConvertToBase
131
     *
132
     * @return array
133
     */
134
    public function returnBuffer(bool $clearBuffer = true, bool $applyConvertToBase = true): array
135
    {
136
        $buffer = $this->buffer;
137
138
        if ($clearBuffer) {
139
            $this->buffer = [];
140
        }
141
142
        if ($applyConvertToBase === true) {
143
            return array_map([$this, 'convertToBase'], $buffer);
144
        }
145
146
        return $buffer;
147
    }
148
149
    /**
150
     * @param string $dataClass
151
     * @param bool   $clearBuffer
152
     * @param bool   $applyConvertToBase
153
     *
154
     * @throws InvalidDataTypeException
155
     * @throws DataTypeDoesNotExistsException
156
     * @throws EndianTypeDoesNotExistsException
157
     *
158
     * @return string
159
     */
160
    public function readReturn(string $dataClass, bool $clearBuffer = true, bool $applyConvertToBase = true): string
161
    {
162
        return $this->read($dataClass)->returnBuffer($clearBuffer, $applyConvertToBase)[0];
163
    }
164
165
    /**
166
     * @param int    $offset
167
     * @param string $dataClass
168
     * @param bool   $clearBuffer
169
     * @param bool   $applyConvertToBase
170
     *
171
     * @throws InvalidDataTypeException
172
     * @throws DataTypeDoesNotExistsException
173
     * @throws EndianTypeDoesNotExistsException
174
     *
175
     * @return string
176
     */
177
    public function readAndReturnFromOffset(int $offset, string $dataClass, bool $clearBuffer = true, bool $applyConvertToBase = true): string
178
    {
179
        return $this->offset($offset)->readReturn($dataClass, $clearBuffer, $applyConvertToBase);
180
    }
181
182
    /**
183
     * @param string $dataClass
184
     *
185
     * @throws InvalidDataTypeException
186
     * @throws DataTypeDoesNotExistsException
187
     * @throws EndianTypeDoesNotExistsException
188
     *
189
     * @return BinaryUtilities
190
     */
191
    public function read(string $dataClass): BinaryUtilities
192
    {
193
        $dataType = $this->getDataType($dataClass);
194
195
        $dataType->setContent($this->content);
196
        $dataType->setOffset($this->offset);
197
        $dataType->setEndianMode($this->getEndianType($this->endian));
198
199
        $this->buffer = array_merge($this->buffer, $dataType->read());
200
201
        $this->offset = $dataType->newOffset();
202
203
        return $this;
204
    }
205
206
    /**
207
     * @param string $dataClass
208
     * @param int    $length
209
     *
210
     * @throws InvalidDataTypeException
211
     * @throws DataTypeDoesNotExistsException
212
     * @throws EndianTypeDoesNotExistsException
213
     *
214
     * @return BinaryUtilities
215
     */
216
    public function readArray(string $dataClass, int $length): BinaryUtilities
217
    {
218
        $dataType = $this->getDataType($dataClass);
219
220
        $dataType->setContent($this->content);
221
        $dataType->setOffset($this->offset);
222
        $dataType->setEndianMode($this->getEndianType($this->endian));
223
224
        $this->buffer = array_merge($this->buffer, $dataType->readArray($length));
225
226
        $this->offset = $dataType->newOffset();
227
228
        return $this;
229
    }
230
231
    /**
232
     * @param string           $dataClass
233
     * @param string|float|int $data
234
     *
235
     * @throws InvalidDataTypeException
236
     * @throws DataTypeDoesNotExistsException
237
     * @throws EndianTypeDoesNotExistsException
238
     *
239
     * @return BinaryUtilities
240
     */
241
    public function write(string $dataClass, $data): BinaryUtilities
242
    {
243
        $dataType = $this->getDataType($dataClass);
244
245
        $dataType->setContent($this->content);
246
        $dataType->setOffset($this->offset);
247
        $dataType->setEndianMode($this->getEndianType($this->endian));
248
249
        $dataType->write($data);
250
251
        $this->content = $dataType->newContent();
252
        $this->offset = $dataType->newOffset();
253
254
        return $this;
255
    }
256
257
    /**
258
     * @param string $dataClass
259
     * @param array  $data
260
     *
261
     * @throws InvalidDataTypeException
262
     * @throws DataTypeDoesNotExistsException
263
     * @throws EndianTypeDoesNotExistsException
264
     *
265
     * @return BinaryUtilities
266
     */
267
    public function writeArray(string $dataClass, array $data): BinaryUtilities
268
    {
269
        $dataType = $this->getDataType($dataClass);
270
271
        $dataType->setContent($this->content);
272
        $dataType->setOffset($this->offset);
273
        $dataType->setEndianMode($this->getEndianType($this->endian));
274
275
        $dataType->writeArray($data);
276
277
        $this->content = $dataType->newContent();
278
        $this->offset = $dataType->newOffset();
279
280
        return $this;
281
    }
282
283
    /**
284
     * @throws FileNotAccessableException
285
     */
286
    public function save(): void
287
    {
288
        $handle = fopen($this->file, 'wb');
289
290
        if ($handle === false) {
291
            throw new FileNotAccessableException();
292
        }
293
294
        fwrite($handle, $this->content);
295
        fclose($handle);
296
    }
297
298
    /**
299
     * @param string $dataClass
300
     *
301
     * @throws DataTypeDoesNotExistsException
302
     * @throws InvalidDataTypeException
303
     *
304
     * @return AbstractDataType
305
     */
306
    private function getDataType(string $dataClass): AbstractDataType
307
    {
308
        if (!class_exists($dataClass)) {
309
            throw new DataTypeDoesNotExistsException();
310
        }
311
312
        if (array_key_exists($dataClass, $this->dataTypeClasses)) {
313
            /** @var AbstractDataType $dataType */
314
            $type = $this->dataTypeClasses[$dataClass];
315
        } else {
316
            /** @var AbstractDataType $type */
317
            $type = new $dataClass();
318
319
            if (!$type instanceof AbstractDataType) {
320
                throw new InvalidDataTypeException();
321
            }
322
        }
323
324
        return $type;
325
    }
326
327
    /**
328
     * @param string|null $entianType
329
     *
330
     * @throws EndianTypeDoesNotExistsException
331
     *
332
     * @return AbstractEndianType
333
     */
334
    private function getEndianType(?string $entianType): AbstractEndianType
335
    {
336
        if ($entianType === null) {
337
            $entianType = BigEndian::class;
338
        }
339
340
        if (!class_exists($entianType)) {
341
            throw new EndianTypeDoesNotExistsException();
342
        }
343
344
        return new $entianType();
345
    }
346
347
    /**
348
     * @param int $value
349
     *
350
     * @return string
351
     */
352
    private function convertToBase(int $value): string
353
    {
354
        return base_convert((string) $value, 10, $this->base);
355
    }
356
357
    /**
358
     * @throws FileErrorException
359
     * @throws FileNotAccessableException
360
     */
361
    private function setData(): void
362
    {
363
        $this->currentBit = 0;
364
        $this->offset = 0;
365
366
        $filesize = filesize($this->file);
367
368
        if ($filesize === false) {
369
            throw new FileErrorException();
370
        }
371
372
        $this->endOfFile = $filesize;
373
        $handle = fopen($this->file, 'rb');
374
375
        if ($handle === false) {
376
            throw new FileNotAccessableException();
377
        }
378
379
        $content = fread($handle, $this->endOfFile);
380
381
        if ($content === false) {
382
            throw new FileErrorException();
383
        }
384
385
        $this->content = $content;
386
        fclose($handle);
387
    }
388
}
389