Completed
Push — master ( ef6e67...66e9af )
by Philip
01:28
created

BinaryUtilities::save()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

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