From: =?utf-8?b?SsOpcsOpbXkgRGVydXNzw6k=?= <jeremy@derusse.com>
Date: Mon, 15 Nov 2021 11:47:04 +0100
Subject: Use single quote to escape formulas

Origin: upstream, https://github.com/symfony/symfony/commit/3da6f2d45e7536ccb2a26f52fbaf340917e208a8
---
 .../Component/Serializer/Encoder/CsvEncoder.php    |  7 +-
 .../Serializer/Tests/Encoder/CsvEncoderTest.php    | 85 ++++++++++++++++++++--
 2 files changed, 81 insertions(+), 11 deletions(-)

diff --git a/src/Symfony/Component/Serializer/Encoder/CsvEncoder.php b/src/Symfony/Component/Serializer/Encoder/CsvEncoder.php
index f20211b..cd71fec 100644
--- a/src/Symfony/Component/Serializer/Encoder/CsvEncoder.php
+++ b/src/Symfony/Component/Serializer/Encoder/CsvEncoder.php
@@ -35,7 +35,8 @@ class CsvEncoder implements EncoderInterface, DecoderInterface
 
     private const UTF8_BOM = "\xEF\xBB\xBF";
 
-    private $formulasStartCharacters = ['=', '-', '+', '@'];
+    private const FORMULAS_START_CHARACTERS = ['=', '-', '+', '@', "\t", "\r"];
+
     private $defaultContext = [
         self::DELIMITER_KEY => ',',
         self::ENCLOSURE_KEY => '"',
@@ -238,8 +239,8 @@ class CsvEncoder implements EncoderInterface, DecoderInterface
             if (is_iterable($value)) {
                 $this->flatten($value, $result, $keySeparator, $parentKey.$key.$keySeparator, $escapeFormulas);
             } else {
-                if ($escapeFormulas && \in_array(substr((string) $value, 0, 1), $this->formulasStartCharacters, true)) {
-                    $result[$parentKey.$key] = "\t".$value;
+                if ($escapeFormulas && \in_array(substr((string) $value, 0, 1), self::FORMULAS_START_CHARACTERS, true)) {
+                    $result[$parentKey.$key] = "'".$value;
                 } else {
                     // Ensures an actual value is used when dealing with true and false
                     $result[$parentKey.$key] = false === $value ? 0 : (true === $value ? 1 : $value);
diff --git a/src/Symfony/Component/Serializer/Tests/Encoder/CsvEncoderTest.php b/src/Symfony/Component/Serializer/Tests/Encoder/CsvEncoderTest.php
index 33a16ee..596afa2 100644
--- a/src/Symfony/Component/Serializer/Tests/Encoder/CsvEncoderTest.php
+++ b/src/Symfony/Component/Serializer/Tests/Encoder/CsvEncoderTest.php
@@ -285,31 +285,52 @@ CSV;
 
         $this->assertSame(<<<'CSV'
 0
-"	=2+3"
+'=2+3
 
 CSV
             , $this->encoder->encode(['=2+3'], 'csv'));
 
         $this->assertSame(<<<'CSV'
 0
-"	-2+3"
+'-2+3
 
 CSV
             , $this->encoder->encode(['-2+3'], 'csv'));
 
         $this->assertSame(<<<'CSV'
 0
-"	+2+3"
+'+2+3
 
 CSV
             , $this->encoder->encode(['+2+3'], 'csv'));
 
         $this->assertSame(<<<'CSV'
 0
-"	@MyDataColumn"
+'@MyDataColumn
 
 CSV
             , $this->encoder->encode(['@MyDataColumn'], 'csv'));
+
+        $this->assertSame(<<<'CSV'
+0
+"'	tab"
+
+CSV
+            , $this->encoder->encode(["\ttab"], 'csv'));
+
+        $this->assertSame(<<<'CSV'
+0
+"'=1+2"";=1+2"
+
+CSV
+            , $this->encoder->encode(['=1+2";=1+2'], 'csv'));
+
+        $this->assertSame(<<<'CSV'
+0
+"'=1+2'"" ;,=1+2"
+
+CSV
+            , $this->encoder->encode(['=1+2\'" ;,=1+2'], 'csv'));
     }
 
     public function testDoNotEncodeFormulas()
@@ -341,13 +362,34 @@ CSV
 
 CSV
             , $this->encoder->encode(['@MyDataColumn'], 'csv'));
+
+        $this->assertSame(<<<'CSV'
+0
+"	tab"
+
+CSV
+            , $this->encoder->encode(["\ttab"], 'csv'));
+
+        $this->assertSame(<<<'CSV'
+0
+"=1+2"";=1+2"
+
+CSV
+            , $this->encoder->encode(['=1+2";=1+2'], 'csv'));
+
+        $this->assertSame(<<<'CSV'
+0
+"=1+2'"" ;,=1+2"
+
+CSV
+            , $this->encoder->encode(['=1+2\'" ;,=1+2'], 'csv'));
     }
 
     public function testEncodeFormulasWithSettingsPassedInContext()
     {
         $this->assertSame(<<<'CSV'
 0
-"	=2+3"
+'=2+3
 
 CSV
             , $this->encoder->encode(['=2+3'], 'csv', [
@@ -356,7 +398,7 @@ CSV
 
         $this->assertSame(<<<'CSV'
 0
-"	-2+3"
+'-2+3
 
 CSV
             , $this->encoder->encode(['-2+3'], 'csv', [
@@ -365,7 +407,7 @@ CSV
 
         $this->assertSame(<<<'CSV'
 0
-"	+2+3"
+'+2+3
 
 CSV
             , $this->encoder->encode(['+2+3'], 'csv', [
@@ -374,12 +416,39 @@ CSV
 
         $this->assertSame(<<<'CSV'
 0
-"	@MyDataColumn"
+'@MyDataColumn
 
 CSV
             , $this->encoder->encode(['@MyDataColumn'], 'csv', [
                 CsvEncoder::ESCAPE_FORMULAS_KEY => true,
             ]));
+
+        $this->assertSame(<<<'CSV'
+0
+"'	tab"
+
+CSV
+            , $this->encoder->encode(["\ttab"], 'csv', [
+                CsvEncoder::ESCAPE_FORMULAS_KEY => true,
+            ]));
+
+        $this->assertSame(<<<'CSV'
+0
+"'=1+2"";=1+2"
+
+CSV
+            , $this->encoder->encode(['=1+2";=1+2'], 'csv', [
+                CsvEncoder::ESCAPE_FORMULAS_KEY => true,
+            ]));
+
+        $this->assertSame(<<<'CSV'
+0
+"'=1+2'"" ;,=1+2"
+
+CSV
+            , $this->encoder->encode(['=1+2\'" ;,=1+2'], 'csv', [
+                CsvEncoder::ESCAPE_FORMULAS_KEY => true,
+            ]));
     }
 
     public function testEncodeWithoutHeader()
