GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Push — master ( 6e6a53...7aa2fb )
by Joni
03:21
created

BigInt::signedOctets()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 6
c 0
b 0
f 0
nc 3
nop 0
dl 0
loc 10
ccs 6
cts 6
cp 1
crap 3
rs 10
1
<?php
2
3
declare(strict_types = 1);
4
5
namespace Sop\ASN1\Util;
6
7
/**
8
 * Class to wrap an integer of arbirtary length.
9
 */
10
class BigInt
11
{
12
    /**
13
     * Number as a GMP object.
14
     *
15
     * @var \GMP
16
     */
17
    private $_gmp;
18
19
    /**
20
     * Number as a base 10 integer string.
21
     *
22
     * @internal Lazily initialized
23
     *
24
     * @var null|string
25
     */
26
    private $_num;
27
28
    /**
29
     * Number as an integer type.
30
     *
31
     * @internal Lazily initialized
32
     *
33
     * @var null|int
34
     */
35
    private $_intNum;
36
37
    /**
38
     * Constructor.
39
     *
40
     * @param \GMP|int|string $num Integer number in base 10
41
     */
42 469
    public function __construct($num)
43
    {
44
        // convert to GMP object
45 469
        if (!($num instanceof \GMP)) {
46 449
            $gmp = @gmp_init($num, 10);
47 449
            if (false === $gmp) {
48 1
                throw new \InvalidArgumentException(
49 1
                    "Unable to convert '{$num}' to integer.");
50
            }
51 448
            $num = $gmp;
52
        }
53 468
        $this->_gmp = $num;
1 ignored issue
show
Documentation Bug introduced by
It seems like $num can also be of type resource. However, the property $_gmp is declared as type GMP. 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...
54 468
    }
55
56
    /**
57
     * @return string
58
     */
59 1
    public function __toString(): string
60
    {
61 1
        return $this->base10();
62
    }
63
64
    /**
65
     * Initialize from an arbitrary length of octets as an unsigned integer.
66
     *
67
     * @param string $octets
68
     *
69
     * @return self
70
     */
71 35
    public static function fromUnsignedOctets(string $octets): self
72
    {
73 35
        if (!strlen($octets)) {
74 1
            throw new \InvalidArgumentException('Empty octets.');
75
        }
76 34
        return new self(gmp_import($octets, 1, GMP_MSW_FIRST | GMP_BIG_ENDIAN));
1 ignored issue
show
Bug introduced by
It seems like gmp_import($octets, 1, S...N1\Util\GMP_BIG_ENDIAN) can also be of type false; however, parameter $num of Sop\ASN1\Util\BigInt::__construct() does only seem to accept GMP|integer|string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

76
        return new self(/** @scrutinizer ignore-type */ gmp_import($octets, 1, GMP_MSW_FIRST | GMP_BIG_ENDIAN));
Loading history...
77
    }
78
79
    /**
80
     * Initialize from an arbitrary length of octets as an signed integer
81
     * having two's complement encoding.
82
     *
83
     * @param string $octets
84
     *
85
     * @return self
86
     */
87 71
    public static function fromSignedOctets(string $octets): self
88
    {
89 71
        if (!strlen($octets)) {
90 1
            throw new \InvalidArgumentException('Empty octets.');
91
        }
92 70
        $neg = ord($octets[0]) & 0x80;
93
        // negative, apply inversion of two's complement
94 70
        if ($neg) {
95 32
            $octets = ~$octets;
96
        }
97 70
        $num = gmp_import($octets, 1, GMP_MSW_FIRST | GMP_BIG_ENDIAN);
98
        // negative, apply addition of two's complement and produce negative result
99 70
        if ($neg) {
100 32
            $num = gmp_neg($num + 1);
101
        }
102 70
        return new self($num);
1 ignored issue
show
Bug introduced by
It seems like $num can also be of type false and resource; however, parameter $num of Sop\ASN1\Util\BigInt::__construct() does only seem to accept GMP|integer|string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

102
        return new self(/** @scrutinizer ignore-type */ $num);
Loading history...
103
    }
104
105
    /**
106
     * Get the number as a base 10 integer string.
107
     *
108
     * @return string
109
     */
110 35
    public function base10(): string
111
    {
112 35
        if (!isset($this->_num)) {
113 35
            $this->_num = gmp_strval($this->_gmp, 10);
114
        }
115 35
        return $this->_num;
1 ignored issue
show
Bug Best Practice introduced by
The expression return $this->_num could return the type null which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
116
    }
117
118
    /**
119
     * Get the number as an integer.
120
     *
121
     * @throws \RuntimeException If number overflows integer size
122
     *
123
     * @return int
124
     */
125 313
    public function intVal(): int
126
    {
127 313
        if (!isset($this->_intNum)) {
128 311
            if (gmp_cmp($this->_gmp, $this->_intMaxGmp()) > 0) {
129 4
                throw new \RuntimeException('Integer overflow.');
130
            }
131 307
            if (gmp_cmp($this->_gmp, $this->_intMinGmp()) < 0) {
132 1
                throw new \RuntimeException('Integer underflow.');
133
            }
134 306
            $this->_intNum = gmp_intval($this->_gmp);
135
        }
136 308
        return $this->_intNum;
137
    }
138
139
    /**
140
     * Get the number as a `GMP` object.
141
     *
142
     * @throws \RuntimeException if number is not a valid integer
143
     *
144
     * @return \GMP
145
     */
146 238
    public function gmpObj(): \GMP
147
    {
148 238
        return clone $this->_gmp;
149
    }
150
151
    /**
152
     * Get the number as an unsigned integer encoded in binary.
153
     *
154
     * @return string
155
     */
156 35
    public function unsignedOctets(): string
157
    {
158 35
        return gmp_export($this->_gmp, 1, GMP_MSW_FIRST | GMP_BIG_ENDIAN);
159
    }
160
161
    /**
162
     * Get the number as a signed integer encoded in two's complement binary.
163
     *
164
     * @return string
165
     */
166 71
    public function signedOctets(): string
167
    {
168 71
        switch (gmp_sign($this->_gmp)) {
169 71
            case 1:
170 31
                return $this->_signedPositiveOctets();
171
            case -1:
172 33
                return $this->_signedNegativeOctets();
173
        }
174
        // zero
175 7
        return chr(0);
176
    }
177
178
    /**
179
     * Encode positive integer in two's complement binary.
180
     *
181
     * @return string
182
     */
183 31
    private function _signedPositiveOctets(): string
184
    {
185 31
        $bin = gmp_export($this->_gmp, 1, GMP_MSW_FIRST | GMP_BIG_ENDIAN);
186
        // if first bit is 1, prepend full zero byte to represent positive two's complement
187 31
        if (ord($bin[0]) & 0x80) {
188 6
            $bin = chr(0x00) . $bin;
189
        }
190 31
        return $bin;
191
    }
192
193
    /**
194
     * Encode negative integer in two's complement binary.
195
     *
196
     * @return string
197
     */
198 33
    private function _signedNegativeOctets(): string
199
    {
200 33
        $num = gmp_abs($this->_gmp);
201
        // compute number of bytes required
202 33
        $width = 1;
203 33
        if ($num > 128) {
204 24
            $tmp = $num;
205
            do {
206 24
                ++$width;
207 24
                $tmp >>= 8;
208 24
            } while ($tmp > 128);
209
        }
210
        // compute two's complement 2^n - x
211 33
        $num = gmp_pow('2', 8 * $width) - $num;
212 33
        $bin = gmp_export($num, 1, GMP_MSW_FIRST | GMP_BIG_ENDIAN);
213
        // if first bit is 0, prepend full inverted byte to represent negative two's complement
214 33
        if (!(ord($bin[0]) & 0x80)) {
215 2
            $bin = chr(0xff) . $bin;
216
        }
217 33
        return $bin;
218
    }
219
220
    /**
221
     * Get the maximum integer value.
222
     *
223
     * @return \GMP
224
     */
225 311
    private function _intMaxGmp(): \GMP
226
    {
227 311
        static $gmp;
228 311
        if (!isset($gmp)) {
229 1
            $gmp = gmp_init(PHP_INT_MAX, 10);
230
        }
231 311
        return $gmp;
232
    }
233
234
    /**
235
     * Get the minimum integer value.
236
     *
237
     * @return \GMP
238
     */
239 307
    private function _intMinGmp(): \GMP
240
    {
241 307
        static $gmp;
242 307
        if (!isset($gmp)) {
243 1
            $gmp = gmp_init(PHP_INT_MIN, 10);
244
        }
245 307
        return $gmp;
246
    }
247
}
248