Merge pull request #133 from parsecsv/fixing-132-output-with-fields

Bug fix: calling ->output() with $fields parameter set
This commit is contained in:
Fonata
2018-03-28 13:25:05 +02:00
committed by GitHub
3 changed files with 83 additions and 46 deletions

View File

@@ -300,14 +300,15 @@ class Csv {
* Class constructor
*
* @param string|null $input The CSV string or a direct filepath
* @param integer|null $offset Number of rows to ignore from the beginning
* of the data
* @param integer|null $limit Limits the number of returned rows to
* specified amount
* @param integer|null $offset Number of rows to ignore from the
* beginning of the data
* @param integer|null $limit Limits the number of returned rows
* to specified amount
* @param string|null $conditions Basic SQL-like conditions for row
* matching
* @param null|true $keep_file_data Keep raw file data in memory after
* successful parsing (useful for debugging)
* successful parsing
* (useful for debugging)
*/
public function __construct($input = null, $offset = null, $limit = null, $conditions = null, $keep_file_data = null) {
$this->init($offset, $limit, $conditions, $keep_file_data);
@@ -318,14 +319,15 @@ class Csv {
}
/**
* @param integer|null $offset Number of rows to ignore from the beginning
* of the data
* @param integer|null $limit Limits the number of returned rows to
* specified amount
* @param integer|null $offset Number of rows to ignore from the
* beginning of the data
* @param integer|null $limit Limits the number of returned rows
* to specified amount
* @param string|null $conditions Basic SQL-like conditions for row
* matching
* @param null|true $keep_file_data Keep raw file data in memory after
* successful parsing (useful for debugging)
* successful parsing
* (useful for debugging)
*/
public function init($offset = null, $limit = null, $conditions = null, $keep_file_data = null) {
if (!is_null($offset)) {
@@ -354,9 +356,12 @@ class Csv {
* Parse a CSV file or string
*
* @param string|null $input The CSV string or a direct filepath
* @param integer $offset Number of rows to ignore from the beginning of the data
* @param integer $limit Limits the number of returned rows to specified amount
* @param string $conditions Basic SQL-like conditions for row matching
* @param integer $offset Number of rows to ignore from the
* beginning of the data
* @param integer $limit Limits the number of returned rows to
* specified amount
* @param string $conditions Basic SQL-like conditions for row
* matching
*
* @return bool True on success
*/
@@ -366,13 +371,11 @@ class Csv {
}
if (empty($input)) {
// todo: but why true?
return true;
return false;
}
$this->init($offset, $limit, $conditions);
if (strlen($input) <= PHP_MAXPATHLEN && is_readable($input)) {
$this->file = $input;
$this->data = $this->parse_file();
@@ -382,12 +385,7 @@ class Csv {
$this->data = $this->parse_string();
}
if ($this->data === false) {
return false;
}
return true;
return $this->data !== false;
}
/**
@@ -396,8 +394,10 @@ class Csv {
*
* @param string $file File location to save to
* @param array $data 2D array of data
* @param bool $append Append current data to end of target CSV, if file exists
* @param array $fields Field names. Sets the header. If it is not set $this->titles would be used instead.
* @param bool $append Append current data to end of target CSV, if file
* exists
* @param array $fields Field names. Sets the header. If it is not set
* $this->titles would be used instead.
*
* @return bool
*/
@@ -419,7 +419,10 @@ class Csv {
* @param string|null $filename If a filename is specified here or in the
* object, headers and data will be output
* directly to browser as a downloadable
* file.
* file. This file doesn't have to exist on
* the server; the parameter only affects
* how the download is called to the
* browser.
* @param array[] $data 2D array with data
* @param array $fields Field names
* @param string|null $delimiter character used to separate data
@@ -527,8 +530,8 @@ class Csv {
}
/**
* Get total number of data rows (exclusive heading line if present) in csv
* without parsing whole data.
* Get total number of data rows (exclusive heading line if present) in CSV
* without parsing the whole data string.
*
* @return bool|int
*/
@@ -541,9 +544,10 @@ class Csv {
$this->_detect_and_remove_sep_row_from_data($data);
$pattern = sprintf('/(%1$s[^%1$s]*%1$s)/i', $this->enclosure);
$pattern = sprintf('/%1$s[^%1$s]*%1$s/i', $this->enclosure);
preg_match_all($pattern, $data, $matches);
/** @var array[] $matches */
foreach ($matches[0] as $match) {
if (empty($match) || (strpos($match, $this->enclosure) === false)) {
continue;
@@ -781,6 +785,9 @@ class Csv {
public function unparse($data = array(), $fields = array(), $append = FileProcessingModeEnum::MODE_FILE_OVERWRITE, $is_php = false, $delimiter = null) {
if (!is_array($data) || empty($data)) {
$data = &$this->data;
} else {
/** @noinspection ReferenceMismatchInspection */
$this->data = $data;
}
if (!is_array($fields) || empty($fields)) {
@@ -795,6 +802,7 @@ class Csv {
$entry = array();
// create heading
/** @noinspection ReferenceMismatchInspection */
$fieldOrder = $this->_validate_fields_for_unparse($fields);
if (!$fieldOrder && !empty($data)) {
$column_count = count($data[0]);
@@ -813,7 +821,7 @@ class Csv {
// create data
foreach ($data as $key => $row) {
foreach (array_keys($fieldOrder) as $index){
foreach (array_keys($fieldOrder) as $index) {
$cell_value = $row[$index];
$entry[] = $this->_enclose_value($cell_value, $delimiter);
}
@@ -829,19 +837,20 @@ class Csv {
return $string;
}
private function _validate_fields_for_unparse($fields){
// this is needed because sometime titles property is overwritten instead of using fields parameter!
$titlesOnParse = !empty($this->data) ? array_keys($this->data[0]) : array();
if (empty($fields)){
private function _validate_fields_for_unparse($fields) {
if (empty($fields)) {
$fields = $this->titles;
}
if (empty($fields)){
if (empty($fields)) {
return array();
}
// this is needed because sometime titles property is overwritten instead of using fields parameter!
$titlesOnParse = !empty($this->data) ? array_keys($this->data[0]) : array();
// both are identical, also in ordering
if (array_values($fields) === array_values($titlesOnParse)){
if (array_values($fields) === array_values($titlesOnParse)) {
return array_combine($fields, $fields);
}
@@ -859,7 +868,10 @@ class Csv {
// original titles are not given in fields. that is okay if count is okay.
if (count($fields) != count($titlesOnParse)) {
throw new \UnexpectedValueException('The specified fields do not match any titles and do not match column count.');
throw new \UnexpectedValueException(
"The specified fields do not match any titles and do not match column count.\n" .
"\$fields was " . print_r($fields, true) .
"\$titlesOnParse was " . print_r($titlesOnParse, true));
}
return array_combine($titlesOnParse, $fields);
@@ -1007,16 +1019,14 @@ class Csv {
$op = $capture[2];
$value = $capture[3];
if (preg_match('/^([\'\"]{1})(.*)([\'\"]{1})$/', $value, $capture)) {
if ($capture[1] == $capture[3]) {
$value = strtr($capture[2], array(
"\\n" => "\n",
"\\r" => "\r",
"\\t" => "\t",
));
if (preg_match('/^([\'\"]{1})(.*)([\'\"]{1})$/', $value, $capture) && $capture[1] == $capture[3]) {
$value = strtr($capture[2], array(
"\\n" => "\n",
"\\r" => "\r",
"\\t" => "\t",
));
$value = stripslashes($value);
}
$value = stripslashes($value);
}
if (array_key_exists($field, $row)) {

View File

@@ -64,7 +64,6 @@ class DataRowCountTest extends TestCase {
$this->assertEquals(3, $this->csv->getTotalDataRowCount());
}
public function testGetTotalRowCountSingleEnclosure() {
$this->csv->heading = false;
$this->csv->enclosure = "'";
@@ -73,4 +72,11 @@ class DataRowCountTest extends TestCase {
$this->assertEquals(3, $this->csv->getTotalDataRowCount());
}
public function testGetTotalRowCountSingleRow() {
$this->csv->heading = false;
$this->csv->enclosure = "'";
$sInput = "86545235689";
$this->csv->load_data($sInput);
$this->assertEquals(1, $this->csv->getTotalDataRowCount());
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace ParseCsv\tests\methods;
use ParseCsv\Csv;
use PHPUnit\Framework\TestCase;
class OutputTest extends TestCase {
/**
* @runInSeparateProcess because download.php uses header()
*/
public function testOutputWithFourParameters() {
$csv = new Csv();
$data = [0 => ['a', 'b', 'c'], 1 => ['d', 'e', 'f']];
$fields = ['col1', 'col2', 'col3'];
$output = $csv->output('test.csv', $data, $fields, ',');
$expected = "col1,col2,col3\ra,b,c\rd,e,f\r";
self::assertEquals($expected, $output);
}
}