Skip to content
Snippets Groups Projects
Commit 974344ac authored by Patrick Broens's avatar Patrick Broens
Browse files

[BUGFIX] CsvUtility method csvToArray does not handle enclosures right

When we have a csv string with enclosures, the string is sometimes not
correctly transformed into a multidimensional array. The cause of this
is the first str_getcsv() function, responsible for exploding the rows.
It returns rows where the first column does not have an enclosure
anymore.

This patch fixes this by using fgetcsv(). A temporary file is created
and written to the typo3temp folder and then passed to fgetcsv(). This
way we get a correct multidimensional array from the csv string.

Change-Id: I042c1ab326ef2b5f5cdd2b1455da76ccefd5ae36
Resolves: #68127
Releases: master
Reviewed-on: http://review.typo3.org/41211


Reviewed-by: default avatarSascha Egerer <sascha@sascha-egerer.de>
Tested-by: default avatarSascha Egerer <sascha@sascha-egerer.de>
Reviewed-by: default avatarChristian Kuhn <lolli@schwarzbu.ch>
Tested-by: default avatarChristian Kuhn <lolli@schwarzbu.ch>
Reviewed-by: default avatarPatrick Broens <patrick@patrickbroens.nl>
Tested-by: default avatarPatrick Broens <patrick@patrickbroens.nl>
parent 5fd24499
Branches
Tags
No related merge requests found
...@@ -22,26 +22,26 @@ class CsvUtility { ...@@ -22,26 +22,26 @@ class CsvUtility {
/** /**
* Convert a string, formatted as CSV, into an multidimensional array * Convert a string, formatted as CSV, into an multidimensional array
* *
* This cannot be done by str_getcsv, since it's impossible to handle enclosed cells with a line feed in it
*
* @param string $input The CSV input * @param string $input The CSV input
* @param string $fieldDelimiter The field delimiter * @param string $fieldDelimiter The field delimiter
* @param string $fieldEnclosure The field enclosure * @param string $fieldEnclosure The field enclosure
* @param string $rowDelimiter The row delimiter
* @param int $maximumColumns The maximum amount of columns * @param int $maximumColumns The maximum amount of columns
* @return array * @return array
*/ */
static public function csvToArray($input, $fieldDelimiter = ',', $fieldEnclosure = '"', $rowDelimiter = LF, $maximumColumns = 0) { static public function csvToArray($input, $fieldDelimiter = ',', $fieldEnclosure = '"', $maximumColumns = 0) {
$multiArray = array(); $multiArray = array();
$maximumCellCount = 0; $maximumCellCount = 0;
// explode() would not work with enclosed newlines if (($handle = fopen('php://memory', 'r+')) !== FALSE) {
$rows = str_getcsv($input, $rowDelimiter); fwrite($handle, $input);
rewind($handle);
foreach ($rows as $row) { while (($cells = fgetcsv($handle, 0, $fieldDelimiter, $fieldEnclosure)) !== FALSE) {
$cells = str_getcsv($row, $fieldDelimiter, $fieldEnclosure); $maximumCellCount = max(count($cells), $maximumCellCount);
$multiArray[] = $cells;
$maximumCellCount = max(count($cells), $maximumCellCount); }
fclose($handle);
$multiArray[] = $cells;
} }
if ($maximumColumns > $maximumCellCount) { if ($maximumColumns > $maximumCellCount) {
...@@ -66,4 +66,4 @@ class CsvUtility { ...@@ -66,4 +66,4 @@ class CsvUtility {
return $multiArray; return $multiArray;
} }
} }
\ No newline at end of file
...@@ -29,8 +29,7 @@ class CsvUtilityTest extends UnitTestCase { ...@@ -29,8 +29,7 @@ class CsvUtilityTest extends UnitTestCase {
'Valid data' => array( 'Valid data' => array(
'input' => 'Column A, Column B, Column C' . LF . 'Value, Value2, Value 3', 'input' => 'Column A, Column B, Column C' . LF . 'Value, Value2, Value 3',
'fieldDelimiter' => ',', 'fieldDelimiter' => ',',
'fieldEnclosure' => '', 'fieldEnclosure' => '"',
'rowDelimiter' => LF,
'maximumColumns' => 0, 'maximumColumns' => 0,
'expectedResult' => array( 'expectedResult' => array(
array('Column A', ' Column B', ' Column C'), array('Column A', ' Column B', ' Column C'),
...@@ -42,7 +41,6 @@ class CsvUtilityTest extends UnitTestCase { ...@@ -42,7 +41,6 @@ class CsvUtilityTest extends UnitTestCase {
'input' => '"Column A", "Column B", "Column C"' . LF . '"Value", "Value2", "Value 3"', 'input' => '"Column A", "Column B", "Column C"' . LF . '"Value", "Value2", "Value 3"',
'fieldDelimiter' => ',', 'fieldDelimiter' => ',',
'fieldEnclosure' => '"', 'fieldEnclosure' => '"',
'rowDelimiter' => LF,
'maximumColumns' => 0, 'maximumColumns' => 0,
'expectedResult' => array( 'expectedResult' => array(
array('Column A', 'Column B', 'Column C'), array('Column A', 'Column B', 'Column C'),
...@@ -54,7 +52,6 @@ class CsvUtilityTest extends UnitTestCase { ...@@ -54,7 +52,6 @@ class CsvUtilityTest extends UnitTestCase {
'input' => '"Column A"; "Column B"; "Column C"' . LF . '"Value"; "Value2"; "Value 3"', 'input' => '"Column A"; "Column B"; "Column C"' . LF . '"Value"; "Value2"; "Value 3"',
'fieldDelimiter' => ';', 'fieldDelimiter' => ';',
'fieldEnclosure' => '"', 'fieldEnclosure' => '"',
'rowDelimiter' => LF,
'maximumColumns' => 0, 'maximumColumns' => 0,
'expectedResult' => array( 'expectedResult' => array(
array('Column A', 'Column B', 'Column C'), array('Column A', 'Column B', 'Column C'),
...@@ -66,7 +63,6 @@ class CsvUtilityTest extends UnitTestCase { ...@@ -66,7 +63,6 @@ class CsvUtilityTest extends UnitTestCase {
'input' => '"Column A"; "Column B"; "Column C"; "Column D"' . LF . '"Value"; "Value2"; "Value 3"', 'input' => '"Column A"; "Column B"; "Column C"; "Column D"' . LF . '"Value"; "Value2"; "Value 3"',
'fieldDelimiter' => ';', 'fieldDelimiter' => ';',
'fieldEnclosure' => '"', 'fieldEnclosure' => '"',
'rowDelimiter' => LF,
'maximumColumns' => 2, 'maximumColumns' => 2,
'expectedResult' => array( 'expectedResult' => array(
array('Column A', 'Column B'), array('Column A', 'Column B'),
...@@ -78,24 +74,11 @@ class CsvUtilityTest extends UnitTestCase { ...@@ -78,24 +74,11 @@ class CsvUtilityTest extends UnitTestCase {
'input' => '"Column A", "Column B", "Column C"' . LF . '"Value", "Value2", "Value 3"', 'input' => '"Column A", "Column B", "Column C"' . LF . '"Value", "Value2", "Value 3"',
'fieldDelimiter' => ';', 'fieldDelimiter' => ';',
'fieldEnclosure' => '"', 'fieldEnclosure' => '"',
'rowDelimiter' => LF,
'maximumColumns' => 0, 'maximumColumns' => 0,
'expectedResult' => array( 'expectedResult' => array(
array('Column A, "Column B", "Column C"'), array('Column A, "Column B", "Column C"'),
array('Value, "Value2", "Value 3"') array('Value, "Value2", "Value 3"')
) )
),
'Data with comma as field delimiter and semicolons as row delimiter' => array(
'input' => '"Column A", "Column B", "Column C";"Value", "Value2", "Value 3"',
'fieldDelimiter' => ',',
'fieldEnclosure' => '"',
'rowDelimiter' => ';',
'maximumColumns' => 0,
'expectedResult' => array(
array('Column A', 'Column B', 'Column C'),
array('Value', 'Value2', 'Value 3')
)
) )
); );
} }
...@@ -104,7 +87,7 @@ class CsvUtilityTest extends UnitTestCase { ...@@ -104,7 +87,7 @@ class CsvUtilityTest extends UnitTestCase {
* @dataProvider csvToArrayDataProvider * @dataProvider csvToArrayDataProvider
* @test * @test
*/ */
public function csvToArraySplitsAsExpected($input, $fieldDelimiter, $fieldEnclosure, $rowDelimiter, $maximumColumns, $expectedResult) { public function csvToArraySplitsAsExpected($input, $fieldDelimiter, $fieldEnclosure, $maximumColumns, $expectedResult) {
$this->assertEquals($expectedResult, CsvUtility::csvToArray($input, $fieldDelimiter, $fieldEnclosure, $rowDelimiter, $maximumColumns)); $this->assertEquals($expectedResult, CsvUtility::csvToArray($input, $fieldDelimiter, $fieldEnclosure, $maximumColumns));
} }
} }
\ No newline at end of file
...@@ -91,14 +91,10 @@ class CommaSeparatedValueProcessor implements DataProcessorInterface { ...@@ -91,14 +91,10 @@ class CommaSeparatedValueProcessor implements DataProcessorInterface {
// Set the field enclosure which is " by default // Set the field enclosure which is " by default
$fieldEnclosure = $cObj->stdWrapValue('fieldEnclosure', $processorConfiguration, '"'); $fieldEnclosure = $cObj->stdWrapValue('fieldEnclosure', $processorConfiguration, '"');
// Set the row delimiter which is "LF" by default
$rowDelimiter = $cObj->stdWrapValue('rowDelimiter', $processorConfiguration, LF);
$processedData[$targetVariableName] = CsvUtility::csvToArray( $processedData[$targetVariableName] = CsvUtility::csvToArray(
$originalValue, $originalValue,
$fieldDelimiter, $fieldDelimiter,
$fieldEnclosure, $fieldEnclosure,
$rowDelimiter,
(int)$maximumColumns (int)$maximumColumns
); );
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment