@@ -27,18 +27,22 @@ class CsvEncoder implements EncoderInterface, DecoderInterface
2727 const ESCAPE_CHAR_KEY = 'csv_escape_char ' ;
2828 const KEY_SEPARATOR_KEY = 'csv_key_separator ' ;
2929 const HEADERS_KEY = 'csv_headers ' ;
30+ const ESCAPE_FORMULAS_KEY = 'csv_escape_formulas ' ;
3031
3132 private $ delimiter ;
3233 private $ enclosure ;
3334 private $ escapeChar ;
3435 private $ keySeparator ;
36+ private $ escapeFormulas ;
37+ private $ formulasStartCharacters = array ('= ' , '- ' , '+ ' , '@ ' );
3538
36- public function __construct (string $ delimiter = ', ' , string $ enclosure = '" ' , string $ escapeChar = '\\' , string $ keySeparator = '. ' )
39+ public function __construct (string $ delimiter = ', ' , string $ enclosure = '" ' , string $ escapeChar = '\\' , string $ keySeparator = '. ' , $ escapeFormulas = false )
3740 {
3841 $ this ->delimiter = $ delimiter ;
3942 $ this ->enclosure = $ enclosure ;
4043 $ this ->escapeChar = $ escapeChar ;
4144 $ this ->keySeparator = $ keySeparator ;
45+ $ this ->escapeFormulas = $ escapeFormulas ;
4246 }
4347
4448 /**
@@ -65,11 +69,11 @@ public function encode($data, $format, array $context = array())
6569 }
6670 }
6771
68- list ($ delimiter , $ enclosure , $ escapeChar , $ keySeparator , $ headers ) = $ this ->getCsvOptions ($ context );
72+ list ($ delimiter , $ enclosure , $ escapeChar , $ keySeparator , $ headers, $ escapeFormulas ) = $ this ->getCsvOptions ($ context );
6973
7074 foreach ($ data as &$ value ) {
7175 $ flattened = array ();
72- $ this ->flatten ($ value , $ flattened , $ keySeparator );
76+ $ this ->flatten ($ value , $ flattened , $ keySeparator, '' , $ escapeFormulas );
7377 $ value = $ flattened ;
7478 }
7579 unset($ value );
@@ -172,13 +176,17 @@ public function supportsDecoding($format)
172176 /**
173177 * Flattens an array and generates keys including the path.
174178 */
175- private function flatten (array $ array , array &$ result , string $ keySeparator , string $ parentKey = '' )
179+ private function flatten (array $ array , array &$ result , string $ keySeparator , string $ parentKey = '' , $ escapeFormulas = false )
176180 {
177181 foreach ($ array as $ key => $ value ) {
178182 if (is_array ($ value )) {
179- $ this ->flatten ($ value , $ result , $ keySeparator , $ parentKey .$ key .$ keySeparator );
183+ $ this ->flatten ($ value , $ result , $ keySeparator , $ parentKey .$ key .$ keySeparator, $ escapeFormulas );
180184 } else {
181- $ result [$ parentKey .$ key ] = $ value ;
185+ if ($ escapeFormulas && \in_array (substr ($ value , 0 , 1 ), $ this ->formulasStartCharacters , true )) {
186+ $ result [$ parentKey .$ key ] = "\t" .$ value ;
187+ } else {
188+ $ result [$ parentKey .$ key ] = $ value ;
189+ }
182190 }
183191 }
184192 }
@@ -190,12 +198,13 @@ private function getCsvOptions(array $context)
190198 $ escapeChar = isset ($ context [self ::ESCAPE_CHAR_KEY ]) ? $ context [self ::ESCAPE_CHAR_KEY ] : $ this ->escapeChar ;
191199 $ keySeparator = isset ($ context [self ::KEY_SEPARATOR_KEY ]) ? $ context [self ::KEY_SEPARATOR_KEY ] : $ this ->keySeparator ;
192200 $ headers = isset ($ context [self ::HEADERS_KEY ]) ? $ context [self ::HEADERS_KEY ] : array ();
201+ $ escapeFormulas = isset ($ context [self ::ESCAPE_FORMULAS_KEY ]) ? $ context [self ::ESCAPE_FORMULAS_KEY ] : $ this ->escapeFormulas ;
193202
194203 if (!is_array ($ headers )) {
195204 throw new InvalidArgumentException (sprintf ('The "%s" context variable must be an array or null, given "%s". ' , self ::HEADERS_KEY , gettype ($ headers )));
196205 }
197206
198- return array ($ delimiter , $ enclosure , $ escapeChar , $ keySeparator , $ headers );
207+ return array ($ delimiter , $ enclosure , $ escapeChar , $ keySeparator , $ headers, $ escapeFormulas );
199208 }
200209
201210 /**
0 commit comments