Completed
Pull Request — master (#365)
by thomas
21:07
created

Number   A

Complexity

Total Complexity 30

Size/Duplication

Total Lines 175
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 88.31%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 175
ccs 68
cts 77
cp 0.8831
rs 10
wmc 30
lcom 1
cbo 5

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A int() 0 7 2
A gmp() 0 7 2
B parseBuffer() 0 23 4
A getBuffer() 0 4 1
A getInt() 0 10 3
A getGmp() 0 4 1
C buffer() 0 22 8
C serialize() 0 30 8
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
use BitWasp\Buffertools\BufferInterface;
10
11
class Number extends Serializable
12
{
13
    const MAX_NUM_SIZE = 4;
14
    const MAX = 2**31-1;
15
    const MIN = -2**31+1;
16
17
    /**
18
     * @var Math
19
     */
20
    private $math;
21
22
    /**
23
     * @var int
24
     */
25
    private $number;
26
27
    /**
28
     * Number constructor.
29
     * @param int $number
30
     * @param Math $math
31
     */
32 1802
    public function __construct($number, Math $math)
33
    {
34 1802
        $this->number = $number;
35 1802
        $this->math = $math;
36 1802
    }
37
38
    /**
39
     * @param int $number
40
     * @param Math|null $math
41
     * @return self
42
     */
43 1636
    public static function int($number, Math $math = null)
44
    {
45 1636
        return new self(
46 48
            $number,
47 1636
            $math ?: Bitcoin::getMath()
48 48
        );
49
    }
50
51
    /**
52
     * @param \GMP $number
53
     * @param Math|null $math
54
     * @return self
55
     */
56
    public static function gmp(\GMP $number, Math $math = null)
57
    {
58
        return new self(
59
            gmp_strval($number, 10),
60
            $math ?: Bitcoin::getMath()
61
        );
62
    }
63
64
    /**
65
     * @param BufferInterface $vch
66
     * @param bool $fRequireMinimal
67
     * @param int $maxNumSize
68
     * @param Math|null $math
69
     * @return self
70
     */
71 1330
    public static function buffer(BufferInterface $vch, $fRequireMinimal, $maxNumSize = self::MAX_NUM_SIZE, Math $math = null)
72
    {
73 1330
        $size = $vch->getSize();
74 1330
        if ($size > $maxNumSize) {
75 22
            throw new \RuntimeException('Script number overflow');
76
        }
77
78 1312
        if ($fRequireMinimal && $size > 0) {
79 110
            $binary = $vch->getBinary();
80
            //$chars = array_values(unpack("C*", $binary));
81 110
            if ((ord($binary[$size - 1]) & 0x7f) == 0) {
82 110
                if ($size <= 1 || (ord($binary[$size - 2]) & 0x80) == 0) {
83 110
                    throw new \RuntimeException('Non-minimally encoded script number');
84
                }
85
            }
86
        }
87
88 1238
        $math = $math ?: Bitcoin::getMath();
89 1238
        $number = new self(0, $math);
90 1238
        $number->number = $number->parseBuffer($vch);
0 ignored issues
show
Documentation Bug introduced by
The property $number was declared of type integer, but $number->parseBuffer($vch) is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
91 1238
        return $number;
92
    }
93
94
    /**
95
     * @param BufferInterface $buffer
96
     * @return int
97
     */
98 1238
    private function parseBuffer(BufferInterface $buffer)
99
    {
100 1238
        $size = $buffer->getSize();
101 1238
        if ($size === 0) {
102 686
            return '0';
103
        }
104
105 840
        $chars = array_values(unpack("C*", $buffer->getBinary()));
106
107 840
        $result = gmp_init(0);
108 840
        for ($i = 0; $i < $size; $i++) {
109 840
            $mul = $i * 8;
110 840
            $byte = $this->math->leftShift(gmp_init($chars[$i], 10), $mul);
111 840
            $result = $this->math->bitwiseOr($result, $byte);
0 ignored issues
show
Bug introduced by
It seems like $result defined by $this->math->bitwiseOr($result, $byte) on line 111 can also be of type resource; however, BitWasp\Bitcoin\Math\Math::bitwiseOr() does only seem to accept object<GMP>, 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...
112 38
        }
113
114 840
        if ($chars[count($chars)-1] & 0x80) {
115 126
            $mask = gmp_com($this->math->leftShift(gmp_init(0x80), (8 * ($size - 1))));
116 126
            $result = $this->math->sub(gmp_init(0), $this->math->bitwiseAnd($result, $mask));
0 ignored issues
show
Bug introduced by
It seems like $result defined by $this->math->sub(gmp_ini...iseAnd($result, $mask)) on line 116 can also be of type resource; however, Mdanter\Ecc\Math\GmpMath::bitwiseAnd() does only seem to accept object<GMP>, 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...
117 10
        }
118
119 840
        return gmp_strval($result, 10);
120
    }
121
122
    /**
123
     * @return BufferInterface
124
     */
125 1670
    private function serialize()
126
    {
127 1670
        if ($this->number == 0) {
128 534
            return new Buffer('', 0);
129
        }
130
131 1558
        $zero = gmp_init(0);
132
        // Using array of integers instead of bytes
133 1558
        $result = [];
134 1558
        $negative = $this->math->cmp(gmp_init($this->number), $zero) < 0;
135 1558
        $abs = $negative ? $this->math->sub($zero, gmp_init($this->number, 10)) : gmp_init($this->number, 10);
136 1558
        $mask = gmp_init(0xff);
137 1558
        while ($this->math->cmp($abs, $zero) > 0) {
0 ignored issues
show
Bug introduced by
It seems like $abs defined by $negative ? $this->math-...init($this->number, 10) on line 135 can also be of type resource; however, Mdanter\Ecc\Math\GmpMath::cmp() does only seem to accept object<GMP>, 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...
138 1558
            $result[] = (int) gmp_strval($this->math->bitwiseAnd($abs, $mask), 10);
0 ignored issues
show
Bug introduced by
It seems like $abs defined by $negative ? $this->math-...init($this->number, 10) on line 135 can also be of type resource; however, Mdanter\Ecc\Math\GmpMath::bitwiseAnd() does only seem to accept object<GMP>, 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...
139 1558
            $abs = $this->math->rightShift($abs, 8);
0 ignored issues
show
Bug introduced by
It seems like $abs can also be of type resource; however, Mdanter\Ecc\Math\GmpMath::rightShift() does only seem to accept object<GMP>, 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...
140 44
        }
141
142 1558
        if ($result[count($result) - 1] & 0x80) {
143 24
            $result[] = $negative ? 0x80 : 0;
144 1542
        } else if ($negative) {
145 42
            $result[count($result) - 1] |= 0x80;
146 8
        }
147
148 1558
        $s = '';
149 1558
        foreach ($result as $i) {
150 1558
            $s .= chr($i);
151 44
        }
152
153 1558
        return new Buffer($s, null, $this->math);
154
    }
155
156
    /**
157
     * @return BufferInterface
158
     */
159 1670
    public function getBuffer()
160
    {
161 1670
        return $this->serialize();
162
    }
163
164
    /**
165
     * @return int
166
     */
167 310
    public function getInt()
168
    {
169 310
        if ($this->math->cmp(gmp_init($this->number, 10), gmp_init(self::MAX)) > 0) {
170
            return self::MAX;
171 310
        } else if ($this->math->cmp(gmp_init($this->number, 10), gmp_init(self::MIN)) < 0) {
172
            return self::MIN;
173
        }
174
175 310
        return $this->number;
176
    }
177
178
    /**
179
     * @return \GMP
180
     */
181 468
    public function getGmp()
182
    {
183 468
        return gmp_init($this->number, 10);
184
    }
185
}
186