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 ( 95c197...564e5e )
by Joni
04:32
created

Real   A

Complexity

Total Complexity 33

Size/Duplication

Total Lines 273
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
eloc 100
dl 0
loc 273
ccs 91
cts 91
cp 1
rs 9.76
c 0
b 0
f 0
wmc 33

11 Methods

Rating   Name   Duplication   Size   Complexity  
A _validateNumber() 0 6 2
A _decodeSpecialRealValue() 0 14 4
A __construct() 0 8 2
B _nr3ToDecimal() 0 30 7
A _encodedContentDER() 0 9 2
B _decimalToNR3() 0 41 7
A _decodeFromDER() 0 21 4
A fromFloat() 0 3 1
A _decodeBinaryEncoding() 0 4 1
A float() 0 3 1
A _decodeDecimalEncoding() 0 8 2
1
<?php
2
3
declare(strict_types = 1);
4
5
namespace Sop\ASN1\Type\Primitive;
6
7
use Sop\ASN1\Component\Identifier;
8
use Sop\ASN1\Component\Length;
9
use Sop\ASN1\Element;
10
use Sop\ASN1\Exception\DecodeException;
11
use Sop\ASN1\Feature\ElementBase;
12
use Sop\ASN1\Type\PrimitiveType;
13
use Sop\ASN1\Type\UniversalClass;
14
15
/**
16
 * Implements *REAL* type.
17
 */
