diff --git a/.gitignore b/.gitignore index 57872d0..f2a5df1 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ +*.swp +tags + /vendor/ diff --git a/.woodpecker.yml b/.woodpecker.yml deleted file mode 100644 index f338970..0000000 --- a/.woodpecker.yml +++ /dev/null @@ -1,17 +0,0 @@ -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 diff --git a/LICENSE b/LICENSE index 80445fb..b57a6c2 100644 --- a/LICENSE +++ b/LICENSE @@ -20,5 +20,4 @@ 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. \ No newline at end of file diff --git a/README.md b/README.md index 1afed82..7c89d21 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,12 @@ CSV parser/generator ==================== -[![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. +A simple PHP library to parse and generate CSV files. ## Composer installation ``` -$ composer require deblan/csv "~3" +$ composer require deblan/csv ``` Or in your composer.json: @@ -22,7 +14,8 @@ Or in your composer.json: ``` { "require": { - "deblan/csv": "~3" + [...] + "deblan/csv": "dev-master" } } ``` @@ -36,85 +29,33 @@ use Deblan\Csv\Csv; $csv = new Csv(); -// Defines the delimiter (default is ;) -$csv->setDelimiter(";"); +$csv->addLine(array('Foo', '$1000')); +$csv->addLine(array('Bar', '$600')); -// 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); +$result = $csv->compile(); ``` -### Parse a file +```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 ```php use Deblan\Csv\CsvParser; -$csv = new CsvParser(); +$csv = new CsvParser('products.csv'); +$csv->setHasLegend(true); +$csv->parse(); -// 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(); +$legend = $csv->getLegend(); $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()) { - // ... -} -``` diff --git a/phpunit.xml b/phpunit.xml index 2fd344a..eabb967 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -7,11 +7,12 @@ convertWarningsToExceptions = "true" processIsolation = "false" stopOnFailure = "false" + syntaxCheck = "false" bootstrap = "vendor/autoload.php" > - - - tests/ - - + + + tests/ + + diff --git a/src/Deblan/Csv/Csv.php b/src/Deblan/Csv/Csv.php index fa57d8f..1054f20 100644 --- a/src/Deblan/Csv/Csv.php +++ b/src/Deblan/Csv/Csv.php @@ -2,340 +2,192 @@ namespace Deblan\Csv; -/** - * class Csv. - * - * @author Simon Vieille - */ +use Deblan\Csv\Exception\CsvInvalidParameterException; + class Csv { - /** - * @var string - */ - protected $delimiter = ';'; + private $delimiter; - /** - * @var string - */ - protected $enclosure = '"'; + private $enclosure; - /** - * @var string - */ - protected $endOfLine = "\n"; + private $endline; - /** - * @var array - */ - protected $datas = []; + private $datas; - /** - * @var array - */ - protected $headers = []; + private $legend; - /** - * @var string - */ - protected $charset = 'UTF-8'; + private $render; - /** - * @var bool - */ - protected $isModified = false; + private $encoding; - /** - * @var string - */ - protected $render; + private $hasLegend = false; - /** - * Set the value of "delimiter". - * - * @param string $delimiter - * - * @return Csv - */ - public function setDelimiter($delimiter) + public function __construct($delimiter = ';', $enclosure = '"', $endline = "\n", $encoding = 'UTF-8') { - if ($this->delimiter !== $delimiter) { - $this->delimiter = $delimiter; - $this->isModified = true; + $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)); } + $this->filename = $v; + } + + protected function setHasLegend($hasLegend) + { + $this->hasLegend = $hasLegend; + return $this; } - /** - * Get the value of "delimiter". - * - * @return array - */ - public function getDelimiter() + public function getHasLegend() { - return $this->delimiter; + return $this->hasLegend; } - /** - * Set the value of "enclosure". - * - * @param string $enclosure - * - * @return Csv - */ - public function setEnclosure($enclosure) + public function hasLegend() { - $enclosure = (string) $enclosure; + return $this->hasLegend; + } - if ($this->enclosure !== $enclosure) { - $this->enclosure = $enclosure; - $this->isModified = true; + 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; } + $this->datas[] = $values; + } + + public function setEncoding($encoding) + { + $this->encoding = $encoding; + return $this; } - /** - * Get the value of "enclosure". - * - * @return string - */ - public function getEnclosure() + public function getEncoding() { - return $this->enclosure; + return $this->encoding; } - /** - * Set the value of "endOfLine". - * - * @param string $endOfLine - * - * @return Csv - */ - public function setEndOfLine($endOfLine) + public function setDelimiter($v) { - if ($this->endOfLine !== $endOfLine) { - $this->endOfLine = (string) $endOfLine; - $this->isModified = true; + if (!is_string($v)) { + throw new CsvInvalidParameterException(sprintf('"%s" is not a valid string.', $v)); } - return $this; + $this->delimiter = $v; } - /** - * Get the value of "endOfLine". - * - * @return string - */ - public function getEndOfLine() + public function setEndline($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; + if (!is_string($v)) { + throw new CsvInvalidParameterException(sprintf('"%s" is not a valid string.', $v)); } - return $this; + $this->endline = $v; } - /** - * Get the value of "headers". - * - * @return array - */ - public function getHeaders() + public function setEnclosure($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; + if (!is_string($v)) { + throw new CsvInvalidParameterException(sprintf('"%s" is not a valid string.', $v)); } - return $this; + $this->enclose = $v; } - /** - * Get the value of "charset". - * - * @return string - */ - public function getCharset() + public function getLegend() { - return $this->charset; + return $this->legend; } - /* - * 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; } - /* - * Appends data. - * - * @param array $data - * - * @return Csv - */ - public function appendData(array $data) + public function compile() { - $this->datas[] = $data; - $this->isModified = true; + $this->render = ""; - 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; + if ($this->datas[0] !== null) { + $this->append($this->datasToCsvLine($this->datas[0])); } - $data = implode($this->delimiter, $columns); - $data = $this->encode($data); + unset($this->datas[0]); - 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); + foreach ($this->datas as $v) { + $this->append($this->datasToCsvLine($v)); } - $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); + if ($this->encoding !== 'UTF-8') { + $this->render = iconv( + mb_detect_encoding($this->render), + $this->encoding, + $this->render + ); } 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()); + } } diff --git a/src/Deblan/Csv/CsvParser.php b/src/Deblan/Csv/CsvParser.php index 7d92af2..99abe49 100644 --- a/src/Deblan/Csv/CsvParser.php +++ b/src/Deblan/Csv/CsvParser.php @@ -2,225 +2,149 @@ namespace Deblan\Csv; -/** - * class Csv. - * - * @author Simon Vieille - */ +use Deblan\Csv\Exception\CsvParserInvalidParameterException; +use Deblan\Csv\Exception\CsvParserException; + class CsvParser { - /** - * @var string - */ - protected $delimiter = ';'; + private $filename; - /** - * @var string - */ - protected $enclosure = '"'; + private $delimiter; - /** - * @var string - */ - protected $endOfLine = "\n"; + private $enclosure; - /** - * @var array - */ - protected $datas = []; + private $escapeChar; - /** - * @var array - */ - protected $headers = []; + private $hasLegend; - /** - * @var bool - */ - protected $hasHeaders = false; + private $datas = array(); - /** - * Set the value of "delimiter". - * - * @param string $delimiter - * - * @return Csv - */ - public function setDelimiter($delimiter) + private $legend = array(); + + private $nullValues = array(); + + public function __construct($filename, $delimiter = ';', $enclosure = '"', $escapeChar = '\\', $hasLegend = false, array $nullValues = array('')) { - $this->delimiter = (string) $delimiter; + $this->setFilename($filename); + $this->setDelimiter($delimiter); + $this->setEnclosure($enclosure); + $this->setEscapeChar($escapeChar); + $this->setHasLegend($hasLegend); + $this->setNullValues($nullValues); + } - return $this; + 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; } /** - * Get the value of "delimiter". * - * @return array + * To improve... + * */ - public function getDelimiter() + protected function cleanNullValues($line) { - return $this->delimiter; + return str_replace($this->nullValues, '', $line); } - /** - * 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; } - /* - * Parses a string. - * - * @param string $string - * - * @return CsvParser - */ - public function parseString($string) + public function parse() { - $this->datas = []; - $this->headers = []; - $lines = str_getcsv($string, $this->endOfLine); - - foreach ($lines as $key => $line) { - $data = $this->parseLine($line, $this->hasHeaders && $key === 0); - - if ($data === null) { - continue; - } - - if ($this->hasHeaders && $key === 0) { - $this->headers = $data; - } else { - $this->datas[] = $data; - } + if (!empty($this->datas)) { + return $this->datas; } - return $this; - } + $lines = file($this->filename); - /* - * 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; + if (empty($lines)) { + return true; } - $csv = str_getcsv($line, $this->delimiter, $this->enclosure); + if ($this->hasLegend) { + $this->legend = str_getcsv($lines[0], $this->delimiter, $this->enclosure, $this->escapeChar); + unset($lines[0]); + } - if (!$isHeaders && $this->hasHeaders && !empty($this->headers)) { - foreach ($this->headers as $key => $header) { - $csv[$header] = isset($csv[$key]) ? $csv[$key] : null; + 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; + } } + + $this->datas[] = $datas; } - return $csv; - } - - /* - * Parses a file. - * - * @param string $filaname - * - * @return CsvParser - */ - public function parseFile($filename) - { - return $this->parseString(file_get_contents($filename)); + return true; } } diff --git a/src/Deblan/Csv/CsvStreamParser.php b/src/Deblan/Csv/CsvStreamParser.php deleted file mode 100644 index e12b7e4..0000000 --- a/src/Deblan/Csv/CsvStreamParser.php +++ /dev/null @@ -1,69 +0,0 @@ - - */ -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/src/Deblan/Csv/Exception/CsvException.php b/src/Deblan/Csv/Exception/CsvException.php new file mode 100644 index 0000000..c7369b3 --- /dev/null +++ b/src/Deblan/Csv/Exception/CsvException.php @@ -0,0 +1,7 @@ +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() - ); - } + public function testTest() + { + } } diff --git a/tests/CsvStreamParserTest.php b/tests/CsvStreamParserTest.php deleted file mode 100644 index 8a6fb8c..0000000 --- a/tests/CsvStreamParserTest.php +++ /dev/null @@ -1,176 +0,0 @@ -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()); - } -} diff --git a/tests/CsvTest.php b/tests/CsvTest.php index ebea607..4ae789c 100644 --- a/tests/CsvTest.php +++ b/tests/CsvTest.php @@ -1,143 +1,94 @@ - */ -class CsvTest extends TestCase +class CsvTest extends \PHPUnit_Framework_TestCase { - public function testGettersAndSettersAndDefaultValues() + public function testAddLine() { $csv = new Csv(); - $this->assertEquals(';', $csv->getDelimiter()); - $csv->setDelimiter('#'); - $this->assertEquals('#', $csv->getDelimiter()); + $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()); $csv = new Csv(); - $this->assertEquals('"', $csv->getEnclosure()); $csv->setEnclosure('#'); - $this->assertEquals('#', $csv->getEnclosure()); + $csv->addLine(array('foo', 'bar')); + $this->assertEquals('#foo#;#bar#'."\n", $csv->compile()); $csv = new Csv(); - $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())); + $csv->setEnclosure('#'); + $csv->addLine(array('f#oo', 'bar')); + $this->assertEquals('#f\\#oo#;#bar#'."\n", $csv->compile()); } } diff --git a/tests/fixtures/example.csv b/tests/fixtures/example.csv deleted file mode 100644 index d6b62ce..0000000 --- a/tests/fixtures/example.csv +++ /dev/null @@ -1,4 +0,0 @@ -"FOO";"BAR" -"foo 1";"bar 1" -"foo 2";"bar 2" -"foo 3";"bar 3" diff --git a/tests/fixtures/example2.csv b/tests/fixtures/example2.csv deleted file mode 100644 index a4e9ac5..0000000 --- a/tests/fixtures/example2.csv +++ /dev/null @@ -1,3 +0,0 @@ -"foo 1";"bar 1" -"foo 2";"ba""r 2" -"foo 3";"bar 3" diff --git a/tests/fixtures/example3.csv b/tests/fixtures/example3.csv deleted file mode 100644 index 9077a11..0000000 --- a/tests/fixtures/example3.csv +++ /dev/null @@ -1,5 +0,0 @@ -'FO''O'#'BAR' -'foo 1'# -'foo 1b' -'foo 2'#'bar 2'#'unexpected 3' -'foo 3'#'bar 3' diff --git a/tests/fixtures/example4.csv b/tests/fixtures/example4.csv deleted file mode 100644 index e56aef3..0000000 --- a/tests/fixtures/example4.csv +++ /dev/null @@ -1,5 +0,0 @@ -'FO''O'#'BAR' -'foo 1'# -'foo 1b' -'foo 2'#'bar 2'#'unexpected 3' -'foo 3'#'bar 3'