Skip to content

Commit

Permalink
Complete Writer enclode feature
Browse files Browse the repository at this point in the history
  • Loading branch information
nyamsprod committed Dec 13, 2024
1 parent 42a8b36 commit 03354a6
Show file tree
Hide file tree
Showing 20 changed files with 139 additions and 46 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ All Notable changes to `Csv` will be documented in this file

- `XMLConverter::formatter`
- `HTMLConverter::formatter`
- `Writer::encloseNone`
- `Writer::encloseNecessary`
- `Writer::noEnclosure`

### Deprecated

Expand All @@ -16,6 +19,7 @@ All Notable changes to `Csv` will be documented in this file
### Fixed

- `JsonConverter::formatter` now accepts callable before only `Closure` where accepted.
- The protected property `Writer::$enclose_all` is no longer a boolean but an integer

### Removed

Expand Down
3 changes: 1 addition & 2 deletions docs/9.0/converter/html.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,12 @@ This method sets the optional attribute name for the field name on the HTML `td`

<p class="message-info">If none is used or an empty string is given, the field name information won't be exported to the HTML table.</p>


### HTMLConverter::formatter

<p class="message-info">New feature introduced in version <code>9.20.0</code></p>

```php
public HTMLConverter::formatter(?callback $formatter): mixed
public HTMLConverter::formatter(?callable $formatter): self
```

This method allow to apply a callback prior to converting your collection individual item.
Expand Down
2 changes: 1 addition & 1 deletion docs/9.0/converter/json.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ $converter->indentSize; //returns the value used
### Json encode formatter

```php
public JsonConverter::formatter(?callback $formatter): mixed
public JsonConverter::formatter(?callable $formatter): self
```

This method allow to apply a callback prior to `json_encode` your collection individual item.
Expand Down
4 changes: 2 additions & 2 deletions docs/9.0/converter/xml.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,11 @@ This method sets the XML field name and optionally the attribute name for the fi
<p class="message-info">New feature introduced in version <code>9.20.0</code></p>

```php
public XMLConverter::formatter(?callback $formatter): mixed
public XMLConverter::formatter(?callable $formatter): self
```

This method allow to apply a callback prior to converting your collection individual item.
This callback allows you to specify how each item will be converted. The formatter should
This callback allows you to specify how each item will be converted. The formatter should
return an associative array suitable for conversion.

## Conversion
Expand Down
15 changes: 12 additions & 3 deletions docs/9.0/writer/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,19 +127,28 @@ By default, `getFlushTreshold` returns `null`.
<p class="message-info"><code>Writer::insertAll</code> always flushes its buffer when all records are inserted, regardless of the threshold value.</p>
<p class="message-info">If set to <code>null</code> the inner flush mechanism of PHP's <code>fputcsv</code> will be used.</p>

## Force Enclosure
## Enclosure

<p class="message-info">Since version <code>9.10.0</code> you can provide control the presence of enclosure around all records.</p>
<p class="message-info">Since version <code>9.20.0</code> generating CSV without enclosure is allowed.</p>

```php
public Writer::forceEnclosure(): self
public Writer::relaxEnclosure(): self
public Writer::noEnclosure(): self
public Writer::encloseAll(): bool
public Writer::encloseNecessary(): bool
public Writer::encloseNone(): bool
```

By default, the `Writer` adds enclosures only around records that requires them. For all other records no enclosure character is present,
With this feature, you can force the enclosure to be present on every record entry or CSV cell. The inclusion will respect
the presence of enclosures inside the cell content as well as the presence of PHP's escape character.
With this feature, you can:

- force the enclosure to be present on every record entry or CSV cell;
- completely remove the presence of enclosure;

<p class="message-warning">When this feature is activated the <code>$escape</code> character is completely ignored (it is the same as setting it to the empty string.)</p>
<p class="message-warning">When no enclosure is used, the library <strong>DO NOT CHECK</strong> for multiline fields.</p>

