Compare commits

...

21 commits
v1 ... master

Author SHA1 Message Date
Simon Vieille e1cb3d8b9a
update ci
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/manual/woodpecker Pipeline was successful
2022-07-26 10:11:21 +02:00
Simon Vieille c2e570f966
update ci
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2022-07-26 10:06:58 +02:00
Simon Vieille 0c34dce1cf
update ci
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2022-07-26 10:06:18 +02:00
Simon Vieille 9cfbebabd6
update ci
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2022-07-26 10:05:14 +02:00
Simon Vieille ec870e287c
add jenkins status
All checks were successful
Gitnet/csv/pipeline/head This commit looks good
2020-09-01 15:41:49 +02:00
Simon Vieille cfb04db402
add jenkins tests
All checks were successful
Gitnet/csv/pipeline/head This commit looks good
2020-09-01 15:40:00 +02:00
Simon Vieille 7719bd7202
typo 2020-05-24 14:31:08 +02:00
Simon Vieille 0f413d06b9
tests for php7 2020-02-10 16:24:56 +01:00
Simon Vieille 9899e5bfbd
add stream parser 2020-02-10 16:24:43 +01:00
Simon Vieille 33e15895d3
CI 2018-06-06 18:59:00 +02:00
Simon Vieille 1ac6c03c1c
CI 2018-06-06 15:51:31 +02:00
Simon Vieille f8731f6fbe CI 2017-03-15 01:01:06 +01:00
Simon Vieille 237290aba9 CI 2017-03-15 00:42:56 +01:00
Simon Vieille 496221566a CI 2017-03-15 00:35:23 +01:00
Simon Vieille 4809f0157b Insight analyse fixes 2017-03-13 13:37:18 +01:00
Simon Vieille 37be0f572f Cleaning code 2017-03-12 18:58:44 +01:00
Simon Vieille 022c31a703 Init of the v2 (CsvParser) 2017-03-12 17:57:30 +01:00
Simon Vieille 6c4a535ecd Tests 2017-03-12 15:25:36 +01:00
Simon Vieille 2c25ffd751 Tests 2017-03-12 15:23:41 +01:00
Simon Vieille ae1176ce78 Documentation 2017-03-12 15:20:09 +01:00
Simon Vieille db680faa97 Init of the v2 2017-03-12 15:04:19 +01:00
19 changed files with 1146 additions and 385 deletions

3
.gitignore vendored
View file

@ -1,4 +1 @@
*.swp
tags
/vendor/

17
.woodpecker.yml Normal file
View file

@ -0,0 +1,17 @@
matrix:
PHP_VERSION:
- 7.3
- 7.4
- 8.0
- 8.1
pipeline:
dependencies:
image: gitnet.fr/deblan/php:${PHP_VERSION}
commands:
- php /usr/local/bin/composer install
- php /usr/local/bin/composer require --dev phpunit/phpunit
tests:
image: gitnet.fr/deblan/php:${PHP_VERSION}
commands:
- php ./vendor/bin/phpunit

View file

@ -20,4 +20,5 @@ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

107
README.md
View file

