Skip to content

[Serializer] Remove deprecated escape_char functionality from CsvEncoder #60924

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions UPGRADE-8.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,26 @@ Security
Serializer
----------

* Remove escape character functionality from `CsvEncoder`

```diff
use Symfony\Component\Serializer\Encoder\CsvEncoder;

// Using escape character in encoding
$encoder = new CsvEncoder();
-$csv = $encoder->encode($data, 'csv', [
- CsvEncoder::ESCAPE_CHAR_KEY => '\\',
-]);
+$csv = $encoder->encode($data, 'csv');

// Using escape character with context builder
use Symfony\Component\Serializer\Context\Encoder\CsvEncoderContextBuilder;

$context = (new CsvEncoderContextBuilder())
- ->withEscapeChar('\\')
->toArray();
```

* Remove `AbstractNormalizerContextBuilder::withDefaultContructorArguments()`, use `withDefaultConstructorArguments()` instead
* Change signature of `NameConverterInterface::normalize()` and `NameConverterInterface::denormalize()` methods:

Expand Down
2 changes: 2 additions & 0 deletions src/Symfony/Component/Serializer/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ CHANGELOG
8.0
---

* Remove `CsvEncoder::ESCAPE_CHAR_KEY` constant and escape character functionality
* Remove `CsvEncoderContextBuilder::withEscapeChar()` method
* Remove `AbstractNormalizerContextBuilder::withDefaultContructorArguments()`, use `withDefaultConstructorArguments()` instead
* Change signature of `NameConverterInterface::normalize()` and `NameConverterInterface::denormalize()` methods:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,25 +57,6 @@ public function withEnclosure(?string $enclosure): static
return $this->with(CsvEncoder::ENCLOSURE_KEY, $enclosure);
}

/**
* Configures the escape character.
*
* Must be empty or a single character.
*
* @deprecated since Symfony 7.2, to be removed in 8.0
*
* @throws InvalidArgumentException
*/
public function withEscapeChar(?string $escapeChar): static
{
trigger_deprecation('symfony/serializer', '7.2', 'The "%s" method is deprecated. It will be removed in 8.0.', __METHOD__);

if (null !== $escapeChar && \strlen($escapeChar) > 1) {
throw new InvalidArgumentException(\sprintf('The "%s" escape character must be empty or a single character.', $escapeChar));
}

return $this->with(CsvEncoder::ESCAPE_CHAR_KEY, $escapeChar);
}

/**
* Configures the key separator when (un)flattening arrays.
Expand Down
22 changes: 6 additions & 16 deletions src/Symfony/Component/Serializer/Encoder/CsvEncoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,6 @@ class CsvEncoder implements EncoderInterface, DecoderInterface
public const FORMAT = 'csv';
public const DELIMITER_KEY = 'csv_delimiter';
public const ENCLOSURE_KEY = 'csv_enclosure';
/**
* @deprecated since Symfony 7.2, to be removed in 8.0
*/
public const ESCAPE_CHAR_KEY = 'csv_escape_char';
public const KEY_SEPARATOR_KEY = 'csv_key_separator';
public const HEADERS_KEY = 'csv_headers';
public const ESCAPE_FORMULAS_KEY = 'csv_escape_formulas';
Expand All @@ -44,7 +40,6 @@ class CsvEncoder implements EncoderInterface, DecoderInterface
private array $defaultContext = [
self::DELIMITER_KEY => ',',
self::ENCLOSURE_KEY => '"',
self::ESCAPE_CHAR_KEY => '',
self::END_OF_LINE => "\n",
self::ESCAPE_FORMULAS_KEY => false,
self::HEADERS_KEY => [],
Expand All @@ -56,10 +51,6 @@ class CsvEncoder implements EncoderInterface, DecoderInterface

public function __construct(array $defaultContext = [])
{
if (\array_key_exists(self::ESCAPE_CHAR_KEY, $defaultContext)) {
trigger_deprecation('symfony/serializer', '7.2', 'Setting the "csv_escape_char" option is deprecated. The option will be removed in 8.0.');
}

$this->defaultContext = array_merge($this->defaultContext, $defaultContext);
}

Expand Down Expand Up @@ -88,7 +79,7 @@ public function encode(mixed $data, string $format, array $context = []): string
}
}

[$delimiter, $enclosure, $escapeChar, $keySeparator, $headers, $escapeFormulas, $outputBom] = $this->getCsvOptions($context);
[$delimiter, $enclosure, $keySeparator, $headers, $escapeFormulas, $outputBom] = $this->getCsvOptions($context);

