Completed
Push — master ( 00ba92...ba315b )
by Philip
02:05
created

BinaryUtilities::convertToCorrectEndian()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 11
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 8
nc 3
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\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
    const MODE_READ = 'read';
22
    const MODE_WRITE = 'write';
23
24
    const BASE_BINARY = 2;
25
    const BASE_OCTAL = 7;
26
    const BASE_DECIMAL = 10;
27
    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
     * @return array
137
     */
138
    public function returnBuffer(): array
139
    {
140
        return array_map([$this, 'convertToBase'], $this->buffer);
141
    }
142
143
    /**
144
     * @param string $dataClass
145
     *
146
     * @throws DataTypeDoesNotExistsException
147
     * @throws EndianTypeDoesNotExistsException
148
     * @throws InvalidDataTypeException
149
     *
150
     * @return BinaryUtilities
151
     */
152
    public function read(string $dataClass): BinaryUtilities
153
    {
154
        $dataType = $this->getDataType($dataClass);
155
156
        $dataType->setContent($this->content);
157
        $dataType->setOffset($this->offset);
158
        $dataType->setEndianMode($this->getEndianType($this->endian));
159
160
        $this->buffer = array_merge($this->buffer, $dataType->read());
161
162
        $this->offset = $dataType->newOffset();
163
164
        return $this;
165
    }
166
167
    /**
168
     * @param string $dataClass
169
     * @param int    $length
170
     *
171
     * @throws DataTypeDoesNotExistsException
172
     * @throws EndianTypeDoesNotExistsException
173
     * @throws InvalidDataTypeException
174
     *
175
     * @return BinaryUtilities
176
     */
177
    public function readArray(string $dataClass, int $length): BinaryUtilities
178
    {
179
        $dataType = $this->getDataType($dataClass);
180
181
        $dataType->setContent($this->content);
182
        $dataType->setOffset($this->offset);
183
        $dataType->setEndianMode($this->getEndianType($this->endian));
184
185
        $this->buffer = array_merge($this->buffer, $dataType->readArray($length));
186
187
        $this->offset = $dataType->newOffset();
188
189
        return $this;
190
    }
191
192
    /**
193
     * @param string $dataClass
194
     * @param int    $data
195
     *
196
     * @throws DataTypeDoesNotExistsException
197
     * @throws InvalidDataTypeException
198
     *
199
     * @return BinaryUtilities
200
     */
201
    public function write(string $dataClass, int $data): BinaryUtilities
202
    {
203
        $dataType = $this->getDataType($dataClass);
204
205
        $dataType->setContent($this->content);
206
        $dataType->setOffset($this->offset);
207
208
        $dataType->write($data);
209
210
        $this->content = $dataType->newContent();
211
        $this->offset = $dataType->newOffset();
212
213
        return $this;
214
    }
215
216
    public function save(): void
217
    {
218
        $handle = fopen($this->file, 'wb');
219
        fwrite($handle, $this->content);
220
        fclose($handle);
221
    }
222
223
    /**
224
     * @param string $dataClass
225
     *
226
     * @throws DataTypeDoesNotExistsException
227
     * @throws InvalidDataTypeException
228
     *
229
     * @return AbstractDataType
230
     */
231
    private function getDataType(string $dataClass): AbstractDataType
232
    {
233
        if (!class_exists($dataClass)) {
234
            throw new DataTypeDoesNotExistsException();
235
        }
236
237
        if (!array_key_exists($dataClass, $this->dataTypeClasses)) {
238
            /** @var AbstractDataType $type */
239
            $type = new $dataClass();
240
241
            if (!$type instanceof AbstractDataType) {
242
                throw new InvalidDataTypeException();
243
            }
244
        } else {
245
            /** @var AbstractDataType $dataType */
246
            $type = $this->dataTypeClasses[$dataClass];
247
        }
248
249
        return $type;
250
    }
251
252
    /**
253
     * @param null|string $entianType
254
     *
255
     * @throws EndianTypeDoesNotExistsException
256
     *
257
     * @return AbstractEndianType
258
     */
259
    private function getEndianType(?string $entianType): AbstractEndianType
260
    {
261
        if ($entianType === null) {
262
            $entianType = BigEndian::class;
263
        }
264
265
        if (!class_exists($entianType)) {
266
            throw new EndianTypeDoesNotExistsException();
267
        }
268
269
        return new $entianType();
270
    }
271
272
    /**
273
     * @param int $value
274
     *
275
     * @return string
276
     */
277
    private function convertToBase(int $value): string
278
    {
279
        return base_convert($value, 10, $this->base);
280
    }
281
282
    private function setContent(): void
283
    {
284
        $this->currentBit = 0;
285
        $this->offset = 0;
286
        $this->endOfFile = filesize($this->file);
287
        $handle = fopen($this->file, 'rb');
288
        $this->content = fread($handle, $this->endOfFile);
289
        fclose($handle);
290
    }
291
}
292