diff --git a/README.md b/README.md
index e8d73d2..0f80ce1 100644
--- a/README.md
+++ b/README.md
@@ -3,12 +3,18 @@ CSV parser/generator
![](https://phpci.gitnet.fr/build-status/image/2?branch=master&label=PHPCensor&style=flat-square)
-A simple PHP library to parse and generate CSV files.
+A simple PHP library to:
+
+* parse a CSV file
+* parse a stream as CSV datas
+* generate CSV files.
+
+PHP >= 7.1 required.
## Composer installation
```
-$ composer require deblan/csv "~2"
+$ composer require deblan/csv "~3"
```
Or in your composer.json:
@@ -16,7 +22,7 @@ Or in your composer.json:
```
{
"require": {
- "deblan/csv": "~2"
+ "deblan/csv": "~3"
}
}
```
@@ -67,7 +73,7 @@ $result = $csv->render('products.csv');
$result = $csv->render('products.csv', FILE_APPEND);
```
-### Parser
+### Parse a file
```php
use Deblan\Csv\CsvParser;
@@ -96,3 +102,19 @@ $csv->parseString($myString);
$headers = $csv->getHeaders();
$products = $csv->getDatas();
```
+
+### Parse a stram
+
+```php
+use Deblan\Csv\CsvStreamParser;
+
+// CsvStreamParser is a CsvParser
+$csv = new CsvStreamParser();
+
+// Parse a stream
+$csv->parseStream(fopen('products.csv', 'r'));
+
+while ($data = $csv->getData()) {
+ // ...
+}
+```
diff --git a/phpunit.xml b/phpunit.xml
index a148726..2fd344a 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -7,7 +7,6 @@
convertWarningsToExceptions = "true"
processIsolation = "false"
stopOnFailure = "false"
- syntaxCheck = "true"
bootstrap = "vendor/autoload.php" >
diff --git a/src/Deblan/Csv/CsvStreamParser.php b/src/Deblan/Csv/CsvStreamParser.php
new file mode 100644
index 0000000..e12b7e4
--- /dev/null
+++ b/src/Deblan/Csv/CsvStreamParser.php
@@ -0,0 +1,69 @@
+
+ */
+class CsvStreamParser extends CsvParser
+{
+ /**
+ * @var resource
+ */
+ protected $resource;
+
+ /**
+ * @var int
+ */
+ protected $length;
+
+ /**
+ * Parse a stream.
+ *
+ * @param resource $resource
+ * @param int $length
+ */
+ public function parseStream($resource, ? int $length = 0): void
+ {
+ if (!is_resource($resource)) {
+ throw new \InvalidArgumentException('First argument must be a valid resource.');
+ }
+
+ $this->resource = $resource;
+ $this->length = $length;
+ }
+
+ /**
+ * Get data of the stream parsing.
+ *
+ * @return null|array
+ */
+ public function getData(): ? array
+ {
+ $csv = fgetcsv($this->resource, $this->length, $this->delimiter, $this->enclosure);
+
+ if ($csv === false) {
+ return null;
+ }
+
+ $isHeaders = $this->hasHeaders && empty($this->headers);
+
+ if ($isHeaders) {
+ $this->headers = $csv;
+
+ return $csv;
+ }
+
+ if (!$isHeaders && $this->hasHeaders && !empty($this->headers)) {
+ foreach ($this->headers as $key => $header) {
+ $csv[$header] = isset($csv[$key]) ? $csv[$key] : null;
+ }
+ }
+
+ $this->datas[] = $csv;
+
+ return $csv;
+ }
+}
diff --git a/tests/CsvParserTest.php b/tests/CsvParserTest.php
index 70c9f83..eab1d3e 100644
--- a/tests/CsvParserTest.php
+++ b/tests/CsvParserTest.php
@@ -1,8 +1,9 @@
assertEquals([], $parser->getDatas());
+ $this->assertEquals([], $parser->getHeaders());
+
+ $parser = new CsvStreamParser();
+ $this->expectException('\InvalidArgumentException');
+ $parser->parseStream(null);
+ }
+
+ public function testStreamParser2()
+ {
+ $parser = new CsvStreamParser();
+ $parser->setHasHeaders(true);
+
+ $parser->parseStream(fopen(__DIR__.'/fixtures/example.csv', 'r'));
+ $this->assertEquals([], $parser->getDatas());
+ $this->assertEquals([], $parser->getHeaders());
+
+ $this->assertEquals([
+ 'FOO',
+ 'BAR',
+ ], $parser->getData());
+ $this->assertEquals([], $parser->getDatas());
+ $this->assertEquals(['FOO', 'BAR'], $parser->getHeaders());
+
+ $this->assertEquals([
+ 'foo 1',
+ 'bar 1',
+ 'FOO' => 'foo 1',
+ 'BAR' => 'bar 1',
+ ], $parser->getData());
+ $this->assertEquals([
+ [
+ 'foo 1',
+ 'bar 1',
+ 'FOO' => 'foo 1',
+ 'BAR' => 'bar 1',
+ ],
+ ], $parser->getDatas());
+ $this->assertEquals(['FOO', 'BAR'], $parser->getHeaders());
+
+ $parser->getData();
+ $this->assertEquals([
+ [
+ 'foo 1',
+ 'bar 1',
+ 'FOO' => 'foo 1',
+ 'BAR' => 'bar 1',
+ ],
+ [
+ 'foo 2',
+ 'bar 2',
+ 'FOO' => 'foo 2',
+ 'BAR' => 'bar 2',
+ ],
+ ], $parser->getDatas());
+ $this->assertEquals(['FOO', 'BAR'], $parser->getHeaders());
+
+ $parser->getData();
+ $this->assertEquals([
+ [
+ 'foo 1',
+ 'bar 1',
+ 'FOO' => 'foo 1',
+ 'BAR' => 'bar 1',
+ ],
+ [
+ 'foo 2',
+ 'bar 2',
+ 'FOO' => 'foo 2',
+ 'BAR' => 'bar 2',
+ ],
+ [
+ 'foo 3',
+ 'bar 3',
+ 'FOO' => 'foo 3',
+ 'BAR' => 'bar 3',
+ ],
+ ], $parser->getDatas());
+ $this->assertEquals(['FOO', 'BAR'], $parser->getHeaders());
+ }
+
+ public function testStreamParser3()
+ {
+ $parser = new CsvStreamParser();
+ $parser->setHasHeaders(false);
+
+ $parser->parseStream(fopen(__DIR__.'/fixtures/example.csv', 'r'));
+ $this->assertEquals([], $parser->getDatas());
+ $this->assertEquals([], $parser->getHeaders());
+
+ $this->assertEquals([
+ 'FOO',
+ 'BAR'
+ ], $parser->getData());
+ $this->assertEquals([
+ [
+ 'FOO',
+ 'BAR'
+ ]
+ ], $parser->getDatas());
+ $this->assertEquals([], $parser->getHeaders());
+
+ $this->assertEquals([
+ 'foo 1',
+ 'bar 1',
+ ], $parser->getData());
+ $this->assertEquals([
+ [
+ 'FOO',
+ 'BAR',
+ ],
+ [
+ 'foo 1',
+ 'bar 1',
+ ],
+ ], $parser->getDatas());
+ $this->assertEquals([], $parser->getHeaders());
+
+ $this->assertEquals([
+ 'foo 2',
+ 'bar 2',
+ ], $parser->getData());
+ $this->assertEquals([
+ [
+ 'FOO',
+ 'BAR',
+ ],
+ [
+ 'foo 1',
+ 'bar 1',
+ ],
+ [
+ 'foo 2',
+ 'bar 2',
+ ],
+ ], $parser->getDatas());
+ $this->assertEquals([], $parser->getHeaders());
+
+ $this->assertEquals([
+ 'foo 3',
+ 'bar 3',
+ ], $parser->getData());
+ $this->assertEquals([
+ [
+ 'FOO',
+ 'BAR',
+ ],
+ [
+ 'foo 1',
+ 'bar 1',
+ ],
+ [
+ 'foo 2',
+ 'bar 2',
+ ],
+ [
+ 'foo 3',
+ 'bar 3',
+ ],
+ ], $parser->getDatas());
+ $this->assertEquals([], $parser->getHeaders());
+
+ $this->assertEquals(null, $parser->getData());
+ }
+}