Passed
Push — master ( 9e1378...d02904 )
by Mark
16:48 queued 06:34
created

FormattedNumber::convertToNumberIfNumeric()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 5
c 1
b 0
f 0
dl 0
loc 11
ccs 6
cts 6
cp 1
rs 10
cc 2
nc 2
nop 1
crap 2
1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet\Calculation\Engine;
4
5
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
6
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
7
8
class FormattedNumber
9
{
10
    /**    Constants                */
11
    /**    Regular Expressions        */
12
    private const STRING_REGEXP_FRACTION = '~^\s*(-?)((\d*)\s+)?(\d+\/\d+)\s*$~';
13
14
    private const STRING_REGEXP_PERCENT = '~^(?:(?: *(?<PrefixedSign>[-+])? *\% *(?<PrefixedSign2>[-+])? *(?<PrefixedValue>[0-9]+\.?[0-9*]*(?:E[-+]?[0-9]*)?) *)|(?: *(?<PostfixedSign>[-+])? *(?<PostfixedValue>[0-9]+\.?[0-9]*(?:E[-+]?[0-9]*)?) *\% *))$~i';
15
16
    // preg_quoted string for major currency symbols, with a %s for locale currency
17
    private const CURRENCY_CONVERSION_LIST = '\$€£¥%s';
18
19
    private const STRING_CONVERSION_LIST = [
20
        [self::class, 'convertToNumberIfNumeric'],
21
        [self::class, 'convertToNumberIfFraction'],
22
        [self::class, 'convertToNumberIfPercent'],
23
        [self::class, 'convertToNumberIfCurrency'],
24
    ];
25
26
    /**
27
     * Identify whether a string contains a formatted numeric value,
28
     * and convert it to a numeric if it is.
29
     *
30
     * @param string $operand string value to test
31
     */
32 13
    public static function convertToNumberIfFormatted(string &$operand): bool
33
    {
34 13
        foreach (self::STRING_CONVERSION_LIST as $conversionMethod) {
35 13
            if ($conversionMethod($operand) === true) {
36 9
                return true;
37
            }
38
        }
39
40 4
        return false;
41
    }
42
43
    /**
44
     * Identify whether a string contains a numeric value,
45
     * and convert it to a numeric if it is.
46
     *
47
     * @param string $operand string value to test
48
     */
49 13
    public static function convertToNumberIfNumeric(string &$operand): bool
50
    {
51 13
        $value = preg_replace(['/(\d),(\d)/u', '/([+-])\s+(\d)/u'], ['$1$2', '$1$2'], trim($operand));
52
53 13
        if (is_numeric($value)) {
54 5
            $operand = (float) $value;
55
56 5
            return true;
57
        }
58
59 8
        return false;
60
    }
61
62
    /**
63
     * Identify whether a string contains a fractional numeric value,
64
     * and convert it to a numeric if it is.
65
     *
66
     * @param string $operand string value to test
67
     */
68 15
    public static function convertToNumberIfFraction(string &$operand): bool
69
    {
70 15
        if (preg_match(self::STRING_REGEXP_FRACTION, $operand, $match)) {
71 7
            $sign = ($match[1] === '-') ? '-' : '+';
72 7
            $wholePart = ($match[3] === '') ? '' : ($sign . $match[3]);
73 7
            $fractionFormula = '=' . $wholePart . $sign . $match[4];
74 7
            $operand = Calculation::getInstance()->_calculateFormulaValue($fractionFormula);
75
76 7
            return true;
77
        }
78
79 8
        return false;
80
    }
81
82
    /**
83
     * Identify whether a string contains a percentage, and if so,
84
     * convert it to a numeric.
85
     *
86
     * @param string $operand string value to test
87
     */
88 110
    public static function convertToNumberIfPercent(string &$operand): bool
89
    {
90 110
        $value = preg_replace('/(\d),(\d)/u', '$1$2', $operand);
91
92 110
        $match = [];
93 110
        if ($value !== null && preg_match(self::STRING_REGEXP_PERCENT, $value, $match, PREG_UNMATCHED_AS_NULL)) {
94
            //Calculate the percentage
95 84
            $sign = ($match['PrefixedSign'] ?? $match['PrefixedSign2'] ?? $match['PostfixedSign']) ?? '';
96 84
            $operand = (float) ($sign . ($match['PostfixedValue'] ?? $match['PrefixedValue'])) / 100;
97
98 84
            return true;
99
        }
100
101 26
        return false;
102
    }
103
104
    /**
105
     * Identify whether a string contains a currency value, and if so,
106
     * convert it to a numeric.
107
     *
108
     * @param string $operand string value to test
109
     */
110 25
    public static function convertToNumberIfCurrency(string &$operand): bool
111
    {
112 25
        $currencyRegexp = self::currencyMatcherRegexp();
113 25
        $value = preg_replace('/(\d),(\d)/u', '$1$2', $operand);
114
115 25
        $match = [];
116 25
        if ($value !== null && preg_match($currencyRegexp, $value, $match, PREG_UNMATCHED_AS_NULL)) {
117
            //Determine the sign
118 21
            $sign = ($match['PrefixedSign'] ?? $match['PrefixedSign2'] ?? $match['PostfixedSign']) ?? '';
119
            //Cast to a float
120 21
            $operand = (float) ($sign . ($match['PostfixedValue'] ?? $match['PrefixedValue']));
121
122 21
            return true;
123
        }
124
125 4
        return false;
126
    }
127
128 25
    public static function currencyMatcherRegexp(): string
129
    {
130 25
        $quotedCurrencyCode = sprintf(self::CURRENCY_CONVERSION_LIST, preg_quote(StringHelper::getCurrencyCode()));
131
132 25
        return '~^(?:(?: *(?<PrefixedSign>[-+])? *(?<PrefixedCurrency>[' . $quotedCurrencyCode . ']) *(?<PrefixedSign2>[-+])? *(?<PrefixedValue>[0-9]+\.?[0-9*]*(?:E[-+]?[0-9]*)?) *)|(?: *(?<PostfixedSign>[-+])? *(?<PostfixedValue>[0-9]+\.?[0-9]*(?:E[-+]?[0-9]*)?) *(?<PostCurrency>[' . $quotedCurrencyCode . ']) *))$~ui';
133
    }
134
}
135