18
class Real extends Element
19
{
20
    use UniversalClass;
21
    use PrimitiveType;
22
23
    /**
24
     * Regex pattern to parse NR3 form number conforming to DER.
25
     *
26
     * @var string
27
     */
28
    const NR3_REGEX = '/^(-?)(\d+)?\.E([+\-]?\d+)$/';
29
30
    /**
31
     * Regex pattern to parse PHP exponent number format.
32
     *
33
     * @see http://php.net/manual/en/language.types.float.php
34
     *
35
     * @var string
36
     */
37
    const PHP_EXPONENT_DNUM = '/^' .
38
        '([+\-]?' . // sign
39
        '(?:' .
40
            '\d+' . // LNUM
41
            '|' .
42
            '(?:\d*\.\d+|\d+\.\d*)' . // DNUM
43
        '))[eE]' .
44
        '([+\-]?\d+)' . // exponent
45
    '$/';
46
47
    /**
48
     * Number zero represented in NR3 form.
49
     *
50
     * @var string
51
     */
52
    const NR3_ZERO = '.E+0';
53
54
    /**
55
     * Number in NR3 form.
56
     *
57
     * @var string
58
     */
59
    private $_number;
60
61
    /**
62
     * Constructor.
63
     *
64
     * @param string $number number in NR3 form
65
     */
66 18
    public function __construct(string $number)
67
    {
68 18
        $this->_typeTag = self::TYPE_REAL;
69 18
        if (!self::_validateNumber($number)) {
70 2
            throw new \InvalidArgumentException(
71 2
                "'{$number}' is not a valid NR3 form real.");
72
        }
73 16
        $this->_number = $number;
74 16
    }
75
76
    /**
77
     * Initialize from float.
78
     *
79
     * @param float $number
80
     *
81
     * @return self
82
     */
83 13
    public static function fromFloat(float $number): self
84
    {
85 13
        return new self(self::_decimalToNR3(strval($number)));
86
    }
87
88
    /**
89
     * Get number as a float.
90
     *
91
     * @return float
92
     */
93 14
    public function float(): float
94
    {
95 14
        return self::_nr3ToDecimal($this->_number);
96
    }
97
98
    /**
99
     * {@inheritdoc}
100
     */
101 14
    protected function _encodedContentDER(): string
102
    {
103
        /* if the real value is the value zero, there shall be no contents
104
         octets in the encoding. (X.690 07-2002, section 8.5.2) */
105 14
        if (self::NR3_ZERO === $this->_number) {
106 1
            return '';
107
        }
108
        // encode in NR3 decimal encoding
109 13
        return chr(0x03) . $this->_number;
110
    }
111
112
    /**
113
     * {@inheritdoc}
114
     */
115 21
    protected static function _decodeFromDER(Identifier $identifier,
116
        string $data, int &$offset): ElementBase
117
    {
118 21
        $idx = $offset;
119 21
        $length = Length::expectFromDER($data, $idx)->intLength();
120
        // if length is zero, value is zero (spec 8.5.2)
121 21
        if (!$length) {
122 1
            $obj = new self(self::NR3_ZERO);
123
        } else {
124 20
            $bytes = substr($data, $idx, $length);
125 20
            $byte = ord($bytes[0]);
126 20
            if (0x80 & $byte) { // bit 8 = 1
127 1
                $obj = self::_decodeBinaryEncoding($bytes);
128 19
            } elseif (0x00 === $byte >> 6) { // bit 8 = 0, bit 7 = 0
129 15
                $obj = self::_decodeDecimalEncoding($bytes);
130
            } else { // bit 8 = 0, bit 7 = 1
131 4
                $obj = self::_decodeSpecialRealValue($bytes);
132
            }
133
        }
134 14
        $offset = $idx + $length;
135 14
        return $obj;
136
    }
137
138
    /**
139
     * @todo Implement
140
     *
141
     * @param string $data
142
     */
143 1
    protected static function _decodeBinaryEncoding(string $data)
144
    {
145 1
        throw new \RuntimeException(
146 1
            'Binary encoding of REAL is not implemented.');
147
    }
148
149
    /**
150
     * @param string $data
151
     *
152
     * @throws \RuntimeException
153
     *
154
     * @return self
155
     */
156 15
    protected static function _decodeDecimalEncoding(string $data): self
157
    {
158 15
        $nr = ord($data[0]) & 0x03;
159 15
        if (0x03 !== $nr) {
160 1
            throw new \RuntimeException('Only NR3 form supported.');
161
        }
162 14
        $str = substr($data, 1);
163 14
        return new self($str);
164
    }
165
166
    /**
167
     * @todo Implement
168
     *
169
     * @param string $data
170
     */
171 4
    protected static function _decodeSpecialRealValue(string $data)
172
    {
173 4
        if (1 !== strlen($data)) {
174 1
            throw new DecodeException(
175 1
                'SpecialRealValue must have one content octet.');
176
        }
177 3
        $byte = ord($data[0]);
178 3
        if (0x40 === $byte) { // positive infinity
179 1
            throw new \RuntimeException('PLUS-INFINITY not supported.');
180
        }
181 2
        if (0x41 === $byte) { // negative infinity
182 1
            throw new \RuntimeException('MINUS-INFINITY not supported.');
183
        }
184 1
        throw new DecodeException('Invalid SpecialRealValue encoding.');
185
    }
186
187
    /**
188
     * Convert decimal number string to NR3 form.
189
     *
190
     * @param string $str
191
     *
192
     * @return string
193
     */
194 13
    private static function _decimalToNR3(string $str): string
195
    {
196
        // if number is in exponent form
197
        /** @var string[] $match */
198 13
        if (preg_match(self::PHP_EXPONENT_DNUM, $str, $match)) {
199 4
            $parts = explode('.', $match[1]);
200 4
            $m = ltrim($parts[0], '0');
201 4
            $e = intval($match[2]);
202
            // if mantissa had decimals
203 4
            if (2 === count($parts)) {
204 4
                $d = rtrim($parts[1], '0');
205 4
                $e -= strlen($d);
206 4
                $m .= $d;
207
            }
208
        } else {
209
            // explode from decimal
210 9
            $parts = explode('.', $str);
211 9
            $m = ltrim($parts[0], '0');
212
            // if number had decimals
213 9
            if (2 === count($parts)) {
214
                // exponent is negative number of the decimals
215 4
                $e = -strlen($parts[1]);
216
                // append decimals to the mantissa
217 4
                $m .= $parts[1];
218
            } else {
219 5
                $e = 0;
220
            }
221
            // shift trailing zeroes from the mantissa to the exponent
222 9
            while ('0' === substr($m, -1)) {
223 2
                ++$e;
224 2
                $m = substr($m, 0, -1);
225
            }
226
        }
227
        /* if exponent is zero, it must be prefixed with a "+" sign
228
         (X.690 07-2002, section 11.3.2.6) */
229 13
        if (0 === $e) {
230 3
            $es = '+';
231
        } else {
232 10
            $es = $e < 0 ? '-' : '';
233
        }
234 13
        return sprintf('%s.E%s%d', $m, $es, abs($e));
235
    }
236
237
    /**
238
     * Convert NR3 form number to decimal.
239
     *
240
     * @param string $str
241
     *
242
     * @throws \UnexpectedValueException
243
     *
244
     * @return float
245
     */
246 14
    private static function _nr3ToDecimal(string $str): float
247
    {
248
        /** @var string[] $match */
249 14
        if (!preg_match(self::NR3_REGEX, $str, $match)) {
250 1
            throw new \UnexpectedValueException(
251 1
                "'{$str}' is not a valid NR3 form real.");
252
        }
253 13
        $m = $match[2];
254
        // if number started with minus sign
255 13
        $inv = '-' === $match[1];
256 13
        $e = intval($match[3]);
257
        // positive exponent
258 13
        if ($e > 0) {
259
            // pad with trailing zeroes
260 2
            $num = $m . str_repeat('0', $e);
261 11
        } elseif ($e < 0) {
262
            // pad with leading zeroes
263 8
            if (strlen($m) < abs($e)) {
264 4
                $m = str_repeat('0', intval(abs($e)) - strlen($m)) . $m;
265
            }
266
            // insert decimal point
267 8
            $num = substr($m, 0, $e) . '.' . substr($m, $e);
268
        } else {
269 3
            $num = empty($m) ? '0' : $m;
270
        }
271
        // if number is negative
272 13
        if ($inv) {
273 6
            $num = "-{$num}";
274
        }
275 13
        return floatval($num);
276
    }
277
278
    /**
279
     * Test that number is valid for this context.
280
     *
281
     * @param mixed $num
282
     *
283
     * @return bool
284
     */
285 18
    private static function _validateNumber($num): bool
286
    {
287 18
        if (!preg_match(self::NR3_REGEX, $num)) {
288 2
            return false;
289
        }
290 16
        return true;
291
    }
292
}
293