diff --git a/composer.json b/composer.json index c7513cc79..45e1390df 100644 --- a/composer.json +++ b/composer.json @@ -25,7 +25,7 @@ "bramus/monolog-colored-line-formatter": "~2.0", "composer/installers": "~1.0", "embed/embed": "^3.0", - "league/csv": "^8", + "league/csv": "^8 || ^9", "league/flysystem": "~1.0.12", "m1/env": "^2.1", "monolog/monolog": "~1.11", @@ -54,7 +54,7 @@ }, "require-dev": { "phpunit/phpunit": "^5.7", - "sminnee/phpunit-mock-objects": "^3.4.5", + "sminnee/phpunit-mock-objects": "^3.4.9", "silverstripe/versioned": "^1", "squizlabs/php_codesniffer": "^3.5" }, diff --git a/src/Dev/CsvBulkLoader.php b/src/Dev/CsvBulkLoader.php index 5a3a0a56c..f90f76eca 100644 --- a/src/Dev/CsvBulkLoader.php +++ b/src/Dev/CsvBulkLoader.php @@ -2,6 +2,7 @@ namespace SilverStripe\Dev; +use League\Csv\MapIterator; use League\Csv\Reader; use SilverStripe\Control\Director; use SilverStripe\ORM\DataObject; @@ -76,9 +77,16 @@ class CsvBulkLoader extends BulkLoader $filepath = Director::getAbsFile($filepath); $csvReader = Reader::createFromPath($filepath, 'r'); $csvReader->setDelimiter($this->delimiter); - $csvReader->stripBom(true); - $tabExtractor = function ($row, $rowOffset, $iterator) { + // league/csv 9 + if (method_exists($csvReader, 'skipInputBOM')) { + $csvReader->skipInputBOM(); + // league/csv 8 + } else { + $csvReader->stripBom(true); + } + + $tabExtractor = function ($row, $rowOffset) { foreach ($row as &$item) { // [SS-2017-007] Ensure all cells with leading tab and then [@=+] have the tab removed on import if (preg_match("/^\t[\-@=\+]+.*/", $item)) { @@ -90,8 +98,9 @@ class CsvBulkLoader extends BulkLoader if ($this->columnMap) { $headerMap = $this->getNormalisedColumnMap(); - $remapper = function ($row, $rowOffset, $iterator) use ($headerMap, $tabExtractor) { - $row = $tabExtractor($row, $rowOffset, $iterator); + + $remapper = function ($row, $rowOffset) use ($headerMap, $tabExtractor) { + $row = $tabExtractor($row, $rowOffset); foreach ($headerMap as $column => $renamedColumn) { if ($column == $renamedColumn) { continue; @@ -110,9 +119,18 @@ class CsvBulkLoader extends BulkLoader } if ($this->hasHeaderRow) { - $rows = $csvReader->fetchAssoc(0, $remapper); + if (method_exists($csvReader, 'fetchAssoc')) { + $rows = $csvReader->fetchAssoc(0, $remapper); + } else { + $csvReader->setHeaderOffset(0); + $rows = new MapIterator($csvReader->getRecords(), $remapper); + } } elseif ($this->columnMap) { - $rows = $csvReader->fetchAssoc($headerMap, $remapper); + if (method_exists($csvReader, 'fetchAssoc')) { + $rows = $csvReader->fetchAssoc($headerMap, $remapper); + } else { + $rows = new MapIterator($csvReader->getRecords($headerMap), $remapper); + } } foreach ($rows as $row) { diff --git a/src/Forms/GridField/GridFieldExportButton.php b/src/Forms/GridField/GridFieldExportButton.php index 02b6a6c87..8d5b3b427 100644 --- a/src/Forms/GridField/GridFieldExportButton.php +++ b/src/Forms/GridField/GridFieldExportButton.php @@ -257,6 +257,10 @@ class GridFieldExportButton implements GridField_HTMLProvider, GridField_ActionP } } + if (method_exists($csvWriter, 'getContent')) { + return $csvWriter->getContent(); + } + return (string)$csvWriter; } diff --git a/tests/php/Forms/GridField/GridFieldExportButtonTest.php b/tests/php/Forms/GridField/GridFieldExportButtonTest.php index 5738beb98..222d5f657 100644 --- a/tests/php/Forms/GridField/GridFieldExportButtonTest.php +++ b/tests/php/Forms/GridField/GridFieldExportButtonTest.php @@ -55,7 +55,7 @@ class GridFieldExportButtonTest extends SapphireTest $config = GridFieldConfig::create()->addComponent(new GridFieldExportButton()); $gridField = new GridField('testfield', 'testfield', $list, $config); - $csvReader = Reader::createFromString($button->generateExportFileData($gridField)); + $csvReader = $this->createReader($button->generateExportFileData($gridField)); $bom = $csvReader->getInputBOM(); $this->assertEquals( @@ -69,7 +69,7 @@ class GridFieldExportButtonTest extends SapphireTest $button = new GridFieldExportButton(); $button->setExportColumns(['Name' => 'My Name']); - $csvReader = Reader::createFromString($button->generateExportFileData($this->gridField)); + $csvReader = $this->createReader($button->generateExportFileData($this->gridField)); $bom = $csvReader->getInputBOM(); $this->assertEquals( @@ -89,7 +89,7 @@ class GridFieldExportButtonTest extends SapphireTest $button = new GridFieldExportButton(); $button->setExportColumns(['Name' => 'My Name']); - $csvReader = Reader::createFromString($button->generateExportFileData($this->gridField)); + $csvReader = $this->createReader($button->generateExportFileData($this->gridField)); $bom = $csvReader->getInputBOM(); $this->assertEquals( @@ -108,7 +108,7 @@ class GridFieldExportButtonTest extends SapphireTest } ]); - $csvReader = Reader::createFromString($button->generateExportFileData($this->gridField)); + $csvReader = $this->createReader($button->generateExportFileData($this->gridField)); $bom = $csvReader->getInputBOM(); $this->assertEquals( @@ -125,7 +125,7 @@ class GridFieldExportButtonTest extends SapphireTest 'City' => 'strtolower', ]); - $csvReader = Reader::createFromString($button->generateExportFileData($this->gridField)); + $csvReader = $this->createReader($button->generateExportFileData($this->gridField)); $bom = $csvReader->getInputBOM(); $this->assertEquals( @@ -143,7 +143,7 @@ class GridFieldExportButtonTest extends SapphireTest ]); $button->setCsvHasHeader(false); - $csvReader = Reader::createFromString($button->generateExportFileData($this->gridField)); + $csvReader = $this->createReader($button->generateExportFileData($this->gridField)); $bom = $csvReader->getInputBOM(); $this->assertEquals( @@ -167,7 +167,7 @@ class GridFieldExportButtonTest extends SapphireTest $exportData = $button->generateExportFileData($this->gridField); - $csvReader = Reader::createFromString($exportData); + $csvReader = $this->createReader($exportData); $bom = $csvReader->getInputBOM(); $this->assertEquals( @@ -183,7 +183,7 @@ class GridFieldExportButtonTest extends SapphireTest 'RugbyTeamNumber' => 'Rugby Team Number' ]); - $csvReader = Reader::createFromString($button->generateExportFileData($this->gridField)); + $csvReader = $this->createReader($button->generateExportFileData($this->gridField)); $bom = $csvReader->getInputBOM(); $this->assertEquals( @@ -191,4 +191,16 @@ class GridFieldExportButtonTest extends SapphireTest (string) $csvReader ); } + + protected function createReader($string) + { + $reader = Reader::createFromString($string); + + // Explicitly set the output BOM in league/csv 9 + if (method_exists($reader, 'getContent')) { + $reader->setOutputBOM(Reader::BOM_UTF8); + } + + return $reader; + } }