99
1010class Formatter
1111{
12+ private const SYMBOL_AT = '/@(?=(?:[^"]*"[^"]*")*[^"]*\Z)/miu ' ;
13+ private const SECTION_SPLIT = '/;(?=(?:[^"]*"[^"]*")*[^"]*\Z)/miu ' ;
14+
1215 /**
1316 * @param mixed $value
1417 * @param mixed $val
@@ -112,7 +115,13 @@ public static function toFormattedString($value, $format, $callBack = null)
112115 if (is_bool ($ value )) {
113116 return $ value ? Calculation::getTRUE () : Calculation::getFALSE ();
114117 }
115- // For now we do not treat strings although section 4 of a format code affects strings
118+ // For now we do not treat strings in sections, although section 4 of a format code affects strings
119+ // Process a single block format code containing @ for text substitution
120+ if (preg_match (self ::SECTION_SPLIT , $ format ) === 0 && preg_match (self ::SYMBOL_AT , $ format ) === 1 ) {
121+ return str_replace ('" ' , '' , preg_replace (self ::SYMBOL_AT , (string ) $ value , $ format ) ?? '' );
122+ }
123+
124+ // If we have a text value, return it "as is"
116125 if (!is_numeric ($ value )) {
117126 return (string ) $ value ;
118127 }
@@ -138,7 +147,7 @@ function ($matches) {
138147 $ format = (string ) preg_replace ('/( \\\(((.)(?!((AM\/PM)|(A\/P))))|([^ ])))(?=(?:[^"]|"[^"]*")*$)/ui ' , '"${2}" ' , $ format );
139148
140149 // Get the sections, there can be up to four sections, separated with a semi-colon (but only if not a quoted literal)
141- $ sections = preg_split (' /(;)(?=(?:[^"]|"[^"]*")*$)/u ' , $ format ) ?: [];
150+ $ sections = preg_split (self :: SECTION_SPLIT , $ format ) ?: [];
142151
143152 [$ colors , $ format , $ value ] = self ::splitFormat ($ sections , $ value );
144153
0 commit comments