Completed
Push — master ( 319c7a...cdee94 )
by thomas
79:06 queued 75:43
created

Parser::doNext()   C

Complexity

Conditions 8
Paths 10

Size

Total Lines 34
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 20
CRAP Score 8.0481

Importance

Changes 4
Bugs 1 Features 0
Metric Value
c 4
b 1
f 0
dl 0
loc 34
ccs 20
cts 22
cp 0.9091
rs 5.3846
cc 8
eloc 21
nc 10
nop 1
crap 8.0481
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
10
class Parser implements \Iterator
11
{
12
    /**
13
     * @var Math
14
     */
15
    private $math;
16
17
    /**
18
     * @var Buffer
19
     */
20
    private $empty;
21
22
    /**
23
     * @var int
24
     */
25
    private $position = 0;
26
27
    /**
28
     * @var int
29
     */
30
    private $end = 0;
31
32
    /**
33
     * @var int
34
     */
35
    private $execPtr = 0;
36
37
    /**
38
     * @var string
39
     */
40
    private $data = '';
41
42
    /**
43
     * @var Operation[]
44
     */
45
    private $array = array();
46
47
    /**
48
     * ScriptParser constructor.
49
     * @param Math $math
50
     * @param ScriptInterface $script
51
     */
52 1125
    public function __construct(Math $math, ScriptInterface $script)
53
    {
54 1125
        $this->math = $math;
55 1125
        $buffer = $script->getBuffer();
56 1125
        $this->data = $buffer->getBinary();
57 1125
        $this->end = $buffer->getSize();
0 ignored issues
show
Documentation Bug introduced by
It seems like $buffer->getSize() can also be of type string. However, the property $end is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
58 1125
        $this->script = $script;
0 ignored issues
show
Bug introduced by
The property script does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
59 1125
        $this->empty = new Buffer('', 0, $math);
60 1125
    }
61
62
    /**
63
     * @return int
64
     */
65
    public function getPosition()
66
    {
67
        return $this->position;
68
    }
69
70
    /**
71
     * @param string $packFormat
72
     * @param integer $strSize
73
     * @return array|bool
74
     */
75 54
    private function unpackSize($packFormat, $strSize)
76
    {
77 54
        if ($this->end - $this->position < $strSize) {
78
            return false;
79
        }
80 54
        $size = unpack($packFormat, substr($this->data, $this->position, $strSize));
81 54
        $size = $size[1];
82 54
        $this->position += $strSize;
83
84 54
        return $size;
85
    }
86
87
    /**
88
     * @param int $size
89
     * @return bool
90
     */
91 771
    private function validateSize($size)
92
    {
93 771
        $pdif = ($this->end - $this->position);
94 771
        $a = $pdif < 0;
95 771
        $b = $pdif < $size;
96 771
        $r = !($a || $b);
97
98 771
        return $r;
99
    }
100
101
    /**
102
     * @param int $ptr
103
     * @return Operation
104
     */
105 1041
    private function doNext($ptr)
106
    {
107 1041
        if ($this->math->cmp($this->position, $this->end) >= 0) {
108
            throw new \RuntimeException('Position exceeds end of script!');
109
        }
110
111 1041
        $opCode = ord($this->data[$this->position++]);
112 1041
        $pushData = $this->empty;
113 1041
        $dataSize = 0;
114
115 1041
        if ($opCode <= Opcodes::OP_PUSHDATA4) {
116 771
            if ($opCode < Opcodes::OP_PUSHDATA1) {
117 747
                $dataSize = $opCode;
118 771
            } else if ($opCode === Opcodes::OP_PUSHDATA1) {
119 30
                $dataSize = $this->unpackSize('C', 1);
120 54
            } else if ($opCode === Opcodes::OP_PUSHDATA2) {
121 12
                $dataSize = $this->unpackSize('v', 2);
122 12
            } else {
123 12
                $dataSize = $this->unpackSize('V', 4);
124
            }
125
126 771
            if ($dataSize === false || $this->validateSize($dataSize) === false) {
127
                throw new \RuntimeException('Failed to unpack data from Script');
128
            }
129
130 771
            $pushData = new Buffer(substr($this->data, $this->position, $dataSize), $dataSize, $this->math);
131
132 771
            $this->position += $dataSize;
133 771
        }
134
135 1041
        $this->array[$ptr] = new Operation($opCode, $pushData, $dataSize);
136
137 1041
        return $this->array[$ptr];
138
    }
139
140
    /**
141
     *
142
     */
143 1083
    public function rewind()
144
    {
145 1083
        $this->execPtr = 0;
146 1083
    }
147
148
    /**
149
     * @return Operation
150
     */
151 1041
    public function current()
152
    {
153 1041
        if (isset($this->array[$this->execPtr])) {
154
            $exec = $this->array[$this->execPtr];
155
        } else {
156 1041
            $exec = $this->doNext($this->execPtr);
157
        }
158
159 1041
        return $exec;
160
    }
161
162
    /**
163
     * @return int
164
     */
165
    public function key()
166
    {
167
        return $this->execPtr;
168
    }
169
170
    /**
171
     * @return Operation
172
     */
173 1065
    public function next()
174
    {
175 1065
        $ptr = $this->execPtr;
176 1065
        if (isset($this->array[$ptr])) {
177 1029
            $this->execPtr++;
178 1029
            return $this->array[$ptr];
179
        }
180
181 36
        return null;
182
    }
183
184
    /**
185
     * @return bool
186
     */
187 1083
    public function valid()
188
    {
189 1083
        return isset($this->array[$this->execPtr]) || $this->position < $this->end;
190
    }
191
192
    /**
193
     * returns a mix of Buffer objects and strings
194
     *
195
     * @return Buffer[]|string[]
196
     */
197 915
    public function parse()
198
    {
199 915
        $data = array();
200
201 915
        $it = $this;
202 915
        foreach ($it as $exec) {
203 903
            $opCode = $exec->getOp();
204 903
            if ($opCode == 0) {
205 162
                $push = Buffer::hex('00', 1, $this->math);
206 903
            } elseif ($opCode <= 78) {
207 519
                $push = $exec->getData();
208 519
            } else {
209
                // None of these are pushdatas, so just an opcode
210 843
                $push = $this->script->getOpcodes()->getOp($opCode);
211
            }
212
213 897
            $data[] = $push;
214 909
        }
215
216 909
        return $data;
217
    }
218
219
    /**
220
     * @return Operation[]
221
     */
222 687
    public function decode()
223
    {
224 687
        $result = [];
225 687
        foreach ($this as $operation) {
226 651
            $result[] = $operation;
227 687
        }
228
229 687
        return $result;
230
    }
231
232
    /**
233
     * @return string
234
     */
235 672
    public function getHumanReadable()
236
    {
237 672
        $parse = $this->parse();
238
239 672
        $array = array_map(
240 672
            function ($value) {
241 672
                return ($value instanceof Buffer)
242 672
                    ? $value->getHex()
243 672
                    : $value;
244 672
            },
245
            $parse
246 672
        );
247
248 672
        return implode(' ', $array);
249
    }
250
}
251