Skip to content

Commit

Permalink
league/csv integration (#4)
Browse files Browse the repository at this point in the history
  • Loading branch information
ajgarlag committed Apr 8, 2016
1 parent efe5007 commit 22f5c80
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 11 deletions.
4 changes: 3 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ sudo: false
matrix:
include:
- php: hhvm
- php: 5.4
env: COMPOSER_FLAGS="--prefer-lowest"
- php: 5.4
- php: 5.5
- php: 5.6
Expand All @@ -14,7 +16,7 @@ matrix:
fast_finish: true

before_script:
- composer update
- composer update --prefer-dist --no-interaction $COMPOSER_FLAGS
script: phpunit --coverage-clover=coverage.clover
after_script:
- wget https://scrutinizer-ci.com/ocular.phar
Expand Down
66 changes: 57 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ foreach ($file as $line) {
}
```

### Stream filtering
### Stream filter

Instead of using the alternative functions or classes, you can use the provided stream filter to fix the enclosure
escape. You must register the stream filter (if not registered yet) and append it to your stream:
Expand All @@ -102,11 +102,8 @@ fputcsv($handler, array('Hello \"World"!'));
rewind($handler);
$row = fgetcsv($handler, 0, ',', '"', '"');
```

**IMPORTANT**: You cannot provide a `$escape_char` to `fputcsv`, and you must set the `$enclosure` and `$escape`
parameters of `fgetcsv` to the same character.

**KNOWN LIMITATION**: The stream filter is not supported in HHVM.
**❮ NOTE ❯**: The `$escape_char` in [fputcsv] MUST be (if allowed) the default one (the backslash `\`). The `$enclosure`
and `$escape` parameters in [fgetcsv] MUST be equals.

#### Custom enclosure character

Expand Down Expand Up @@ -160,7 +157,7 @@ rewind($handler);
$row = fgetcsv($handler, 0, ',', '@', '@');
```

*Beware*: the enclosure character passed via parameters will override the defined via filter name.
**❮ NOTE ❯**: The enclosure character passed via parameters will override the one defined via filter name.


### End of line (EOL)
Expand All @@ -182,13 +179,62 @@ CsvRfcUtils::setDefaultWriteEol(CsvRfcUtils::EOL_WRITE_RFC);

#### Reading CSV
To read the CSV data, this implementation leverages the PHP native capabilities to read files. If you are having any
problem with this, you should enable the `auto_detect_line_endings` configuration option as following the PHP doc
[recomendation](https://secure.php.net/manual/en/filesystem.configuration.php#ini.auto-detect-line-endings).
problem with the EOL detection, you should enable the `auto_detect_line_endings` configuration option as following the
PHP doc [recomendation](https://secure.php.net/manual/en/filesystem.configuration.php#ini.auto-detect-line-endings).
```php
ini_set('ini.auto-detect-line-endings', true);
```


### Integration with `league/csv`

The well known [`league/csv`](http://csv.thephpleague.com/) package provide a great object oriented API to work with CSV
data, but as long as it leverages the default PHP implementation for CSV, is affected by the [#50686] bug.

You can use this component with `league/csv` to produce [RFC 4180] compatible files avoiding this bug.


#### Writer integration

To integrate this component with the `league/csv` writer implementation, you can use the Stream Filter API.

```php
use Ajgl\Csv\Rfc;
use League\Csv\Writer;

CsvRfcWriteStreamFilter::register();
$writer = Writer::createFromPath('/tmp/foobar.csv');
if (!$writer->isActiveStreamFilter()) {
throw new \Exception("The Stream Filter API is not active.");
}
$writer->appendStreamFilter(CsvRfcWriteStreamFilter::FILTERNAME_DEFAULT);
$writer->insertOne(array('"Hello\", World!'));
```
**❮ NOTE ❯**: Do not override the default writer escape character (`\`).

##### Known limitations
* The `league/csv` package does not support the Stream Filter API when the writer instance is created from a
`SplFileObject`.
* The `league/csv` implementation will always leverage the standard `\SplFileObject::fputcsv` to write CSV data, so the
fix to write [RFC 4180] compatible files from `Ajgl\Csv\Rfc\Spl\SplFileObject::fputcsv` will be ignored.


#### Reader Integration

To read back the CSV data, you can leverage the standard implementation, but you MUST set the reader escape and
enclosure characters to the same value.

```php
use League\Csv\Reader;

$reader = Reader::createFromPath('/tmp/foobar.csv');
$reader->setEscape($reader->getEnclosure());
foreach ($reader as $row) {
//...
}
```


License
-------

Expand All @@ -210,6 +256,8 @@ If you find this component useful, please add a ★ in the [GitHub repository pa

[#50686]: https://bugs.php.net/bug.php?id=50686
[RFC 4180]: https://tools.ietf.org/html/rfc4180
[fgetcsv]: http://php.net/manual/en/function.fgetcsv.php
[fputcsv]: http://php.net/manual/en/function.fputcsv.php
[LICENSE]: LICENSE
[Github issue tracker]: https://github.com/ajgarlag/AjglCsvRfc/issues
[Antonio J. García Lagar]: http://aj.garcialagar.es
Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
},
"require-dev": {
"phpunit/phpunit": "^4.8",
"fabpot/php-cs-fixer": "^1.11"
"fabpot/php-cs-fixer": "^1.11",
"league/csv": ">=7.2"
},
"extra": {
"branch-alias": {
Expand Down
29 changes: 29 additions & 0 deletions tests/CsvRfcWriteStreamFilterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
namespace Ajgl\Csv\Rfc\Tests;

use Ajgl\Csv\Rfc\CsvRfcWriteStreamFilter;
use League\Csv\Reader;
use League\Csv\Writer;

/**
* @author Antonio J. García Lagar <[email protected]>
Expand Down Expand Up @@ -65,4 +67,31 @@ public function testFilterWithCustomEnclosureViaFiltername()
$expected = '%%%Hello\%%, World!%'."\n";
$this->assertEquals($expected, $actual);
}

public function testLeagueCsvIntegrationWithPath()
{
if (!class_exists('League\Csv\Writer')) {
$this->markTestSkipped("'league/csv' package not found.");
}

$payload = '"Hello\", World!';

CsvRfcWriteStreamFilter::register();
$filepath = tempnam(sys_get_temp_dir(), 'ajgl_csv_rfc_test_');
$writer = Writer::createFromPath($filepath);
$writer->appendStreamFilter(CsvRfcWriteStreamFilter::FILTERNAME_DEFAULT);
$writer->insertOne(array($payload));
unset($writer);

$fp = fopen($filepath, 'r');
$actual = fgets($fp, 4096);
$expected = '"""Hello\"", World!"'."\n";
$this->assertEquals($expected, $actual);
fclose($fp);

$reader = Reader::createFromPath($filepath);
$reader->setEscape($reader->getEnclosure());
$data = array(array($payload));
$this->assertEquals($data, iterator_to_array($reader));
}
}

0 comments on commit 22f5c80

Please sign in to comment.