BaseConverter::convertInteger()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
crap 1
1
<?php
2
3
namespace Riimu\Kit\BaseConversion;
4
5
/**
6
 * Arbitrary precision number base converter.
7
 *
8
 * BaseConverter provides convenience to number conversion by providing a method
9
 * that accepts numbers as strings in addition to selecting the appropriate
10
 * number conversion strategy based on the provided number bases (which may also
11
 * be provided as constructor arguments for NumberBase instead of instances of
12
 * the said class).
13
 *
14
 * BaseConverter can also be used as a simple replacement for PHP's built in
15
 * `base_convert()` via the provided static method that accepts arguments
16
 * similar to the built in function in addition to providing the extra features
17
 * of this library (such as arbitrary precision conversion and support for
18
 * fractions).
19
 *
20
 * @author Riikka Kalliomäki <[email protected]>
21
 * @copyright Copyright (c) 2014-2017 Riikka Kalliomäki
22
 * @license http://opensource.org/licenses/mit-license.php MIT License
23
 */
24
class BaseConverter implements Converter
25
{
26
    /** @var Converter Selected converter for base conversion */
27
    private $converter;
28
29
    /** @var int Precision provided to the fraction converter */
30
    private $precision;
31
32
    /** @var NumberBase Number base used by provided numbers */
33
    private $source;
34
35
    /** @var NumberBase Number base used by returned numbers */
36
    private $target;
37
38
    /**
39
     * Creates a new instance of BaseConverter.
40
     *
41
     * The source and target number bases can be provided either as an instance
42
     * of the NumberBase class or as constructor arguments that are provided to
43
     * the NumberBase class.
44
     *
45
     * The constructor will select the most optimal conversion strategy based
46
     * on the provided number bases.
47
     *
48
     * @see NumberBase::__construct
49
     * @param mixed $sourceBase Number base used by the provided numbers
50
     * @param mixed $targetBase Number base used by the returned numbers
51
     */
52 39
    public function __construct($sourceBase, $targetBase)
53
    {
54 39
        $this->source = $sourceBase instanceof NumberBase ? $sourceBase : new NumberBase($sourceBase);
55 39
        $this->target = $sourceBase instanceof NumberBase ? $targetBase : new NumberBase($targetBase);
56
57
        try {
58 39
            $this->converter = new ReplaceConverter($this->source, $this->target);
59 25
        } catch (InvalidNumberBaseException $ex) {
60 18
            $this->converter = new DecimalConverter($this->source, $this->target);
61
        }
62
63 39
        $this->precision = -1;
64 39
    }
65
66
    /**
67
     * Converts the provided number from base to another.
68
     *
69
     * This method provides a convenient replacement to PHP's built in
70
     * `base_convert()`. The number bases are simply passed along to the
71
     * constructor, which means they can be instances of NumberBase class or
72
     * constructor parameters for that class.
73
     *
74
     * Note that due to the way the constructor parameters for NumberBase work,
75
     * this method can be used exactly the same way as `base_convert()`.
76
     *
77
     * @param string $number The number to convert
78
     * @param mixed $fromBase Number base used by the provided number
79
     * @param mixed $toBase Number base used by the returned number
80
     * @param int $precision Precision for inaccurate conversion
81
     * @return string|false The converted number or false on error
82
     */
83 3
    public static function baseConvert($number, $fromBase, $toBase, $precision = -1)
84
    {
85 3
        $converter = new self($fromBase, $toBase);
86 3
        $converter->setPrecision($precision);
87
88 3
        return $converter->convert($number);
89
    }
90
91
    /**
92
     * Converts the number provided as a string from source base to target base.
93
     *
94
     * This method provides convenient conversions by accepting the number as
95
     * a string. The number may optionally be preceded by a plus or minus sign
96
     * which is prepended to the result as well. The number may also have a
97
     * period, which separates the integer part and the fractional part.
98
     *
99
     * Due to the special meaning of `+`, `-` and `.`, it is not recommended to
100
     * use this method to convert numbers when using number bases that have a
101
     * meaning for these characters (such as base64).
102
     *
103
     * If the number contains invalid characters, the method will return false
104
     * instead.
105
     *
106
     * @param string $number The number to convert
107
     * @return string|false The converted number or false on error
108
     */
109 18
    public function convert($number)
110
    {
111 18
        $integer = (string) $number;
112 18
        $fractions = null;
113 18
        $sign = '';
114
115 18
        if (in_array(substr($integer, 0, 1), ['+', '-'], true)) {
116 12
            $sign = $integer[0];
117 12
            $integer = substr($integer, 1);
118 4
        }
119
120 18
        if (($pos = strpos($integer, '.')) !== false) {
121 12
            $fractions = substr($integer, $pos + 1);
122 12
            $integer = substr($integer, 0, $pos);
123 4
        }
124
125 18
        return $this->convertNumber($sign, $integer, $fractions);
126
    }
127
128
    /**
129
     * Converts the different parts of the number and handles invalid digits.
130
     * @param string $sign Sign that preceded the number or an empty string
131
     * @param string $integer The integer part of the number
132
     * @param string|null $fractions The fractional part of the number or null if none
133
     * @return string|false The converted number or false on error
134
     */
135 18
    private function convertNumber($sign, $integer, $fractions)
136
    {
137
        try {
138 18
            $result = implode('', $this->convertInteger($this->source->splitString($integer)));
139
140 15
            if ($fractions !== null) {
141 14
                $result .= '.' . implode('', $this->convertFractions($this->source->splitString($fractions)));
142 4
            }
143 8
        } catch (DigitList\InvalidDigitException $ex) {
144 3
            return false;
145
        }
146
147 15
        return $sign . $result;
148
    }
149
150 6
    public function setPrecision($precision)
151
    {
152 6
        $this->precision = (int) $precision;
153 6
    }
154
155 21
    public function convertInteger(array $number)
156
    {
157 21
        return $this->converter->convertInteger($number);
158
    }
159
160 18
    public function convertFractions(array $number)
161
    {
162 18
        $this->converter->setPrecision($this->precision);
163
164 18
        return $this->converter->convertFractions($number);
165
    }
166
}
167