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()); + } +}