Completed
Pull Request — master (#56)
by thomas
15:05
created

Parser::__construct()   B

Complexity

Conditions 5
Paths 12

Size

Total Lines 20
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 20
rs 8.8571
cc 5
eloc 13
nc 12
nop 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace BitWasp\Buffertools;
6
7
use BitWasp\Buffertools\Exceptions\ParserOutOfRange;
8
use Mdanter\Ecc\EccFactory;
9
use Mdanter\Ecc\Math\GmpMathInterface;
10
11
class Parser
12
{
13
    /**
14
     * @var string
15
     */
16
    private $string;
17
18
    /**
19
     * @var \Mdanter\Ecc\Math\GmpMathInterface
20
     */
21
    private $math;
22
23
    /**
24
     * @var int
25
     */
26
    private $size = 0;
27
28
    /**
29
     * @var int
30
     */
31
    private $position = 0;
32
33
    /**
34
     * Instantiate class, optionally taking Buffer or HEX.
35
     *
36
     * @param null|string|BufferInterface $input
37
     * @param GmpMathInterface|null $math
38
     */
39
    public function __construct($input = null, GmpMathInterface $math = null)
40
    {
41
        $this->math = $math ?: EccFactory::getAdapter();
42
43
        if (null === $input) {
44
            $input = '';
45
        }
46
47
        if (is_string($input)) {
48
            $bin = Buffer::hex($input, null, $this->math)->getBinary();
49
        } elseif ($input instanceof BufferInterface) {
50
            $bin = $input->getBinary();
51
        } else {
52
            throw new \InvalidArgumentException("Invalid argument to Parser");
53
        }
54
55
        $this->string = $bin;
56
        $this->position = 0;
57
        $this->size = strlen($this->string);
58
    }
59
60
    /**
61
     * Get the position pointer of the parser - ie, how many bytes from 0
62
     *
63
     * @return int
64
     */
65
    public function getPosition(): int
66
    {
67
        return $this->position;
68
    }
69
70
    /**
71
     * Get the total size of the parser
72
     *
73
     * @return int
74
     */
75
    public function getSize()
76
    {
77
        return $this->size;
78
    }
79
80
    /**
81
     * Parse $bytes bytes from the string, and return the obtained buffer
82
     *
83
     * @param  int $numBytes
84
     * @param  bool $flipBytes
85
     * @return BufferInterface
86
     * @throws \Exception
87
     */
88
    public function readBytes(int $numBytes, bool $flipBytes = false): BufferInterface
89
    {
90
        $string = substr($this->string, $this->getPosition(), $numBytes);
91
        $length = strlen($string);
92
93
        if ($length === 0) {
94
            throw new ParserOutOfRange('Could not parse string of required length (empty)');
95
        } elseif ($length < $numBytes) {
96
            throw new ParserOutOfRange('Could not parse string of required length (too short)');
97
        }
98
99
        $this->position += $numBytes;
100
101
        if ($flipBytes) {
102
            $string = Buffertools::flipBytes($string);
103
        }
104
105
        return new Buffer($string, $length, $this->math);
0 ignored issues
show
Bug introduced by
It seems like $string defined by \BitWasp\Buffertools\Buf...ols::flipBytes($string) on line 102 can also be of type object<BitWasp\Buffertools\Buffer>; however, BitWasp\Buffertools\Buffer::__construct() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
106
    }
107
108
    /**
109
     * Write $data as $bytes bytes. Can be flipped if needed.
110
     *
111
     * @param  integer $numBytes - number of bytes to write
112
     * @param  SerializableInterface|BufferInterface|string $data - buffer, serializable or hex
113
     * @param  bool $flipBytes
114
     * @return Parser
115
     */
116
    public function writeBytes(int $numBytes, $data, bool $flipBytes = false): Parser
117
    {
118
        // Treat $data to ensure it's a buffer, with the correct size
119
        if ($data instanceof SerializableInterface) {
120
            $data = $data->getBuffer();
121
        }
122
123
        if (is_string($data)) {
124
            // Convert to a buffer
125
            $data = Buffer::hex($data, $numBytes, $this->math);
126
        } else if (!($data instanceof BufferInterface)) {
127
            throw new \RuntimeException('Invalid data passed to Parser::writeBytes');
128
        }
129
130
        $this->writeBuffer($numBytes, $data, $flipBytes);
131
132
        return $this;
133
    }
134
135
    /**
136
     * Write $data as $bytes bytes. Can be flipped if needed.
137
     *
138
     * @param  integer $numBytes
139
     * @param  string $data
140
     * @param  bool $flipBytes
141
     * @return Parser
142
     */
143
    public function writeRawBinary(int $numBytes, string $data, bool $flipBytes = false): Parser
144
    {
145
        return $this->writeBuffer($numBytes, new Buffer($data, $numBytes), $flipBytes);
146
    }
147
148
    /**
149
     * @param BufferInterface $buffer
150
     * @param bool $flipBytes
151
     * @param int $numBytes
152
     * @return Parser
153
     */
154
    public function writeBuffer(int $numBytes, BufferInterface $buffer, bool $flipBytes = false): Parser
155
    {
156
        // only create a new buffer if the size does not match
157
        if ($buffer->getSize() != $numBytes) {
158
            $buffer = new Buffer($buffer->getBinary(), $numBytes, $this->math);
159
        }
160
161
        $this->appendBuffer($buffer, $flipBytes);
162
163
        return $this;
164
    }
165
166
    /**
167
     * @param BufferInterface $buffer
168
     * @param bool $flipBytes
169
     * @return Parser
170
     */
171
    public function appendBuffer(BufferInterface $buffer, bool $flipBytes = false): Parser
172
    {
173
        $this->appendBinary($buffer->getBinary(), $flipBytes);
174
        return $this;
175
    }
176
177
    /**
178
     * @param string $binary
179
     * @param bool $flipBytes
180
     * @return Parser
181
     */
182
    public function appendBinary(string $binary, bool $flipBytes = false): Parser
183
    {
184
        if ($flipBytes) {
185
            $binary = Buffertools::flipBytes($binary);
186
        }
187
188
        $this->string .= $binary;
189
        $this->size += strlen($binary);
190
        return $this;
191
    }
192
193
    /**
194
     * Take an array containing serializable objects.
195
     * @param SerializableInterface[]|BufferInterface[] $serializable
196
     * @return Parser
197
     */
198
    public function writeArray(array $serializable): Parser
199
    {
200
        $parser = new Parser(Buffertools::numToVarInt(count($serializable)), $this->math);
201
        foreach ($serializable as $object) {
202
            if ($object instanceof SerializableInterface) {
203
                $object = $object->getBuffer();
204
            }
205
206
            if ($object instanceof BufferInterface) {
207
                $parser->writeBytes($object->getSize(), $object);
208
            } else {
209
                throw new \RuntimeException('Input to writeArray must be Buffer[], or SerializableInterface[]');
210
            }
211
        }
212
213
        $this->string .= $parser->getBuffer()->getBinary();
214
        $this->size += $parser->getSize();
215
216
        return $this;
217
    }
218
219
    /**
220
     * Return the string as a buffer
221
     *
222
     * @return BufferInterface
223
     */
224
    public function getBuffer(): BufferInterface
225
    {
226
        return new Buffer($this->string, null, $this->math);
227
    }
228
}
229