@ -1,12 +1,20 @@
CSV parser/generator
====================
A simple PHP library to parse and generate CSV files.
[![Build Status](https://ci.gitnet.fr/api/badges/deblan/csv/status.svg)](https://ci.gitnet.fr/deblan/csv)
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
$ composer require deblan/csv "~3"
```
Or in your composer.json:
@ -14,8 +22,7 @@ Or in your composer.json:
```
{
"require": {
[...]
"deblan/csv": "dev-master"
"deblan/csv": "~3"
}
}
```
@ -29,33 +36,85 @@ use Deblan\Csv\Csv;
$csv = new Csv();
$csv->addLine(array('Foo', '$1000'));
$csv->addLine(array('Bar', '$600'));
// Defines the delimiter (default is ;)
$csv->setDelimiter(";");
$result = $csv->compile();
// Defines the enclosure (default is ")
$csv->setEnclosure('"');
// Defines the end of line (default is \n)
$csv->setEndOfLine("\n");
// Defines the charset (default is UTF-8)
$csv->setCharset("UTF-8");
// Add a new line at the end
$csv->addData(['Foo', '$1000'));
// Add a new line at the end
$csv->appendData(['Bar', '$600']);
// Add a new line at the beginning
$csv->prependData(['Boo', '$3000']);
// Defines all the datas
$csv->setDatas([[...], [...]]);
// Defines the header
$csv->setHeaders(['Product', 'Price']);
// Rendering
$result = $csv->render();
// Rendering to a file
$result = $csv->render('products.csv');
// Appending to a file
$result = $csv->render('products.csv', FILE_APPEND);
```
```php
use Deblan\Csv\Csv;
$csv = new Csv();
$csv->setLegend(array('product name', 'price'));
$csv->addLine(array('Foo', '$1000'));
$csv->addLine(array('Bar', '$600'));
$csv->compileToFile('products.csv');
```
### Parser
### Parse a file
```php
use Deblan\Csv\CsvParser;
$csv = new CsvParser('products.csv');
$csv->setHasLegend(true);
$csv->parse();
$csv = new CsvParser();
$legend = $csv->getLegend();
// Defines the delimiter (default is ;)
$csv->setDelimiter(";");
// Defines the enclosure (default is ")
$csv->setEnclosure('"');
// Defines the end of line (default is \n)
$csv->setEndOfLine("\n");
// Headers?
$csv->setHasHeaders(true);
// Parse a file
$csv->parseFile('products.csv');
// Parse a string
$csv->parseString($myString);
// Headers and datas
$headers = $csv->getHeaders();
$products = $csv->getDatas();
```
### Parse a stream
```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()) {
// ...
}
```

View file

@ -7,12 +7,11 @@
convertWarningsToExceptions = "true"
processIsolation = "false"
stopOnFailure = "false"
syntaxCheck = "false"
bootstrap = "vendor/autoload.php" >
<testsuites>
<testsuite name="Deblan CSV Test Suite">
<directory>tests/</directory>
</testsuite>
</testsuites>
<testsuites>
<testsuite name="Deblan CSV Test Suite">
<directory>tests/</directory>
</testsuite>
</testsuites>
</phpunit>

View file

@ -2,192 +2,340 @@
namespace Deblan\Csv;
use Deblan\Csv\Exception\CsvInvalidParameterException;
/**
* class Csv.
*
* @author Simon Vieille <simon@deblan.fr>
*/
class Csv
{
private $delimiter;
/**
* @var string
*/
protected $delimiter = ';';
private $enclosure;
/**
* @var string
*/
protected $enclosure = '"';
private $endline;
/**
* @var string
*/
protected $endOfLine = "\n";
private $datas;
/**
* @var array
*/
protected $datas = [];
private $legend;
/**
* @var array
*/
protected $headers = [];
private $render;
/**
* @var string
*/
protected $charset = 'UTF-8';
private $encoding;
/**
* @var bool
*/
protected $isModified = false;
private $hasLegend = false;
/**
* @var string
*/
protected $render;
public function __construct($delimiter = ';', $enclosure = '"', $endline = "\n", $encoding = 'UTF-8')
/**
* Set the value of "delimiter".
*
* @param string $delimiter
*
* @return Csv
*/
public function setDelimiter($delimiter)
{
$this->setDelimiter($delimiter);
$this->setEnclosure($enclosure);
$this->setEndLine($endline);
$this->setEncoding($encoding);
$this->datas = array(0 => null);
$this->legend = array();
$this->render = "";
}
public function setFilename($v)
{
if (!is_string($v)) {
throw new CsvInvalidParameterException(sprintf('"%s" is not a valid string.', $v));
if ($this->delimiter !== $delimiter) {
$this->delimiter = $delimiter;
$this->isModified = true;
}
$this->filename = $v;
}
protected function setHasLegend($hasLegend)
{
$this->hasLegend = $hasLegend;
return $this;
}
public function getHasLegend()
/**
* Get the value of "delimiter".
*
* @return array
*/
public function getDelimiter()
{
return $this->hasLegend;
return $this->delimiter;
}
public function hasLegend()
/**
* Set the value of "enclosure".
*
* @param string $enclosure
*
* @return Csv
*/
public function setEnclosure($enclosure)
{
return $this->hasLegend;
}
$enclosure = (string) $enclosure;
public function setLegend(array $values)
{
$this->setHasLegend(true);
$this->legend = $values;
$this->addLine($values, 0);
}
public function addLine(array $values, $key = null)
{
if ($key !== null) {
$this->datas[$key] = $values;
return true;
if ($this->enclosure !== $enclosure) {
$this->enclosure = $enclosure;
$this->isModified = true;
}
$this->datas[] = $values;
}
public function setEncoding($encoding)
{
$this->encoding = $encoding;
return $this;
}
public function getEncoding()
/**
* Get the value of "enclosure".
*
* @return string
*/
public function getEnclosure()
{
return $this->encoding;
return $this->enclosure;
}
public function setDelimiter($v)
/**
* Set the value of "endOfLine".
*
* @param string $endOfLine
*
* @return Csv
*/
public function setEndOfLine($endOfLine)
{
if (!is_string($v)) {
throw new CsvInvalidParameterException(sprintf('"%s" is not a valid string.', $v));
if ($this->endOfLine !== $endOfLine) {
$this->endOfLine = (string) $endOfLine;
$this->isModified = true;
}
$this->delimiter = $v;
return $this;
}
public function setEndline($v)
/**
* Get the value of "endOfLine".
*
* @return string
*/
public function getEndOfLine()
{
if (!is_string($v)) {
throw new CsvInvalidParameterException(sprintf('"%s" is not a valid string.', $v));
return $this->endOfLine;
}
/**
* Set the value of "headers".
*
* @param array $headers
*
* @return Csv
*/
public function setHeaders(array $headers)
{
$this->headers = $headers;
if ($this->headers !== $headers) {
$this->headers = $headers;
$this->isModified = true;
}
$this->endline = $v;
return $this;
}
public function setEnclosure($v)
/**
* Get the value of "headers".
*
* @return array
*/
public function getHeaders()
{
if (!is_string($v)) {
throw new CsvInvalidParameterException(sprintf('"%s" is not a valid string.', $v));
return $this->headers;
}
/**
* Set the value of "charset".
*
* @param string $charset
*
* @return Csv
*/
public function setCharset($charset)
{
if ($this->charset !== $charset) {
$this->charset = (string) $charset;
$this->isModified = true;
}
$this->enclose = $v;
return $this;
}
public function getLegend()
/**
* Get the value of "charset".
*
* @return string
*/
public function getCharset()
{
return $this->legend;
return $this->charset;
}
/*
* Sets the value of "datas".
*
* @param array $datas
*
* @return Csv
*/
public function setDatas(array $datas)
{
if ($this->datas !== $datas) {
$this->datas = $datas;
$this->isModified = true;
}
return $this;
}
/**
* Get the value of "datas".
*
* @return array
*/
public function getDatas()
{
return $this->datas;
}
public function compile()
/*
* Appends data.
*
* @param array $data
*
* @return Csv
*/
public function appendData(array $data)
{
$this->render = "";
$this->datas[] = $data;
$this->isModified = true;
if ($this->datas[0] !== null) {
$this->append($this->datasToCsvLine($this->datas[0]));
return $this;
}
/*
* Alias of "appendData()".
*
* {@inheritdoc self::appendData()}
*/
public function addData(array $data)
{
return $this->appendData($data);
}
/*
* Prepends data.
*
* @param array $data
*
* @return Csv
*/
public function preprendData(array $data)
{
array_unshift($this->datas, $data);
$this->isModified = true;
return $this;
}
/**
* Formats an array data to a CSV string.
*
* @param array $data
*
* @return array
*/
protected function formatData(array $data)
{
$columns = [];
foreach ($data as $value) {
$value = (string) $value;
if (!empty($this->enclosure)) {
$value = str_replace($this->enclosure, str_repeat($this->enclosure, 2), $value);
}
$value = sprintf('%1$s%2$s%1$s', $this->enclosure, (string) $value);
$columns[] = $value;
}
unset($this->datas[0]);
$data = implode($this->delimiter, $columns);
$data = $this->encode($data);
foreach ($this->datas as $v) {
$this->append($this->datasToCsvLine($v));
return $data;
}
/*
* Changes the charset if needed.
*
* @param string $value
*
* @return string
*/
public function encode($value)
{
return mb_convert_encoding(
$value,
$this->charset,
mb_detect_encoding($value, mb_list_encodings())
);
}
/*
* Renders the CSV.
*
* @param string $filename @see file_put_contents
* @param int $flags @see file_put_contents
*
* @return string
*/
public function render($filename = null, $flags = null)
{
if ($this->isModified || empty($this->render)) {
$lines = [];
if (!empty($this->headers)) {
$lines[] = $this->formatData($this->headers);
}
foreach ($this->datas as $data) {
$lines[] = $this->formatData($data);
}
$this->render = implode($this->encode($this->endOfLine), $lines);
}
if ($this->encoding !== 'UTF-8') {
$this->render = iconv(
mb_detect_encoding($this->render),
$this->encoding,
$this->render
);
$this->isModified = false;
if ($filename !== null) {
$content = $this->render;
if ($flags === FILE_APPEND && file_exists($filename)) {
$content = $this->endOfLine.$content;
}
file_put_contents($filename, $content, $flags);
}
return $this->render;
}
public function hasDatas()
{
return count($this->datas) > 1;
}
protected function datasToCsvLine($datas)
{
foreach ($datas as $k => $v) {
$v = str_replace('\\', '\\\\', $v);
if ($this->enclose) {
$v = str_replace($this->enclose, '\\'.$this->enclose, $v);
} else {
$v = str_replace($this->delimiter, '\\'.$this->delimiter, $v);
}
$datas[$k] = $this->enclose.$v.$this->enclose;
}
$datas = implode($this->delimiter, $datas);
return $datas;
}
protected function append($line)
{
$this->render.= sprintf("%s%s", $line, $this->endline);
}
public function compileToFile($filename)
{
if (file_exists($filename)) {
unlink($filename);
}
file_put_contents($filename, $this->compile());
}
}

View file

@ -2,149 +2,225 @@
namespace Deblan\Csv;
use Deblan\Csv\Exception\CsvParserInvalidParameterException;
use Deblan\Csv\Exception\CsvParserException;
/**
* class Csv.
*
* @author Simon Vieille <simon@deblan.fr>
*/
class CsvParser
{
private $filename;
/**
* @var string
*/
protected $delimiter = ';';
private $delimiter;
/**
* @var string
*/
protected $enclosure = '"';
private $enclosure;
/**
* @var string
*/
protected $endOfLine = "\n";
private $escapeChar;
/**
* @var array
*/
protected $datas = [];
private $hasLegend;
/**
* @var array
*/
protected $headers = [];
private $datas = array();
/**
* @var bool
*/
protected $hasHeaders = false;
private $legend = array();
private $nullValues = array();
public function __construct($filename, $delimiter = ';', $enclosure = '"', $escapeChar = '\\', $hasLegend = false, array $nullValues = array(''))
/**
* Set the value of "delimiter".
*
* @param string $delimiter
*
* @return Csv
*/
public function setDelimiter($delimiter)
{
$this->setFilename($filename);
$this->setDelimiter($delimiter);
$this->setEnclosure($enclosure);
$this->setEscapeChar($escapeChar);
$this->setHasLegend($hasLegend);
$this->setNullValues($nullValues);
}
$this->delimiter = (string) $delimiter;
public function setFilename($v)
{
if (!is_string($v)) {
throw new CsvParserInvalidParameterException(sprintf('"%s" is not a valid string.', $v));
}
if (!file_exists($v)) {
throw new CsvParserException(sprintf('"%s" does not exist.', $v));
}
if (!is_readable($v)) {
throw new CsvParserException(sprintf('"%s" is not readable.', $v));
}
$this->filename = $v;
}
public function setDelimiter($v)
{
if (!is_string($v)) {
throw new CsvParserInvalidParameterException(sprintf('"%s" is not a valid string.', $v));
}
$this->delimiter = $v;
}
public function setEnclosure($v)
{
if (!is_string($v)) {
throw new CsvParserInvalidParameterException(sprintf('"%s" is not a valid string.', $v));
}
$this->enclosure = $v;
}
public function setEscapeChar($v)
{
if (!is_string($v)) {
throw new CsvParserInvalidParameterException(sprintf('"%s" is not a valid string.', $v));
}
$this->escapeChar = $v;
}
public function setHasLegend($v)
{
if (!is_bool($v)) {
throw new CsvParserInvalidParameterException(sprintf('"%s" is not a valid bool.', $v));
}
$this->hasLegend = $v;
}
public function getHasLegend()
{
return $this->hasLegend;
}
public function getLegend()
{
return $this->legend;
}
public function setNullValues(array $v)
{
$this->nullValues = $v;
return $this;
}
/**
* Get the value of "delimiter".
*
* To improve...
*
* @return array
*/
protected function cleanNullValues($line)
public function getDelimiter()
{
return str_replace($this->nullValues, '', $line);
return $this->delimiter;
}
/**
* Set the value of "enclosure".
*
* @param string $enclosure
*
* @return Csv
*/
public function setEnclosure($enclosure)
{
$this->enclosure = (string) $enclosure;
return $this;
}
/**
* Get the value of "enclosure".
*
* @return string
*/
public function getEnclosure()
{
return $this->enclosure;
}
/**
* Set the value of "endOfLine".
*
* @param string $endOfLine
*
* @return Csv
*/
public function setEndOfLine($endOfLine)
{
$this->endOfLine = (string) $endOfLine;
return $this;
}
/**
* Get the value of "endOfLine".
*
* @return string
*/
public function getEndOfLine()
{
return $this->endOfLine;
}
/**
* Get the value of "hasHeaders".
*
* @return bool
*/
public function getHasHeaders()
{
return $this->hasHeaders;
}
/**
* Set the value of "headers".
*
* @param bool $hasHeaders
*
* @return Csv
*/
public function setHasHeaders($hasHeaders)
{
$this->hasHeaders = (bool) $hasHeaders;
return $this;
}
/**
* Get the value of "headers".
*
* @return array
*/
public function getHeaders()
{
return $this->headers;
}
/**
* Get the value of "datas".
*
* @return array
*/
public function getDatas()
{
return $this->datas;
}
public function parse()
/*
* Parses a string.
*
* @param string $string
*
* @return CsvParser
*/
public function parseString($string)
{
if (!empty($this->datas)) {
return $this->datas;
}
$this->datas = [];
$this->headers = [];
$lines = str_getcsv($string, $this->endOfLine);
$lines = file($this->filename);
foreach ($lines as $key => $line) {
$data = $this->parseLine($line, $this->hasHeaders && $key === 0);
if (empty($lines)) {
return true;
}
if ($this->hasLegend) {
$this->legend = str_getcsv($lines[0], $this->delimiter, $this->enclosure, $this->escapeChar);
unset($lines[0]);
}
foreach ($lines as $l => $line) {
$datas = str_getcsv($this->cleanNullValues($line), $this->delimiter, $this->enclosure, $this->escapeChar);
if ($this->hasLegend) {
foreach ($this->legend as $k => $v) {
$datas[$v] = isset($datas[$k]) ? $datas[$k] : null;
}
if ($data === null) {
continue;
}
$this->datas[] = $datas;
if ($this->hasHeaders && $key === 0) {
$this->headers = $data;
} else {
$this->datas[] = $data;
}
}
return true;
return $this;
}
/*
* Parses a line.
*
* @param string $line
* @param bool $isHeaders
*
* @return array
*/
public function parseLine($line, $isHeaders = false)
{
$line = trim($line);
if (empty($line)) {
return null;
}
$csv = str_getcsv($line, $this->delimiter, $this->enclosure);
if (!$isHeaders && $this->hasHeaders && !empty($this->headers)) {
foreach ($this->headers as $key => $header) {
$csv[$header] = isset($csv[$key]) ? $csv[$key] : null;
}
}
return $csv;
}
/*
* Parses a file.
*
* @param string $filaname
*
* @return CsvParser
*/
public function parseFile($filename)
{
return $this->parseString(file_get_contents($filename));
}
}

View file

@ -0,0 +1,69 @@
<?php
namespace Deblan\Csv;
/**
* class Csv.
*
* @author Simon Vieille <simon@deblan.fr>
*/
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;
}
}

View file

@ -1,7 +0,0 @@
<?php
namespace Deblan\Csv\Exception;
class CsvException extends \Exception
{
}

View file

@ -1,7 +0,0 @@
<?php
namespace Deblan\Csv\Exception;
class CsvInvalidParameterException extends CsvException
{
}

View file

@ -1,7 +0,0 @@
<?php
namespace Deblan\Csv\Exception;
class CsvParserException extends \Exception
{
}

View file

@ -1,7 +0,0 @@
<?php
namespace Deblan\Csv\Exception;
class CsvParserInvalidParameterException extends CsvParserException
{
}

View file

@ -1,8 +1,189 @@
<?php
class CsvParserTest extends \PHPUnit_Framework_TestCase
use Deblan\Csv\CsvParser;
use PHPUnit\Framework\TestCase;
class CsvParserTest extends TestCase
{
public function testTest()
{
}
public function testGettersAndSettersAndDefaultValues()
{
$parser = new CsvParser();
$this->assertEquals(';', $parser->getDelimiter());
$parser->setDelimiter('#');
$this->assertEquals('#', $parser->getDelimiter());
$parser = new CsvParser();
$this->assertEquals("\n", $parser->getEndOfLine());
$parser->setEndOfLine("\r\n");
$this->assertEquals("\r\n", $parser->getEndOfLine());
$parser = new CsvParser();
$this->assertEquals('"', $parser->getEnclosure());
$parser->setEnclosure("'");
$this->assertEquals("'", $parser->getEnclosure());
$parser = new CsvParser();
$this->assertEquals([], $parser->getDatas());
$this->assertEquals([], $parser->getHeaders());
$this->assertEquals(false, $parser->getHasHeaders());
$parser->setHasHeaders(true);
$this->assertEquals(true, $parser->getHasHeaders());
}
public function testParser()
{
$parser = new CsvParser();
$this->assertEquals(['foo', 'bar'], $parser->parseLine('"foo";"bar"'));
$this->assertEquals([], $parser->getDatas());
$this->assertEquals([], $parser->getHeaders());
$parser = new CsvParser();
$parser->parseString('"foo";"bar"');
$this->assertEquals([['foo', 'bar']], $parser->getDatas());
$this->assertEquals([], $parser->getHeaders());
$parser = new CsvParser();
$parser->parseString('"foo";"bar"'."\n".'"foo2";"bar2"');
$this->assertEquals([['foo', 'bar'], ['foo2', 'bar2']], $parser->getDatas());
$this->assertEquals([], $parser->getHeaders());
$parser = new CsvParser();
$parser->setHasHeaders(true);
$parser->parseString('"foo";"bar"'."\n".'"foo2";"bar2"');
$this->assertEquals([['foo2', 'bar2', 'foo' => 'foo2', 'bar' => 'bar2']], $parser->getDatas());
$this->assertEquals(['foo', 'bar'], $parser->getHeaders());
$parser = new CsvParser();
$parser->setHasHeaders(true);
$parser->setEnclosure(null);
$parser->parseString('foo;bar'."\n".'foo2;bar2;boo2');
$this->assertEquals([['foo2', 'bar2', 'boo2', 'foo' => 'foo2', 'bar' => 'bar2']], $parser->getDatas());
$this->assertEquals(['foo', 'bar'], $parser->getHeaders());
$parser = new CsvParser();
$parser->setHasHeaders(true);
$parser->parseString('foo;bar'."\n".'foo2');
$this->assertEquals([['foo2', 'foo' => 'foo2', 'bar' => null]], $parser->getDatas());
$this->assertEquals(['foo', 'bar'], $parser->getHeaders());
$parser = new CsvParser();
$parser->setHasHeaders(true);
$parser->parseFile(__DIR__.'/fixtures/example.csv');
$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());
$parser = new CsvParser();
$parser->setHasHeaders(false);
$parser->parseFile(__DIR__.'/fixtures/example2.csv');
$this->assertEquals(
[
[
'foo 1',
'bar 1',
],
[
'foo 2',
'ba"r 2',
],
[
'foo 3',
'bar 3',
],
],
$parser->getDatas()
);
$parser = new CsvParser();
$parser->setHasHeaders(true);
$parser->setEnclosure("'");
$parser->setDelimiter('#');
$parser->parseFile(__DIR__.'/fixtures/example3.csv');
$this->assertEquals(
[
[
'foo 1',
'',
"FO'O" => 'foo 1',
'BAR' => '',
],
[
'foo 1b',
"FO'O" => 'foo 1b',
'BAR' => null,
],
[
'foo 2',
'bar 2',
'unexpected 3',
"FO'O" => 'foo 2',
'BAR' => 'bar 2',
],
[
'foo 3',
'bar 3',
"FO'O" => 'foo 3',
'BAR' => 'bar 3',
],
],
$parser->getDatas()
);
$parser = new CsvParser();
$parser->setHasHeaders(true);
$parser->setEnclosure("'");
$parser->setDelimiter('#');
$parser->setEndOfLine("\r\n");
$parser->parseFile(__DIR__.'/fixtures/example4.csv');
$this->assertEquals(
[
[
'foo 1',
'',
"FO'O" => 'foo 1',
'BAR' => '',
],
[
'foo 1b',
"FO'O" => 'foo 1b',
'BAR' => null,
],
[
'foo 2',
'bar 2',
'unexpected 3',
"FO'O" => 'foo 2',
'BAR' => 'bar 2',
],
[
'foo 3',
'bar 3',
"FO'O" => 'foo 3',
'BAR' => 'bar 3',
],
],
$parser->getDatas()
);
}
}

View file

@ -0,0 +1,176 @@
<?php
use Deblan\Csv\CsvParser;
use PHPUnit\Framework\TestCase;
use Deblan\Csv\CsvStreamParser;
class CsvParserParserTest extends CsvParserTest
{
public function testStreamParser()
{
$parser = new CsvStreamParser();
$this->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());
}
}

View file

@ -1,94 +1,143 @@
<?php
use Deblan\Csv\Csv;
use PHPUnit\Framework\TestCase;
class CsvTest extends \PHPUnit_Framework_TestCase
/**
* class CsvTest.
*
* @author Simon Vieille <simon@deblan.fr>
*/
class CsvTest extends TestCase
{
public function testAddLine()
public function testGettersAndSettersAndDefaultValues()
{
$csv = new Csv();
$csv->addLine(array('foo', 'bar'));
$this->assertEquals('"foo";"bar"'."\n", $csv->compile());
}
public function testSetLegend()
{
$csv = new Csv();
$this->assertEquals(false, $csv->getHasLegend());
$csv->setLegend($a = array('bim', 'bam'));
$this->assertEquals($a, $csv->getLegend());
$csv->addLine(array('foo', 'bar'));
$this->assertEquals(true, $csv->getHasLegend());
$this->assertEquals(
'"bim";"bam"'."\n".
'"foo";"bar"'."\n",
$csv->compile()
);
$csv = new Csv();
$csv->addLine(array('foo', 'bar'));
$csv->setLegend(array('bim', 'bam'));
$this->assertEquals(true, $csv->getHasLegend());
$this->assertEquals(
'"bim";"bam"'."\n".
'"foo";"bar"'."\n",
$csv->compile()
);
}
public function testHasDatas()
{
$csv = new Csv();
$this->assertEquals(false, $csv->hasDatas());
$csv->setLegend(array('bim', 'bam'));
$this->assertEquals(false, $csv->hasDatas());
$csv->addLine(array('foo', 'bar'));
$this->assertEquals(true, $csv->hasDatas());
$csv = new Csv();
$csv->addLine(array('foo', 'bar'));
$this->assertEquals(true, $csv->hasDatas());
}
public function testDatasToCsvLine()
{
$csv = new Csv();
$csv->addLine(array('fo\\o', 'bar'));
$this->assertEquals('"fo\\\\o";"bar"'."\n", $csv->compile());
$csv = new Csv();
$csv->setDelimiter(':');
$csv->addLine(array('foo', 'bar'));
$this->assertEquals('"foo":"bar"'."\n", $csv->compile());
$csv = new Csv();
$csv->setDelimiter(':');
$csv->addLine(array('fo:o', 'bar'));
$this->assertEquals('"fo:o":"bar"'."\n", $csv->compile());
$csv = new Csv();
$csv->setDelimiter(':');
$csv->setEnclosure('');
$csv->addLine(array('fo:o', 'bar'));
$this->assertEquals('fo\\:o:bar'."\n", $csv->compile());
$this->assertEquals(';', $csv->getDelimiter());
$csv->setDelimiter('#');
$this->assertEquals('#', $csv->getDelimiter());
$csv = new Csv();
$this->assertEquals('"', $csv->getEnclosure());
$csv->setEnclosure('#');
$csv->addLine(array('foo', 'bar'));
$this->assertEquals('#foo#;#bar#'."\n", $csv->compile());
$this->assertEquals('#', $csv->getEnclosure());
$csv = new Csv();
$csv->setEnclosure('#');
$csv->addLine(array('f#oo', 'bar'));
$this->assertEquals('#f\\#oo#;#bar#'."\n", $csv->compile());
$this->assertEquals("\n", $csv->getEndOfLine());
$csv->setEndOfLine("\r\n");
$this->assertEquals("\r\n", $csv->getEndOfLine());
$csv = new Csv();
$this->assertEquals([], $csv->getDatas());
$csv->setDatas([['a', 'b', 'c'], ['d', 'e', 'f']]);
$this->assertEquals([['a', 'b', 'c'], ['d', 'e', 'f']], $csv->getDatas());
$csv = new Csv();
$this->assertEquals([], $csv->getHeaders());
$csv->setHeaders(['a', 'b', 'c']);
$this->assertEquals(['a', 'b', 'c'], $csv->getHeaders());
$csv = new Csv();
$this->assertEquals('UTF-8', $csv->getCharset());
$csv->setCharset('ISO-8859-1');
$this->assertEquals('ISO-8859-1', $csv->getCharset());
}
public function testRender()
{
$csv = new Csv();
$this->assertEquals(null, $csv->render());
$csv->addData(['foo', 'bar']);
$this->assertEquals('"foo";"bar"', $csv->render());
$csv = new Csv();
$csv->appendData(['foo', 'bar']);
$csv->appendData(['foo2', 'bar2']);
$this->assertEquals('"foo";"bar"'."\n".'"foo2";"bar2"', $csv->render());
$csv = new Csv();
$csv->preprendData(['foo2', 'bar2']);
$csv->preprendData(['foo', 'bar']);
$this->assertEquals('"foo";"bar"'."\n".'"foo2";"bar2"', $csv->render());
$csv = new Csv();
$csv->addData(['foo', 'bar']);
$csv->preprendData(['foo2', 'bar2']);
$csv->appendData(['foo3', 'bar3']);
$this->assertEquals('"foo2";"bar2"'."\n".'"foo";"bar"'."\n".'"foo3";"bar3"', $csv->render());
$csv = new Csv();
$csv->setHeaders(['a', 'b']);
$csv->addData(['foo', 'bar']);
$this->assertEquals('"a";"b"'."\n".'"foo";"bar"', $csv->render());
$csv = new Csv();
$csv->setHeaders(['a"b', 'cd"']);
$csv->addData(['f"oo', 'b""ar']);
$this->assertEquals('"a""b";"cd"""'."\n".'"f""oo";"b""""ar"', $csv->render());
$csv = new Csv();
$csv->addData(['foo', 'bar']);
$csv->setHeaders(['a', 'b']);
$this->assertEquals('"a";"b"'."\n".'"foo";"bar"', $csv->render());
$csv = new Csv();
$csv->addData(['foo', 'bar']);
$csv->addData(['foo2', 'bar2']);
$csv->setHeaders(['a', 'b']);
$csv->setEndOfLine("\r\n");
$this->assertEquals('"a";"b"'."\r\n".'"foo";"bar"'."\r\n".'"foo2";"bar2"', $csv->render());
$csv = new Csv();
$csv->setHeaders(['a', "b'd"]);
$csv->addData(["fo'o", 'bar']);
$csv->setEnclosure("'");
$this->assertEquals("'a';'b''d'"."\n"."'fo''o';'bar'", $csv->render());
$csv = new Csv();
$csv->setHeaders(['a', 'b']);
$csv->addData(['foo', 'bar']);
$csv->setDelimiter('#');
$this->assertEquals('"a"#"b"'."\n".'"foo"#"bar"', $csv->render());
$filename = tempnam(sys_get_temp_dir(), 'csvtests');
$csv = new Csv();
$csv->setHeaders(['a', 'b']);
$csv->addData(['foo', 'bar']);
$render = $csv->render($filename);
$this->assertEquals('"a";"b"'."\n".'"foo";"bar"', $render);
$this->assertEquals('"a";"b"'."\n".'"foo";"bar"', file_get_contents($filename));
$render = $csv->render($filename);
$this->assertEquals('"a";"b"'."\n".'"foo";"bar"', $render);
$this->assertEquals('"a";"b"'."\n".'"foo";"bar"', file_get_contents($filename));
unlink($filename);
$csv = new Csv();
$csv->setHeaders(['a', 'b']);
$csv->addData(['foo', 'bar']);
$csv->render($filename);
$csv = new Csv();
$csv->addData(['foo2', 'bar2']);
$render = $csv->render($filename, FILE_APPEND);
$this->assertEquals('"a";"b"'."\n".'"foo";"bar"'."\n".'"foo2";"bar2"', file_get_contents($filename));
unlink($filename);
}
public function testEncoding()
{
$csv = new Csv();
$csv->addData(['é']);
$render = $csv->render();
$this->assertEquals('"é"', $csv->render());
$csv = new Csv();
$csv->addData(['é']);
$csv->setCharset('ISO-8859-1');
$render = $csv->render();
$this->assertEquals('"é"', utf8_encode($csv->render()));
}
}

4
tests/fixtures/example.csv vendored Normal file
View file

@ -0,0 +1,4 @@
"FOO";"BAR"
"foo 1";"bar 1"
"foo 2";"bar 2"
"foo 3";"bar 3"
1 FOO BAR
2 foo 1 bar 1
3 foo 2 bar 2
4 foo 3 bar 3

3
tests/fixtures/example2.csv vendored Normal file
View file

@ -0,0 +1,3 @@
"foo 1";"bar 1"
"foo 2";"ba""r 2"
"foo 3";"bar 3"
1 foo 1 bar 1
2 foo 2 ba"r 2
3 foo 3 bar 3

5
tests/fixtures/example3.csv vendored Normal file
View file

@ -0,0 +1,5 @@
'FO''O'#'BAR'
'foo 1'#
'foo 1b'
'foo 2'#'bar 2'#'unexpected 3'
'foo 3'#'bar 3'
1 'FO''O'#'BAR'
2 'foo 1'#
3 'foo 1b'
4 'foo 2'#'bar 2'#'unexpected 3'
5 'foo 3'#'bar 3'

5
tests/fixtures/example4.csv vendored Normal file
View file

@ -0,0 +1,5 @@
'FO''O'#'BAR'
'foo 1'#
'foo 1b'
'foo 2'#'bar 2'#'unexpected 3'
'foo 3'#'bar 3'
1 'FO''O'#'BAR'
2 'foo 1'#
3 'foo 1b'
4 'foo 2'#'bar 2'#'unexpected 3'
5 'foo 3'#'bar 3'