foreach ($data as &$value) {
$flattened = [];
Expand All @@ -101,15 +92,15 @@ public function encode(mixed $data, string $format, array $context = []): string
$endOfLine = $context[self::END_OF_LINE] ?? $this->defaultContext[self::END_OF_LINE];

if (!($context[self::NO_HEADERS_KEY] ?? $this->defaultContext[self::NO_HEADERS_KEY])) {
fputcsv($handle, $headers, $delimiter, $enclosure, $escapeChar);
fputcsv($handle, $headers, $delimiter, $enclosure, '');
if ("\n" !== $endOfLine && 0 === fseek($handle, -1, \SEEK_CUR)) {
fwrite($handle, $endOfLine);
}
}

$headers = array_fill_keys($headers, '');
foreach ($data as $row) {
fputcsv($handle, array_replace($headers, $row), $delimiter, $enclosure, $escapeChar);
fputcsv($handle, array_replace($headers, $row), $delimiter, $enclosure, '');
if ("\n" !== $endOfLine && 0 === fseek($handle, -1, \SEEK_CUR)) {
fwrite($handle, $endOfLine);
}
Expand Down Expand Up @@ -150,9 +141,9 @@ public function decode(string $data, string $format, array $context = []): mixed
$headerCount = [];
$result = [];

[$delimiter, $enclosure, $escapeChar, $keySeparator, , , , $asCollection] = $this->getCsvOptions($context);
[$delimiter, $enclosure, $keySeparator, , , , $asCollection] = $this->getCsvOptions($context);

while (false !== ($cols = fgetcsv($handle, 0, $delimiter, $enclosure, $escapeChar))) {
while (false !== ($cols = fgetcsv($handle, 0, $delimiter, $enclosure, ''))) {
$nbCols = \count($cols);

if (null === $headers) {
Expand Down Expand Up @@ -244,7 +235,6 @@ private function getCsvOptions(array $context): array
{
$delimiter = $context[self::DELIMITER_KEY] ?? $this->defaultContext[self::DELIMITER_KEY];
$enclosure = $context[self::ENCLOSURE_KEY] ?? $this->defaultContext[self::ENCLOSURE_KEY];
$escapeChar = $context[self::ESCAPE_CHAR_KEY] ?? $this->defaultContext[self::ESCAPE_CHAR_KEY];
$keySeparator = $context[self::KEY_SEPARATOR_KEY] ?? $this->defaultContext[self::KEY_SEPARATOR_KEY];
$headers = $context[self::HEADERS_KEY] ?? $this->defaultContext[self::HEADERS_KEY];
$escapeFormulas = $context[self::ESCAPE_FORMULAS_KEY] ?? $this->defaultContext[self::ESCAPE_FORMULAS_KEY];
Expand All @@ -255,7 +245,7 @@ private function getCsvOptions(array $context): array
throw new InvalidArgumentException(\sprintf('The "%s" context variable must be an array or null, given "%s".', self::HEADERS_KEY, get_debug_type($headers)));
}

return [$delimiter, $enclosure, $escapeChar, $keySeparator, $headers, $escapeFormulas, $outputBom, $asCollection];
return [$delimiter, $enclosure, $keySeparator, $headers, $escapeFormulas, $outputBom, $asCollection];
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,25 +122,4 @@ public function testCannotSetMultipleBytesAsEnclosure()
$this->contextBuilder->withEnclosure('ọ');
}

/**
* @group legacy
*/
public function testCannotSetMultipleBytesAsEscapeChar()
{
$this->expectUserDeprecationMessage('Since symfony/serializer 7.2: The "Symfony\Component\Serializer\Context\Encoder\CsvEncoderContextBuilder::withEscapeChar" method is deprecated. It will be removed in 8.0.');

$this->expectException(InvalidArgumentException::class);
$this->contextBuilder->withEscapeChar('ọ');
}

/**
* @group legacy
*/
public function testWithEscapeCharIsDeprecated()
{
$this->expectUserDeprecationMessage('Since symfony/serializer 7.2: The "Symfony\Component\Serializer\Context\Encoder\CsvEncoderContextBuilder::withEscapeChar" method is deprecated. It will be removed in 8.0.');
$context = $this->contextBuilder->withEscapeChar('\\');

$this->assertSame(['csv_escape_char' => '\\'], $context->toArray());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -732,25 +732,4 @@ public static function provideIterable()
yield 'generator' => [(fn (): \Generator => yield from $data)()];
}

/**
* @group legacy
*/
public function testPassingNonEmptyEscapeCharIsDeprecated()
{
$this->expectUserDeprecationMessage('Since symfony/serializer 7.2: Setting the "csv_escape_char" option is deprecated. The option will be removed in 8.0.');
$encoder = new CsvEncoder(['csv_escape_char' => '@']);

$this->assertSame(
[[
'A, B@"' => 'D',
'C' => 'E',
]],
$encoder->decode(<<<'CSV'
"A, B@"", "C"
"D", "E"
CSV,
'csv'
)
);
}
}
1 change: 0 additions & 1 deletion src/Symfony/Component/Serializer/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
],
"require": {
"php": ">=8.4",
"symfony/deprecation-contracts": "^2.5|^3",
"symfony/polyfill-ctype": "~1.8"
},
"require-dev": {
Expand Down
Loading