Completed
Push — master ( d90068...e376d8 )
by thomas
27:11 queued 20:09
created

Number   A

Complexity

Total Complexity 29

Size/Duplication

Total Lines 170
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 90.77%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 29
c 1
b 0
f 0
lcom 1
cbo 5
dl 0
loc 170
ccs 59
cts 65
cp 0.9077
rs 10

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A int() 0 7 2
A gmp() 0 7 2
C buffer() 0 22 8
B parseBuffer() 0 23 4
C serialize() 0 25 7
A getBuffer() 0 4 1
A getInt() 0 10 3
A getGmp() 0 4 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace BitWasp\Bitcoin\Script\Interpreter;
6
7
use BitWasp\Bitcoin\Bitcoin;
8
use BitWasp\Bitcoin\Math\Math;
9
use BitWasp\Bitcoin\Serializable;
10
use BitWasp\Buffertools\Buffer;
11
use BitWasp\Buffertools\BufferInterface;
12
13
class Number extends Serializable
14
{
15
    const MAX_NUM_SIZE = 4;
16
    const MAX = 2**31-1;
17
    const MIN = -2**31+1;
18
19
    /**
20
     * @var Math
21
     */
22
    private $math;
23
24
    /**
25
     * @var int
26
     */
27
    private $number;
28
29
    /**
30
     * Number constructor.
31
     * @param int $number
32
     * @param Math $math
33
     */
34 3501
    public function __construct($number, Math $math)
35
    {
36 3501
        $this->number = $number;
37 3501
        $this->math = $math;
38 3501
    }
39
40
    /**
41
     * @param int $number
42
     * @param Math|null $math
43
     * @return self
44
     */
45 3230
    public static function int($number, Math $math = null)
46
    {
47 3230
        return new self(
48 3230
            $number,
49 3230
            $math ?: Bitcoin::getMath()
50
        );
51
    }
52
53
    /**
54
     * @param \GMP $number
55
     * @param Math|null $math
56
     * @return self
57
     */
58
    public static function gmp(\GMP $number, Math $math = null)
59
    {
60
        return new self(
61
            gmp_strval($number, 10),
62
            $math ?: Bitcoin::getMath()
63
        );
64
    }
65
66
    /**
67
     * @param BufferInterface $vch
68
     * @param bool $fRequireMinimal
69
     * @param int $maxNumSize
70
     * @param Math|null $math
71
     * @return self
72
     */
73 2694
    public static function buffer(BufferInterface $vch, $fRequireMinimal, $maxNumSize = self::MAX_NUM_SIZE, Math $math = null)
74
    {
75 2694
        $size = $vch->getSize();
76 2694
        if ($size > $maxNumSize) {
77 44
            throw new \RuntimeException('Script number overflow');
78
        }
79
80 2658
        if ($fRequireMinimal && $size > 0) {
81 220
            $binary = $vch->getBinary();
82
            //$chars = array_values(unpack("C*", $binary));
83 220
            if ((ord($binary[$size - 1]) & 0x7f) === 0) {
84 220
                if ($size <= 1 || (ord($binary[$size - 2]) & 0x80) === 0) {
85 220
                    throw new \RuntimeException('Non-minimally encoded script number');
86
                }
87
            }
88
        }
89
90 2510
        $math = $math ?: Bitcoin::getMath();
91 2510
        $number = new self(0, $math);
92 2510
        $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...
93 2510
        return $number;
94
    }
95
96
    /**
97
     * @param BufferInterface $buffer
98
     * @return int
99
     */
100 2510
    private function parseBuffer(BufferInterface $buffer)
101
    {
102 2510
        $size = $buffer->getSize();
103 2510
        if ($size === 0) {
104 1421
            return '0';
105
        }
106
107 1677
        $chars = array_values(unpack("C*", $buffer->getBinary()));
108
109 1677
        $result = gmp_init(0);
110 1677
        for ($i = 0; $i < $size; $i++) {
111 1677
            $mul = $i * 8;
112 1677
            $byte = $this->math->leftShift(gmp_init($chars[$i], 10), $mul);
113 1677
            $result = $this->math->bitwiseOr($result, $byte);
0 ignored issues
show
Bug introduced by
It seems like $result 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...
114
        }
115
116 1677
        if ($chars[count($chars)-1] & 0x80) {
117 197
            $mask = gmp_com($this->math->leftShift(gmp_init(0x80), (8 * ($size - 1))));
118 197
            $result = $this->math->sub(gmp_init(0), $this->math->bitwiseAnd($result, $mask));
0 ignored issues
show
Bug introduced by
It seems like $result 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...
119
        }
120
121 1677
        return gmp_strval($result, 10);
122
    }
123
124
    /**
125
     * @return BufferInterface
126
     */
127 3378
    private function serialize()
128
    {
129 3378
        if ((int) $this->number === 0) {
130 1126
            return new Buffer('', 0);
131
        }
132
133 3136
        $zero = gmp_init(0);
134
        // Using array of integers instead of bytes
135 3136
        $result = [];
136 3136
        $negative = $this->math->cmp(gmp_init($this->number), $zero) < 0;
137 3136
        $abs = $negative ? $this->math->sub($zero, gmp_init($this->number, 10)) : gmp_init($this->number, 10);
138 3136
        $mask = gmp_init(0xff);
139 3136
        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 137 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...
140 3136
            $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 137 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...
141 3136
            $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...
142
        }
143
144 3136
        if ($result[count($result) - 1] & 0x80) {
145 26
            $result[] = $negative ? 0x80 : 0;
146 3118
        } else if ($negative) {
147 108
            $result[count($result) - 1] |= 0x80;
148
        }
149
150 3136
        return new Buffer(pack("C*", ...$result));
151
    }
152
153
    /**
154
     * @return BufferInterface
155
     */
156 3378
    public function getBuffer(): BufferInterface
157
    {
158 3378
        return $this->serialize();
159
    }
160
161
    /**
162
     * @return int
163
     */
164 462
    public function getInt()
165
    {
166 462
        if ($this->math->cmp(gmp_init($this->number, 10), gmp_init(self::MAX)) > 0) {
167
            return self::MAX;
168 462
        } else if ($this->math->cmp(gmp_init($this->number, 10), gmp_init(self::MIN)) < 0) {
169
            return self::MIN;
170
        }
171
172 462
        return $this->number;
173
    }
174
175
    /**
176
     * @return \GMP
177
     */
178 948
    public function getGmp()
179
    {
180 948
        return gmp_init($this->number, 10);
181
    }
182
}
183