Skip to content
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

Fix usage of non empty $escape in PHP 8.4 #531

Closed
Closed
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
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,27 @@

All Notable changes to `Csv` will be documented in this file

## [Next] - TBD

### Added

- `League\Csv\Fragment\Expression`
- `League\Csv\Fragment\Selection` (internal class)

### Deprecated

- `League\Csv\FragmentFinder::findAll` use `League\Csv\Fragment\Expression::fragment` instead

### Fixed

- `Cast*` methods accept more input type.
- `FragmentFinder` now removes duplicate selection.
- `TabularaDataReader::matching` will return an empty `Iterable` instance when no selection is valid, previously and empty `TabularDataReader` instance was returned as unique item of the iterable returned.

### Removed

- None

## [9.16.0](https://github.com/thephpleague/csv/compare/9.15.0...9.16.0) - 2024-05-24

### Added
Expand Down
81 changes: 75 additions & 6 deletions docs/9.0/reader/statement.md
Original file line number Diff line number Diff line change
Expand Up @@ -434,16 +434,17 @@ Here are some selection example:
- `cell=5,2-8,9` : will select the cells located between row `4` and column `1` and row `7` and column `8`;

Of note, the RFC allows for multiple selections, separated by a `;`. which are translated
as `OR` expressions. To strictly cover The RFC the class exposes the `findAll` method
as `OR` expressions. To strictly cover The RFC the class exposes the `find` method
which returns an iterable containing the results of all found fragments as distinct `TabulatDataReader`
instances.

<p class="message-warning">If some selections are invalid no error is returned; the invalid
selection is skipped from the returned value.</p>
<p class="message-info">This <code>find</code> method is introduced with version <code>9.17.0</code>.</p>
<p class="message-notice">The <code>findAll</code> method is deprecated, you should use <code>find</code> instead.</p>
<p class="message-warning">If some selections are invalid no error is returned; the invalid selection is skipped from the returned value.</p>

To restrict the returned values you may use the `findFirst` and `findFirstOrFail` methods.
Both methods return on success a `TabularDataReader` instance. While the `first` method
always return the first selection found or `null`; `firstOrFail` **MUST** return a
always return the first selection found if it is not empty or `null`; `firstOrFail` **MUST** return a non-empty
`TabularDataReader` instance or throw. It will also throw if the expression syntax is
invalid while all the other methods just ignore the error.

Expand All @@ -456,15 +457,83 @@ use League\Csv\FragmentFinder;
$reader = Reader::createFromPath('/path/to/file.csv');
$finder = FragmentFinder::create();

$finder->findAll('row=7-5;8-9', $reader); // return an Iterator<TabularDataReader>
$finder->find('row=7-5;8-9', $reader); // return an Iterator<TabularDataReader>
$finder->findFirst('row=7-5;8-9', $reader); // return an TabularDataReader
$finder->findFirstOrFail('row=7-5;8-9', $reader); // will throw
```

- `FragmentFinder::findAll` returns an Iterator containing a single `TabularDataReader` because the first selection
- `FragmentFinder::find` returns an Iterator containing a single `TabularDataReader` because the first selection
is invalid;
- `FragmentFinder::findFirst` returns the single valid `TabularDataReader`
- `FragmentFinder::findFirstOrFail` throws a `SyntaxError`.

Both classes, `FragmentFinder` and `Statement` returns an instance that implements the `TabularDataReader` interface
which returns the found data in a consistent way.

### Fragment Expression builder

<p class="message-info">This mechanism is introduced with version <code>9.17.0</code>.</p>

The `Expression` class provides an immutable, fluent interface to create valid expressions.
We can rewrite the previous example as followed:

```php
use League\Csv\Reader;
use League\Csv\Fragment\Expression;
use League\Csv\FragmentFinder;

$reader = Reader::createFromPath('/path/to/file.csv');
$finder = FragmentFinder::create();
$expression = Expression::fromRow('7-5', '8-9');

$finder->find($expression, $reader); // return an Iterator<TabularDataReader>
$finder->findFirst($expression, $reader); // return an TabularDataReader
$finder->findFirstOrFail($expression, $reader); // will throw
```

The `Expression` validates that your selections are valid according to the selection scheme chosen.
The class exposes method to create fragment expression for:

- cell selection using `Expression::fromCell`;
- row selection using `Expression::fromRow`;
- column selection using `Expression::fromColumn`;

```php
use League\Csv\Fragment\Expression;

$expression = Expression::fromRow('7-5', '8-9');
echo $expression;
// returns 'row=8-9' and removes `7-5` because it is an invalid selection
```

You can even gradually create your expression using a fluent and immutable API
using the `push`, `unshift` and `remove` methods. And there are convenient method to
inspect the class to know how nmany selections are present and to select them according
to their indices using the `get` a `has` methods. You are also able to tell if a specific
selection in present via the `contains` method.

```php
use League\Csv\Fragment\Expression;

$expression = Expression::fromRow()
->push('5-8')
->unshift('12-15')
->replace('5-8', '12-*')
->remove('12-15');

echo $expression->toString();
// or
echo $expression;
// returns 'row=12-*'
```

You can use que `Expression` to directly query a `TabularDataReader` using the `fragment` method.
The result will be an iterable structure containing `TabularDataReader` instances whose index present the
selection used to generate the data. The `firstFragment` method can be used to return the first
`TabularDataReader` instance regardless if it contains data or not.

```php
$document = Reader::createFromPath('/path/to/file.csv');
Expression::fromRow('7-5', '8-9')->fragment($document); // returns an iterable<string, TabularDataReader>
Expression::fromRow('7-5', '8-9')->firstFragment($document); // returns the first selection found regardless if it contains data or not
```
1 change: 1 addition & 0 deletions docs/9.0/reader/tabular-data-reader.md
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,7 @@ $reader->matchingFirstOrFail('row=3-1;4-6'); // will throw

<p class="message-info"> Wraps the functionality of <code>FragmentFinder</code> class.</p>
<p class="message-notice">Added in version <code>9.12.0</code> for <code>Reader</code> and <code>ResultSet</code>.</p>
<p class="message-info">In addition to using a string expression, starting with version <code>9.17.0</code> you can alternatively use an `Expression` object.</p>

### chunkBy

Expand Down
4 changes: 2 additions & 2 deletions src/AbstractCsv.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ abstract class AbstractCsv implements ByteSequence
protected ?Bom $input_bom = null;
protected ?Bom $output_bom = null;
protected string $delimiter = ',';
protected string $enclosure = '"';
protected string $escape = '\\';
protected string $enclosure = '\\';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this change is not required and not needed.

protected string $escape = '';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a BC break.

protected bool $is_input_bom_included = false;

/**
Expand Down
Loading
Loading