Completed
Push — master ( 26caf6...2acf03 )
by thomas
8s
created

Parser   A

Complexity

Total Complexity 26

Size/Duplication

Total Lines 205
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 6

Test Coverage

Coverage 89.47%
Metric Value
wmc 26
lcom 2
cbo 6
dl 0
loc 205
ccs 68
cts 76
cp 0.8947
rs 10

11 Methods

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