From edc003ad164521d10d96ac9f2f670b543511b806 Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Sat, 4 Feb 2023 04:17:16 +0100 Subject: [PATCH] Improved handling of @ format mask Note that it still doesn't work fully if in section 4 --- .../Style/NumberFormat/Formatter.php | 13 ++++++++++-- tests/data/Style/NumberFormat.php | 21 +++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/PhpSpreadsheet/Style/NumberFormat/Formatter.php b/src/PhpSpreadsheet/Style/NumberFormat/Formatter.php index 03b002e533..347dfa6440 100644 --- a/src/PhpSpreadsheet/Style/NumberFormat/Formatter.php +++ b/src/PhpSpreadsheet/Style/NumberFormat/Formatter.php @@ -9,6 +9,9 @@ class Formatter { + private const SYMBOL_AT = '/@(?=(?:[^"]*"[^"]*")*[^"]*\Z)/miu'; + private const SECTION_SPLIT = '/;(?=(?:[^"]*"[^"]*")*[^"]*\Z)/miu'; + /** * @param mixed $value * @param mixed $val @@ -112,7 +115,13 @@ public static function toFormattedString($value, $format, $callBack = null) if (is_bool($value)) { return $value ? Calculation::getTRUE() : Calculation::getFALSE(); } - // For now we do not treat strings although section 4 of a format code affects strings + // 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) ?? ''); + } + + // If we have a text value, return it "as is" if (!is_numeric($value)) { return (string) $value; } @@ -138,7 +147,7 @@ function ($matches) { $format = (string) preg_replace('/(\\\(((.)(?!((AM\/PM)|(A\/P))))|([^ ])))(?=(?:[^"]|"[^"]*")*$)/ui', '"${2}"', $format); // Get the sections, there can be up to four sections, separated with a semi-colon (but only if not a quoted literal) - $sections = preg_split('/(;)(?=(?:[^"]|"[^"]*")*$)/u', $format) ?: []; + $sections = preg_split(self::SECTION_SPLIT, $format) ?: []; [$colors, $format, $value] = self::splitFormat($sections, $value); diff --git a/tests/data/Style/NumberFormat.php b/tests/data/Style/NumberFormat.php index 4bd6b48118..9b43db345a 100644 --- a/tests/data/Style/NumberFormat.php +++ b/tests/data/Style/NumberFormat.php @@ -373,6 +373,27 @@ 'test', '_-€* #,##0.00_-;"-€"* #,##0.00_-;_-€* -??_-;_-@_-', ], + // String masks (ie. @) + [ + 'World', + 'World', + '@', + ], + [ + 'Hello World', + 'World', + 'Hello @', + ], + [ + 'Hello World', + 'World', + '"Hello "@', + ], + [ + 'Meet me @ The Boathouse @ 16:30', + 'The Boathouse', + '"Meet me @ "@" @ 16:30"', + ], // Named colours // Simple color [