diff --git a/CHANGELOG.md b/CHANGELOG.md index 8125f69e15..0993b77ebe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,7 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). ### Fixed -- Nothing yet. +- Ignore cell formatting when the format is a single @. [Issue #4242](https://github.com/PHPOffice/PhpSpreadsheet/issues/4242) [PR #4243](https://github.com/PHPOffice/PhpSpreadsheet/pull/4243) ## 2024-11-22 - 3.5.0 diff --git a/src/PhpSpreadsheet/Style/NumberFormat/Formatter.php b/src/PhpSpreadsheet/Style/NumberFormat/Formatter.php index 8ae33354ae..cb15e8f375 100644 --- a/src/PhpSpreadsheet/Style/NumberFormat/Formatter.php +++ b/src/PhpSpreadsheet/Style/NumberFormat/Formatter.php @@ -14,6 +14,7 @@ class Formatter extends BaseFormatter * Matches any @ symbol that isn't enclosed in quotes. */ private const SYMBOL_AT = '/@(?=(?:[^"]*"[^"]*")*[^"]*\Z)/miu'; + private const QUOTE_REPLACEMENT = "\u{fffe}"; // invalid Unicode character /** * Matches any ; symbol that isn't enclosed in quotes, for a "section" split. @@ -125,8 +126,23 @@ public static function toFormattedString($value, string $format, ?array $callBac } // For now we do not treat strings in sections, although section 4 of a format code affects strings // Process a single block format code containing @ for text substitution - if (preg_match(self::SECTION_SPLIT, $format) === 0 && preg_match(self::SYMBOL_AT, $format) === 1) { - return str_replace('"', '', preg_replace(self::SYMBOL_AT, (string) $value, $format) ?? ''); + $formatx = str_replace('\\"', self::QUOTE_REPLACEMENT, $format); + if (preg_match(self::SECTION_SPLIT, $format) === 0 && preg_match(self::SYMBOL_AT, $formatx) === 1) { + if (!str_contains($format, '"')) { + return str_replace('@', $value, $format); + } + //escape any dollar signs on the string, so they are not replaced with an empty value + $value = str_replace( + ['$', '"'], + ['\\$', self::QUOTE_REPLACEMENT], + (string) $value + ); + + return str_replace( + ['"', self::QUOTE_REPLACEMENT], + ['', '"'], + preg_replace(self::SYMBOL_AT, $value, $formatx) ?? $value + ); } // If we have a text value, return it "as is" diff --git a/tests/data/Style/NumberFormat.php b/tests/data/Style/NumberFormat.php index 7296a248cc..58d6151284 100644 --- a/tests/data/Style/NumberFormat.php +++ b/tests/data/Style/NumberFormat.php @@ -1682,4 +1682,49 @@ '#,##0.00;;"---"', ], 'issue 4124' => ['1 HUF', 1, '#,##0_-[$HUF]'], + 'issue 4242 General with dollar sign' => [ + 'General $200 - 200', // expected result + 'General $200 - 200', // cell contents + NumberFormat::FORMAT_GENERAL, // cell style + ], + 'issue 4242 Text with dollar sign' => [ + 'Text $200 - 200', + 'Text $200 - 200', + NumberFormat::FORMAT_TEXT, + ], + 'issue 4242 Text with quotes, format without' => [ + '"Hello" she said and "Hello" I replied', + '"Hello" she said and "Hello" I replied', + NumberFormat::FORMAT_TEXT, + ], + 'issue 4242 Format with quotes, text without' => [ + 'Text: $200 - 200', + '$200 - 200', + '"Text: "' . NumberFormat::FORMAT_TEXT, + ], + 'issue 4242 single quote mark' => [ + '"', + '"', + '@', + ], + 'issue 4242 dollar sign' => [ + '$100', + '$100', + '@', + ], + 'issue 4242 repeat unquoted at signs' => [ + 'xy xy xy', + 'xy', + '@ @ @', + ], + 'issue 4242 quotes in format and text' => [ + 'Text: "Hooray" for me', + '"Hooray" for me', + '"Text: "' . NumberFormat::FORMAT_TEXT, + ], + 'issue 4242 escaped quote in format' => [ + '"Hello"', + 'Hello', + '\\"@\\"', + ], ];