Completed
Push — 0.0.34 ( 61212b...2e9020 )
by thomas
20:55
created

Parser   A

Complexity

Total Complexity 34

Size/Duplication

Total Lines 241
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 6

Test Coverage

Coverage 90.36%

Importance

Changes 0
Metric Value
dl 0
loc 241
ccs 75
cts 83
cp 0.9036
rs 9.2
c 0
b 0
f 0
wmc 34
lcom 2
cbo 6

12 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 1
A getPosition() 0 4 1
A unpackSize() 0 12 2
D doNext() 0 38 10
A slice() 0 18 4
A rewind() 0 5 1
A current() 0 10 2
A key() 0 4 1
A next() 0 10 2
A valid() 0 4 2
A decode() 0 9 2
B getHumanReadable() 0 16 6
1
<?php
2
3
namespace BitWasp\Bitcoin\Script\Parser;
4
5
use BitWasp\Bitcoin\Math\Math;
6
use BitWasp\Bitcoin\Script\Opcodes;
7
use BitWasp\Bitcoin\Script\Script;
8
use BitWasp\Bitcoin\Script\ScriptInterface;
9
use BitWasp\Buffertools\Buffer;
10
use BitWasp\Buffertools\BufferInterface;
11
12
class Parser implements \Iterator
13
{
14
    /**
15
     * @var Math
16
     */
17
    private $math;
18
19
    /**
20
     * @var BufferInterface
21
     */
22
    private $empty;
23
24
    /**
25
     * @var ScriptInterface
26
     */
27
    private $script;
28
29
    /**
30
     * @var int
31
     */
32
    private $count = 0;
33
34
    /**
35
     * @var int
36
     */
37
    private $position = 0;
38
39
    /**
40
     * @var int
41
     */
42
    private $end = 0;
43
44
    /**
45
     * @var int
46
     */
47
    private $execPtr = 0;
48
49
    /**
50
     * @var string
51
     */
52
    private $data = '';
53
54
    /**
55
     * @var Operation[]
56
     */
57
    private $array = array();
58
59
    /**
60
     * ScriptParser constructor.
61
     * @param Math $math
62
     * @param ScriptInterface $script
63
     */
64 2598
    public function __construct(Math $math, ScriptInterface $script)
65
    {
66 2598
        $this->math = $math;
67 2598
        $buffer = $script->getBuffer();
68 2598
        $this->data = $buffer->getBinary();
69 2598
        $this->end = $buffer->getSize();
70 2598
        $this->script = $script;
71 2598
        $this->empty = new Buffer('', 0, $math);
72 2598
    }
73
74
    /**
75
     * @return int
76
     */
77 2
    public function getPosition()
78
    {
79 2
        return $this->position;
80
    }
81
82
    /**
83
     * @param string $packFormat
84
     * @param integer $strSize
85
     * @return array|bool
86
     */
87 72
    private function unpackSize($packFormat, $strSize)
88
    {
89 72
        if ($this->end - $this->position < $strSize) {
90
            return false;
91
        }
92
93 72
        $size = unpack($packFormat, substr($this->data, $this->position, $strSize));
94 72
        $size = $size[1];
95 72
        $this->position += $strSize;
96
97 72
        return $size;
98
    }
99
100
    /**
101
     * @param int $ptr
102
     * @return Operation
103
     */
104 2572
    private function doNext($ptr)
105
    {
106 2572
        if ($this->position >= $this->end) {
107
            throw new \RuntimeException('Position exceeds end of script!');
108
        }
109
110 2572
        $opCode = ord($this->data[$this->position++]);
111 2572
        $pushData = $this->empty;
112 2572
        $dataSize = 0;
113
114 2572
        if ($opCode <= Opcodes::OP_PUSHDATA4) {
115 1980
            if ($opCode < Opcodes::OP_PUSHDATA1) {
116 1940
                $dataSize = $opCode;
117 72
            } else if ($opCode === Opcodes::OP_PUSHDATA1) {
118 38
                $dataSize = $this->unpackSize('C', 1);
119 38
            } else if ($opCode === Opcodes::OP_PUSHDATA2) {
120 28
                $dataSize = $this->unpackSize('v', 2);
121
            } else {
122 10
                $dataSize = $this->unpackSize('V', 4);
123
            }
124
125 1980
            $delta = ($this->end - $this->position);
126 1980
            if ($dataSize === false || $delta < 0 || $delta < $dataSize) {
127 10
                throw new \RuntimeException('Failed to unpack data from Script');
128
            }
129
130 1974
            if ($dataSize > 0) {
131 1494
                $pushData = new Buffer(substr($this->data, $this->position, $dataSize), $dataSize, $this->math);
132
            }
133
134 1974
            $this->position += $dataSize;
135
        }
136
137 2566
        $this->array[$ptr] = new Operation($opCode, $pushData, $dataSize);
138 2566
        $this->count++;
139
140 2566
        return $this->array[$ptr];
141
    }
142
143
    /**
144
     * @param $begin
145
     * @param null|int $length
146
     * @return Script
147
     */
148 130
    public function slice($begin, $length = null)
149
    {
150 130
        if ($begin < 0) {
151
            throw new \RuntimeException("Invalid start of script - cannot be negative or ");
152
        }
153
154 130
        $maxLength = $this->end - $begin;
155
156 130
        if (null === $length) {
157
            $length = $maxLength;
158
        } else {
159 130
            if ($length > $maxLength) {
160
                throw new \RuntimeException("Cannot slice this much from script");
161
            }
162
        }
163
164 130
        return new Script(new Buffer(substr($this->data, $begin, $length)));
165
    }
166
167
    /**
168
     *
169
     */
170 2584
    public function rewind()
171
    {
172 2584
        $this->execPtr = 0;
173 2584
        $this->position = 0;
174 2584
    }
175
176
    /**
177
     * @return Operation
178
     */
179 2572
    public function current()
180
    {
181 2572
        if (isset($this->array[$this->execPtr])) {
182 130
            $exec = $this->array[$this->execPtr];
183
        } else {
184 2572
            $exec = $this->doNext($this->execPtr);
185
        }
186
187 2566
        return $exec;
188
    }
189
190
    /**
191
     * @return int
192
     */
193
    public function key()
194
    {
195
        return $this->execPtr;
196
    }
197
198
    /**
199
     * @return Operation
200
     */
201 2512
    public function next()
202
    {
203 2512
        $ptr = $this->execPtr;
204 2512
        if (isset($this->array[$ptr])) {
205 2500
            $this->execPtr++;
206 2500
            return $this->array[$ptr];
207
        }
208
209 12
        return null;
210
    }
211
212
    /**
213
     * @return bool
214
     */
215 2584
    public function valid()
216
    {
217 2584
        return isset($this->array[$this->execPtr]) || $this->position < $this->end;
218
    }
219
220
    /**
221
     * @return Operation[]
222
     */
223 1326
    public function decode()
224
    {
225 1326
        $result = [];
226 1326
        foreach ($this as $operation) {
227 1314
            $result[] = $operation;
228
        }
229
230 1326
        return $result;
231
    }
232
233
    /**
234
     * @return string
235
     */
236 6
    public function getHumanReadable()
237
    {
238 6
        return implode(' ', array_map(
239 6
            function (Operation $operation) {
240 4
                $op = $operation->getOp();
241 4
                if ($op === Opcodes::OP_0 || $op === Opcodes::OP_1NEGATE || $op >= Opcodes::OP_1 && $op <= Opcodes::OP_16) {
242
                    return $this->script->getOpcodes()->getOp($op);
243 4
                } else if ($operation->isPush()) {
244 4
                    return $operation->getData()->getHex();
245
                } else {
246 2
                    return $this->script->getOpcodes()->getOp($operation->getOp());
247
                }
248 6
            },
249
            $this->decode()
250
        ));
251
    }
252
}
253