From 35530502d2f6d7d0966c731e7ae546a4f99bec81 Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Sat, 7 May 2022 08:24:53 -0700 Subject: [PATCH] Allow Csv Reader to Treat String as Contents of File (#2792) See #1285, which went stale and was closed, but recently received some positive feeback. It seems easy to implement, and the only other plaintext file format, Html, allows loading from a string. Those two reasons combined suggest that we should do it. --- docs/topics/reading-and-writing-to-file.md | 9 +++- src/PhpSpreadsheet/Reader/Csv.php | 45 +++++++++++++++++-- .../Reader/Csv/CsvLoadFromStringTest.php | 26 +++++++++++ 3 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 tests/PhpSpreadsheetTests/Reader/Csv/CsvLoadFromStringTest.php diff --git a/docs/topics/reading-and-writing-to-file.md b/docs/topics/reading-and-writing-to-file.md index df8ed7c2..19928f04 100644 --- a/docs/topics/reading-and-writing-to-file.md +++ b/docs/topics/reading-and-writing-to-file.md @@ -436,7 +436,14 @@ You can read a .csv file using the following code: ```php $reader = new \PhpOffice\PhpSpreadsheet\Reader\Csv(); -$spreadsheet = $reader->load("sample.csv"); +$spreadsheet = $reader->load('sample.csv'); +``` + +You can also treat a string as if it were the contents of a CSV file as follows: + +```php +$reader = new \PhpOffice\PhpSpreadsheet\Reader\Csv(); +$spreadsheet = $reader->loadSpreadsheetFromString($data); ``` #### Setting CSV options diff --git a/src/PhpSpreadsheet/Reader/Csv.php b/src/PhpSpreadsheet/Reader/Csv.php index e894e9a4..b604ceef 100644 --- a/src/PhpSpreadsheet/Reader/Csv.php +++ b/src/PhpSpreadsheet/Reader/Csv.php @@ -265,6 +265,18 @@ class Csv extends BaseReader return $this->loadIntoExisting($filename, $spreadsheet); } + /** + * Loads Spreadsheet from string. + */ + public function loadSpreadsheetFromString(string $contents): Spreadsheet + { + // Create new Spreadsheet + $spreadsheet = new Spreadsheet(); + + // Load into this instance + return $this->loadStringOrFile('data://text/plain,' . urlencode($contents), $spreadsheet, true); + } + private function openFileOrMemory(string $filename): void { // Open file @@ -314,16 +326,43 @@ class Csv extends BaseReader $this->preserveNumericFormatting = $preserveNumericFormatting; } + /** + * Open data uri for reading. + */ + private function openDataUri(string $filename): void + { + $fileHandle = fopen($filename, 'rb'); + if ($fileHandle === false) { + // @codeCoverageIgnoreStart + throw new ReaderException('Could not open file ' . $filename . ' for reading.'); + // @codeCoverageIgnoreEnd + } + + $this->fileHandle = $fileHandle; + } + /** * Loads PhpSpreadsheet from file into PhpSpreadsheet instance. */ public function loadIntoExisting(string $filename, Spreadsheet $spreadsheet): Spreadsheet + { + return $this->loadStringOrFile($filename, $spreadsheet, false); + } + + /** + * Loads PhpSpreadsheet from file into PhpSpreadsheet instance. + */ + private function loadStringOrFile(string $filename, Spreadsheet $spreadsheet, bool $dataUri): Spreadsheet { // Deprecated in Php8.1 $iniset = $this->setAutoDetect('1'); // Open file - $this->openFileOrMemory($filename); + if ($dataUri) { + $this->openDataUri($filename); + } else { + $this->openFileOrMemory($filename); + } $fileHandle = $this->fileHandle; // Skip BOM, if any @@ -395,8 +434,8 @@ class Csv extends BaseReader } elseif (strcasecmp(Calculation::getFALSE(), $rowDatum) === 0 || strcasecmp('false', $rowDatum) === 0) { $rowDatum = false; } - } elseif ($rowDatum === null) { - $rowDatum = ''; + } else { + $rowDatum = $rowDatum ?? ''; } } diff --git a/tests/PhpSpreadsheetTests/Reader/Csv/CsvLoadFromStringTest.php b/tests/PhpSpreadsheetTests/Reader/Csv/CsvLoadFromStringTest.php new file mode 100644 index 00000000..fda1f723 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Reader/Csv/CsvLoadFromStringTest.php @@ -0,0 +1,26 @@ +loadSpreadsheetFromString($data); + $sheet = $spreadsheet->getActiveSheet(); + self::AssertSame('2+3', $sheet->getCell('B2')->getValue()); + self::AssertSame('7 , 8', $sheet->getCell('A3')->getValue()); + self::AssertSame("12\n13", $sheet->getCell('B4')->getValue()); + } +}