diff --git a/src/PhpSpreadsheet/Calculation/Engine/FormattedNumber.php b/src/PhpSpreadsheet/Calculation/Engine/FormattedNumber.php index 70a231719..6853578d7 100644 --- a/src/PhpSpreadsheet/Calculation/Engine/FormattedNumber.php +++ b/src/PhpSpreadsheet/Calculation/Engine/FormattedNumber.php @@ -3,6 +3,7 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\Engine; use PhpOffice\PhpSpreadsheet\Calculation\Calculation; +use PhpOffice\PhpSpreadsheet\Shared\StringHelper; class FormattedNumber { @@ -17,6 +18,7 @@ class FormattedNumber [self::class, 'convertToNumberIfNumeric'], [self::class, 'convertToNumberIfFraction'], [self::class, 'convertToNumberIfPercent'], + [self::class, 'convertToNumberIfCurrency'], ]; /** @@ -92,4 +94,28 @@ class FormattedNumber return false; } + + /** + * Identify whether a string contains a currency value, and if so, + * convert it to a numeric. + * + * @param string $operand string value to test + */ + public static function convertToNumberIfCurrency(string &$operand): bool + { + $quotedCurrencyCode = preg_quote(StringHelper::getCurrencyCode()); + $regExp = '~^(?:(?: *(?[-+])? *' . $quotedCurrencyCode . ' *(?[-+])? *(?[0-9]+\.?[0-9*]*(?:E[-+]?[0-9]*)?) *)|(?: *(?[-+])? *(?[0-9]+\.?[0-9]*(?:E[-+]?[0-9]*)?) *' . $quotedCurrencyCode . ' *))$~i'; + + $match = []; + if (preg_match($regExp, $operand, $match, PREG_UNMATCHED_AS_NULL)) { + //Determine the sign + $sign = ($match['PrefixedSign'] ?? $match['PrefixedSign2'] ?? $match['PostfixedSign']) ?? ''; + //Cast to a float + $operand = (float) ($sign . ($match['PostfixedValue'] ?? $match['PrefixedValue'])); + + return true; + } + + return false; + } } diff --git a/tests/PhpSpreadsheetTests/Calculation/CalculationTest.php b/tests/PhpSpreadsheetTests/Calculation/CalculationTest.php index 0b94c19ec..5aaf7a615 100644 --- a/tests/PhpSpreadsheetTests/Calculation/CalculationTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/CalculationTest.php @@ -5,6 +5,7 @@ namespace PhpOffice\PhpSpreadsheetTests\Calculation; use PhpOffice\PhpSpreadsheet\Calculation\Calculation; use PhpOffice\PhpSpreadsheet\Calculation\Functions; use PhpOffice\PhpSpreadsheet\Cell\DataType; +use PhpOffice\PhpSpreadsheet\Shared\StringHelper; use PhpOffice\PhpSpreadsheet\Spreadsheet; use PHPUnit\Framework\TestCase; @@ -216,6 +217,20 @@ class CalculationTest extends TestCase self::assertSame(2.0, $cell2->getCalculatedValue()); } + public function testCellWithStringCurrency(): void + { + $currencyCode = StringHelper::getCurrencyCode(); + + $spreadsheet = new Spreadsheet(); + $workSheet = $spreadsheet->getActiveSheet(); + $cell1 = $workSheet->getCell('A1'); + $cell1->setValue($currencyCode . '2'); + $cell2 = $workSheet->getCell('B1'); + $cell2->setValue('=100*A1'); + + self::assertSame(200.0, $cell2->getCalculatedValue()); + } + public function testBranchPruningFormulaParsingSimpleCase(): void { $calculation = Calculation::getInstance(); diff --git a/tests/PhpSpreadsheetTests/Calculation/Engine/FormattedNumberTest.php b/tests/PhpSpreadsheetTests/Calculation/Engine/FormattedNumberTest.php index 40c16cecb..375db36c8 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Engine/FormattedNumberTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Engine/FormattedNumberTest.php @@ -3,6 +3,7 @@ namespace PhpOffice\PhpSpreadsheetTests\Calculation\Engine; use PhpOffice\PhpSpreadsheet\Calculation\Engine\FormattedNumber; +use PhpOffice\PhpSpreadsheet\Shared\StringHelper; use PHPUnit\Framework\TestCase; class FormattedNumberTest extends TestCase @@ -183,4 +184,44 @@ class FormattedNumberTest extends TestCase 'permutation_88' => [' - % 2.50e - 06 ', ' - % 2.50e - 06 '], ]; } + + /** + * @dataProvider providerCurrencies + */ + public function testCurrencies(string $expected, string $value): void + { + $originalValue = $value; + $result = FormattedNumber::convertToNumberIfCurrency($value); + if ($result === false) { + self::assertSame($expected, $originalValue); + self::assertSame($expected, $value); + } else { + self::assertSame($expected, (string) $value); + self::assertNotEquals($value, $originalValue); + } + } + + public function providerCurrencies(): array + { + $currencyCode = StringHelper::getCurrencyCode(); + return [ + 'basic_prefix_currency' => ['2.75', "{$currencyCode}2.75"], + 'basic_postfix_currency' => ['2.75', "2.75{$currencyCode}"], + + 'basic_prefix_currency_with_spaces' => ['2.75', "{$currencyCode} 2.75"], + 'basic_postfix_currency_with_spaces' => ['2.75', "2.75 {$currencyCode}"], + + 'negative_basic_prefix_currency' => ['-2.75', "-{$currencyCode}2.75"], + 'negative_basic_postfix_currency' => ['-2.75', "-2.75{$currencyCode}"], + + 'negative_basic_prefix_currency_with_spaces' => ['-2.75', "-{$currencyCode} 2.75"], + 'negative_basic_postfix_currency_with_spaces' => ['-2.75', "-2.75 {$currencyCode}"], + + 'basic_prefix_scientific_currency' => ['2000000', "{$currencyCode}2E6"], + 'basic_postfix_scientific_currency' => ['2000000', "2E6{$currencyCode}"], + + 'basic_prefix_scientific_currency_with_spaces' => ['2000000', "{$currencyCode} 2E6"], + 'basic_postfix_scientific_currency_with_spaces' => ['2000000', "2E6 {$currencyCode}"], + ]; + } }