```php
<?php
Expand Down
4 changes: 2 additions & 2 deletions src/ColumnConsistencyTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ protected function setUp(): void
protected function tearDown(): void
{
$csv = new SplFileObject(__DIR__.'/../test_files/foo.csv', 'w');
$csv->setCsvControl();
$csv->fputcsv(['john', 'doe', '[email protected]'], ',', '"');
$csv->setCsvControl(escape: '\\');
$csv->fputcsv(fields: ['john', 'doe', '[email protected]']);
unset($this->csv);
}

Expand Down
2 changes: 1 addition & 1 deletion src/EscapeFormulaTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public function testFormatterOnWriter(): void
$record = ['2', '2017-07-25', 'Important Client', '=2+5', 240, "\ttab", "\rcr", null];
$expected = "2,2017-07-25,\"Important Client\",'=2+5,240,\"'\ttab\",\"'\rcr\",\n";
$csv = Writer::createFromFileObject(new SplTempFileObject());
$csv->addFormatter(new EscapeFormula());
$csv->addFormatter((new EscapeFormula())->escapeRecord(...));
$csv->insertOne($record);
self::assertStringContainsString($expected, $csv->toString());
}
Expand Down
7 changes: 4 additions & 3 deletions src/HTMLConverter.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

namespace League\Csv;

use Closure;
use Deprecated;
use DOMDocument;
use DOMElement;
Expand All @@ -30,8 +31,8 @@ class HTMLConverter
/** table id attribute value. */
protected string $id_value = '';
protected XMLConverter $xml_converter;
/** @var ?callable(array, array-key): array */
protected mixed $formatter = null;
/** @var ?Closure(array, array-key): array */
protected ?Closure $formatter = null;

public static function create(): self
{
Expand Down Expand Up @@ -174,7 +175,7 @@ public function td(string $fieldname_attribute_name): self
public function formatter(?callable $formatter): self
{
$clone = clone $this;
$clone->formatter = $formatter;
$clone->formatter = ($formatter instanceof Closure || null === $formatter) ? $formatter : $formatter(...);

return $clone;
}
Expand Down
4 changes: 2 additions & 2 deletions src/InfoTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,9 @@ public function testDetectDelimiterWithNoValidDelimiter(): void
public function testDetectDelimiterListWithInconsistentCSV(): void
{
$data = new SplTempFileObject();
$data->setCsvControl(';');
$data->setCsvControl(separator: ';', escape: '\\');
$data->fputcsv(['toto', 'tata', 'tutu']);
$data->setCsvControl('|');
$data->setCsvControl('|', escape: '\\');
$data->fputcsv(['toto', 'tata', 'tutu']);
$data->fputcsv(['toto', 'tata', 'tutu']);
$data->fputcsv(['toto', 'tata', 'tutu']);
Expand Down
6 changes: 3 additions & 3 deletions src/JsonConverter.php
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,8 @@ final class JsonConverter
public readonly int $depth;
/** @var int<1, max> */
public readonly int $indentSize;
/** @var ?callable(T, array-key): mixed */
public readonly mixed $formatter;
/** @var ?Closure(T, array-key): mixed */
public readonly ?Closure $formatter;
/** @var int<1, max> */
public readonly int $chunkSize;
/** @var non-empty-string */
Expand Down Expand Up @@ -151,7 +151,7 @@ private function __construct(int $flags, int $depth, int $indentSize, ?callable
$this->flags = $flags;
$this->depth = $depth;
$this->indentSize = $indentSize;
$this->formatter = $formatter;
$this->formatter = ($formatter instanceof Closure || null === $formatter) ? $formatter : $formatter(...);
$this->chunkSize = $chunkSize;

// Initialize settings and closure to use for conversion.
Expand Down
2 changes: 1 addition & 1 deletion src/Query/Constraint/Column.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public static function filterOn(
}

if (is_callable($operator)) {
return new self($column, Closure::fromCallable($operator), $value);
return new self($column, $operator(...), $value);
}

return new self(
Expand Down
16 changes: 7 additions & 9 deletions src/Query/Ordering/Column.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,15 +68,13 @@ public static function sortOn(
default => throw new QueryException('Unknown or unsupported ordering operator value: '.$direction),
};

if (is_callable($callback)) {
$callback = Closure::fromCallable($callback);
}

return new self(
$operator,
$column,
$callback ?? static fn (mixed $first, mixed $second): int => $first <=> $second
);
$callback = match (true) {
null === $callback => static fn (mixed $first, mixed $second): int => $first <=> $second,
$callback instanceof Closure => $callback,
default => $callback(...),
};

return new self($operator, $column, $callback);
}

/**
Expand Down
9 changes: 6 additions & 3 deletions src/ReaderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ final class ReaderTest extends TabularDataReaderTestCase
protected function setUp(): void
{
$tmp = new SplTempFileObject();
$tmp->setCsvControl(escape: '\\');
foreach ($this->expected as $row) {
$tmp->fputcsv($row);
}
Expand Down Expand Up @@ -156,6 +157,7 @@ public function testCall(): void
];

$file = new SplTempFileObject();
$file->setCsvControl(escape: '\\');
foreach ($raw as $row) {
$file->fputcsv($row);
}
Expand Down Expand Up @@ -213,7 +215,7 @@ public function testStripBOM(array $record, string $expected_bom, string $expect
{
/** @var resource $fp */
$fp = fopen('php://temp', 'r+');
fputcsv($fp, $record);
fputcsv($fp, $record, escape: '');
$csv = Reader::createFromStream($fp);
self::assertSame($expected_bom, $csv->getInputBOM());
foreach ($csv as $row) {
Expand Down Expand Up @@ -274,7 +276,7 @@ public function testDisablingBOMStripping(): void
$expected_record = [Bom::Utf16Le->value.'john', 'doe', '[email protected]'];
/** @var resource $fp */
$fp = fopen('php://temp', 'r+');
fputcsv($fp, $expected_record);
fputcsv($fp, $expected_record, escape: '');
$csv = Reader::createFromStream($fp);
$csv->includeInputBOM();
self::assertSame(Bom::Utf16Le->value, $csv->getInputBOM());
Expand Down Expand Up @@ -333,7 +335,7 @@ public function testGetHeaderThrowsExceptionWithStreamObject(): void
/** @var resource $tmp */
$tmp = fopen('php://temp', 'r+');
foreach ($this->expected as $row) {
fputcsv($tmp, $row);
fputcsv($tmp, $row, escape: '');
}

$csv = Reader::createFromStream($tmp);
Expand Down Expand Up @@ -363,6 +365,7 @@ public function testJsonSerialize(): void
];

$tmp = new SplTempFileObject();
$tmp->setCsvControl(escape: '\\');
foreach ($expected as $row) {
$tmp->fputcsv($row);
}
Expand Down
5 changes: 5 additions & 0 deletions src/ResultSetTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ final class ResultSetTest extends TabularDataReaderTestCase
protected function setUp(): void
{
$tmp = new SplTempFileObject();
$tmp->setCsvControl(escape: '\\');
foreach ($this->expected as $row) {
$tmp->fputcsv($row);
}
Expand Down Expand Up @@ -173,6 +174,7 @@ public function testFetchAssocWithRowIndex(): void
];

$tmp = new SplTempFileObject();
$tmp->setCsvControl(escape: '\\');
foreach ($arr as $row) {
$tmp->fputcsv($row);
}
Expand Down Expand Up @@ -203,6 +205,7 @@ public function testFetchColumnInconsistentColumnCSV(): void
];

$file = new SplTempFileObject();
$file->setCsvControl(escape: '\\');
foreach ($raw as $row) {
$file->fputcsv($row);
}
Expand All @@ -220,6 +223,7 @@ public function testFetchColumnEmptyCol(): void
];

$file = new SplTempFileObject();
$file->setCsvControl(escape: '\\');
foreach ($raw as $row) {
$file->fputcsv($row);
}
Expand Down Expand Up @@ -312,6 +316,7 @@ public function testJsonSerialize(): void
];

$tmp = new SplTempFileObject();
$tmp->setCsvControl(escape: '\\');
foreach ($expected as $row) {
$tmp->fputcsv($row);
}
Expand Down
2 changes: 1 addition & 1 deletion src/Statement.php
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ final protected static function wrapSingleArgumentCallable(callable $where): cal
$reflection = new ReflectionFunction($where instanceof Closure ? $where : $where(...));

return match ($reflection->getNumberOfRequiredParameters()) {
0 => throw new InvalidArgument('The where condition must be a callable with 2 required parameters.'),
0 => throw new InvalidArgument('The where condition must be callable with 2 required parameters.'),
1 => fn (mixed $record, int $key) => $where($record),
default => $where,
};
Expand Down
1 change: 1 addition & 0 deletions src/StatementTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ final class StatementTest extends TestCase
protected function setUp(): void
{
$tmp = new SplTempFileObject();
$tmp->setCsvControl(escape: '\\');
foreach ($this->expected as $row) {
$tmp->fputcsv($row);
}
Expand Down
4 changes: 2 additions & 2 deletions src/StreamTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ public function testCreateStreamFromPathWithContext(): void
];

foreach ($expected as $row) {
fputcsv($fp, $row);
fputcsv($fp, $row, escape: '');
}

$stream = Stream::createFromPath(
Expand Down Expand Up @@ -143,7 +143,7 @@ public function testFSeekThrowsExceptionOnNonSeakableResource(): void
$this->expectException(UnavailableFeature::class);

$stream = Stream::createFromResource(STDOUT);
$stream->fputcsv(['foo', 'bar']);
$stream->fputcsv(['foo', 'bar'], escape: '');
$stream->fseek(-1);
}

Expand Down
Loading

0 comments on commit 03354a6

Please sign in to comment.