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

Number::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 5
ccs 4
cts 4
cp 1
rs 9.4286
cc 1
eloc 3
nc 1
nop 2
crap 1
1
<?php
2
3
namespace BitWasp\Bitcoin\Script\Interpreter;
4
5
use BitWasp\Bitcoin\Bitcoin;
6
use BitWasp\Bitcoin\Math\Math;
7
use BitWasp\Bitcoin\Serializable;
8
use BitWasp\Buffertools\Buffer;
9
10
class Number extends Serializable
11
{
12
    const MAX_NUM_SIZE = 4;
13
    const MAX = 4294967295; // 2^32-1
14
    const MIN = -4294967295; // -2^32+1
15
16
    /**
17
     * @var Math
18
     */
19
    private $math;
20
21
    /**
22
     * @var int
23
     */
24
    private $number;
25
26
    /**
27
     * Number constructor.
28
     * @param int $number
29
     * @param Math $math
30
     */
31 762
    public function __construct($number, Math $math)
32
    {
33 762
        $this->number = $number;
34 762
        $this->math = $math;
35 762
    }
36
37
    /**
38
     * @param int $number
39
     * @param Math|null $math
40
     * @return self
41
     */
42 222
    public static function int($number, Math $math = null)
43
    {
44 222
        return new self(
45 222
            $number,
46 222
            $math ?: Bitcoin::getMath()
47 222
        );
48
    }
49
50
    /**
51
     * @param Buffer $vch
52
     * @param bool $fRequireMinimal
53
     * @param int $maxNumSize
54
     * @param Math|null $math
55
     * @return self
56
     */
57 762
    public static function buffer(Buffer $vch, $fRequireMinimal, $maxNumSize = self::MAX_NUM_SIZE, Math $math = null)
58
    {
59 762
        $size = $vch->getSize();
60 762
        if ($size > $maxNumSize) {
61
            throw new \RuntimeException('Script number overflow');
62
        }
63
64 762
        if ($fRequireMinimal && $size > 0) {
65
            $binary = $vch->getBinary();
66
            if (ord($binary[$size - 1]) & 0x7f === 0) {
67
                if ($size <= 1 || ord($binary[$size - 2]) & 0x80 === 0) {
68
                    throw new \RuntimeException('Non-minimally encoded script number');
69
                }
70
            }
71
        }
72
73 762
        $math = $math ?: Bitcoin::getMath();
74 762
        $number = new self(0, $math);
75 762
        $number->number = $number->parseBuffer($vch);
0 ignored issues
show
Documentation Bug introduced by
It seems like $number->parseBuffer($vch) can also be of type string. However, the property $number 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...
76 762
        return $number;
77
    }
78
79
    /**
80
     * @param Buffer $buffer
81
     * @return int
82
     */
83 762
    private function parseBuffer(Buffer $buffer)
84
    {
85 762
        $size = $buffer->getSize();
86 762
        if ($size === 0) {
87 762
            return '0';
88
        }
89
90
        $chars = array_map(function ($binary) {
91 762
            return ord($binary);
92
93 762
        }, str_split($buffer->flip()->getBinary(), 1));
94
95 762
        $result = 0;
96 762
        for ($i = 0; $i < $size; $i++) {
97 762
            $mul = $this->math->mul($i, 8);
98 762
            $byte = $this->math->leftShift($chars[$i], $mul);
99 762
            $result = $this->math->bitwiseOr($result, $byte);
100 762
        }
101
102 762
        if ($chars[0] & 0x80) {
103
            $mask = gmp_strval(gmp_com($this->math->leftShift(0x80, (8 * ($size - 1)))), 10);
104
            return $this->math->sub(0, $this->math->bitwiseAnd($result, $mask));
105
        }
106
107 762
        return $result;
108
    }
109
110
    /**
111
     * @return Buffer
112
     */
113 762
    private function serialize()
114
    {
115 762
        if ($this->math->cmp($this->number, '0') === 0) {
116 762
            return new Buffer('', 4);
117
        }
118
119
        // Using array of integers instead of bytes
120 762
        $result = [];
121 762
        $negative = $this->math->cmp($this->number, 0) < 0;
122 762
        $abs = $negative ? $this->math->sub(0, $this->number) : $this->number;
123 762
        while ($this->math->cmp($abs, 0) > 0) {
124 762
            array_unshift($result, (int)$this->math->bitwiseAnd($abs, 0xff));
125 762
            $abs = $this->math->rightShift($abs, 8);
126 762
        }
127
128 762
        if ($result[0] & 0x80) {
129
            array_unshift($result, $negative ? 0x80 : 0x00);
130 762
        } else if ($negative) {
131
            array_unshift($result, 0x80);
132
        }
133
134 762
        return new Buffer(array_reduce($result, function ($agg, $current) {
135 762
            return $agg . chr($current);
136 762
        }), 4, $this->math);
137
138
    }
139
140
    /**
141
     * @return Buffer
142
     */
143 762
    public function getBuffer()
144
    {
145 762
        return $this->serialize();
146
    }
147
148
    /**
149
     * @return int
150
     */
151 192
    public function getInt()
152
    {
153 192
        if ($this->math->cmp($this->number, self::MAX) > 0) {
154
            return self::MAX;
155 192
        } else if ($this->math->cmp($this->number, self::MIN) < 0) {
156
            return self::MIN;
157
        }
158
159 192
        return $this->number;
160
    }
161
}
162