Completed
Pull Request — master (#5)
by X
02:25
created

FloatParser::parseDnum()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 20
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 4

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 14
c 1
b 0
f 0
nc 4
nop 1
dl 0
loc 20
ccs 15
cts 15
cp 1
crap 4
rs 9.2
1
<?php
2
/**
3
 * parser for PHP serialized float number
4
 */
5
namespace xKerman\Restricted;
6
7
/**
8
 * Parser for PHP serialized float number
9
 *
10
 * format: http://php.net/manual/en/language.types.float.php
11
 */
12
class FloatParser implements ParserInterface
13
{
14
    /**
15
     * parse given `$source` as PHP serialized float number
16
     *
17
     * @param Source $source parser input
18
     * @return array
19
     * @throws UnserializeFailedException
20
     */
21 29
    public function parse(Source $source)
22
    {
23 29
        $source->consume('d');
24 26
        $source->consume(':');
25
26 26
        $result = '';
27
28 26
        if ($source->peek() === 'N') {
29 1
            return $this->parseNan($source);
30
        }
31
32 25
        if ($this->isSign($source->peek())) {
33 11
            $result .= $source->peek();
34 11
            $source->next();
35 11
        }
36
37 25
        if ($source->peek() === 'I') {
38 3
            if ($result === '+') {
39 1
                return $source->triggerError();
40
            }
41 2
            return $this->parseInf($source, ($result === '-'));
42
        }
43
44 22
        list($dnum, $source) = $this->parseDnum($source);
45 14
        $result .= $dnum;
46
47 14
        list($exponential, $source) = $this->parseExponentialPart($source);
48 14
        $result .= $exponential;
49
50 14
        $source->consume(';');
51 12
        return [floatval($result), $source];
52
    }
53
54
    /**
55
     * judge if given `$char` is minus|plus sign
56
     *
57
     * @param string $char target character
58
     * @return boolean
59
     */
60 25
    private function isSign($char)
61
    {
62 25
        return $char === '+' || $char === '-';
63
    }
64
65
    /**
66
     * parse given `$source` as NAN
67
     *
68
     * @param Source $source input
69
     * @return array
70
     * @throws UnserializeFailedException
71
     */
72 1
    private function parseNan($source)
73
    {
74 1
        $source->consume('N');
75 1
        $source->consume('A');
76 1
        $source->consume('N');
77 1
        $source->consume(';');
78 1
        return [NAN, $source];
79
    }
80
81
    /**
82
     * parse given `$source` as INF / -INF
83
     *
84
     * @param Source  $source input
85
     * @param boolean $minus  target is negative or not
86
     * @return array
87
     * @throws UnserializeFailedException
88
     */
89 2
    private function parseInf($source, $minus)
90
    {
91 2
        $source->consume('I');
92 2
        $source->consume('N');
93 2
        $source->consume('F');
94 2
        $source->consume(';');
95
96 2
        if ($minus) {
97 1
            return [-INF, $source];
98
        }
99 1
        return [INF, $source];
100
    }
101
102
    /**
103
     * parse given `$source` as sequence of DIGIT
104
     *
105
     * @param Source $source input
106
     * @return array
107
     * @throws UnserializeFailedException
108
     */
109 22
    private function parseDigits($source)
110
    {
111 22
        $result = '';
112 22
        while (ctype_digit($source->peek())) {
113 14
            $result .= $source->peek();
114 14
            $source->next();
115 14
        }
116 22
        return [$result, $source];
117
    }
118
119
    /**
120
     * parse integer part and fraction part
121
     *
122
     * @param Source $source input
123
     * @return array
124
     * @throws UnserializeFailedException
125
     */
126 22
    private function parseDnum($source)
127
    {
128 22
        list($digits, $source) = $this->parseDigits($source);
129 22
        $result = $digits;
130 22
        $hasIntegerPart = ($digits !== '');
131
132 22
        $hasFractionPart = false;
133 22
        if ($source->peek() === '.') {
134 13
            $result .= $source->peek();
135 13
            $source->next();
136 13
            list($digits, $source) = $this->parseDigits($source);
137 13
            $result .= $digits;
138 13
            $hasFractionPart = ($digits !== '');
139 13
        }
140
141 22
        if (!$hasIntegerPart && !$hasFractionPart) {
142 8
            return $source->triggerError();
143
        }
144 14
        return [$result, $source];
145
    }
146
147
    /**
148
     * parse given `$source` as exponentail part
149
     *
150
     * @param Source $source input
151
     * @return array
152
     * @throws UnserializeFailedException
153
     */
154 14
    private function parseExponentialPart($source)
155
    {
156 14
        if (strtolower($source->peek()) !== 'e') {
157 8
            return ['', $source];
158
        }
159
160 6
        $result = $source->peek();
161 6
        $source->next();
162 6
        $parser = new NumberStringParser();
163 6
        list($exp, $source) = $parser->parse($source);
164 6
        $result .= $exp;
165 6
        return [$result, $source];
166
    }
167
}
168