Skip to content

Commit ade8ec1

Browse files
committed
add stream support to Reader/Csv
1 parent 4cca3ef commit ade8ec1

File tree

4 files changed

+67
-21
lines changed

4 files changed

+67
-21
lines changed

src/PhpSpreadsheet/Reader/BaseReader.php

+3-2
Original file line numberDiff line numberDiff line change
@@ -188,16 +188,17 @@ public function load($file, int $flags = 0): Spreadsheet
188188
*/
189189
protected function openFile($file): void
190190
{
191-
$fileHandle = false;
192191
if (is_string($file)) {
193192
$filename = $file;
194193
File::assertFile($file);
195194

196195
// Open file
197196
$fileHandle = fopen($file, 'rb');
198-
} else {
197+
} else if (is_resource($file)) {
199198
$filename = 'stream';
200199
$fileHandle = $file;
200+
} else {
201+
throw new ReaderException('invalid type for file. Only file path or a stream resource is allowed');
201202
}
202203
if ($fileHandle === false) {
203204
throw new ReaderException('Could not open file ' . $filename . ' for reading.');

src/PhpSpreadsheet/Reader/Csv.php

+29-19
Original file line numberDiff line numberDiff line change
@@ -276,20 +276,18 @@ public function loadSpreadsheetFromString(string $contents): Spreadsheet
276276
return $this->loadStringOrFile('data://text/plain,' . urlencode($contents), $spreadsheet, true);
277277
}
278278

279-
private function openFileOrMemory(string $filename): void
279+
private function openFileOrMemory($file): void
280280
{
281281
// Open file
282-
$fhandle = $this->canRead($filename);
283-
if (!$fhandle) {
284-
throw new Exception($filename . ' is an Invalid Spreadsheet file.');
282+
if (!$this->canRead($file)) {
283+
throw new Exception($file . ' is an Invalid Spreadsheet file.');
285284
}
286285
if ($this->inputEncoding === self::GUESS_ENCODING) {
287-
$this->inputEncoding = self::guessEncoding($filename, $this->fallbackEncoding);
286+
$this->inputEncoding = self::guessEncoding($file, $this->fallbackEncoding);
288287
}
289-
$this->openFile($filename);
288+
$this->openFile($file);
290289
if ($this->inputEncoding !== 'UTF-8') {
291-
fclose($this->fileHandle);
292-
$entireFile = file_get_contents($filename);
290+
$entireFile = stream_get_contents($this->fileHandle);
293291
$fileHandle = fopen('php://memory', 'r+b');
294292
if ($fileHandle !== false && $entireFile !== false) {
295293
$this->fileHandle = $fileHandle;
@@ -346,24 +344,26 @@ private function openDataUri(string $filename): void
346344
/**
347345
* Loads PhpSpreadsheet from file into PhpSpreadsheet instance.
348346
*/
349-
public function loadIntoExisting(string $filename, Spreadsheet $spreadsheet): Spreadsheet
347+
public function loadIntoExisting($file, Spreadsheet $spreadsheet): Spreadsheet
350348
{
351-
return $this->loadStringOrFile($filename, $spreadsheet, false);
349+
return $this->loadStringOrFile($file, $spreadsheet, false);
352350
}
353351

354352
/**
355353
* Loads PhpSpreadsheet from file into PhpSpreadsheet instance.
356354
*/
357-
private function loadStringOrFile(string $filename, Spreadsheet $spreadsheet, bool $dataUri): Spreadsheet
355+
private function loadStringOrFile($file, Spreadsheet $spreadsheet, bool $dataUri): Spreadsheet
358356
{
359357
// Deprecated in Php8.1
360358
$iniset = $this->setAutoDetect('1');
361359

362360
// Open file
363361
if ($dataUri) {
364-
$this->openDataUri($filename);
362+
$this->openDataUri($file);
363+
$filename = $file;
365364
} else {
366-
$this->openFileOrMemory($filename);
365+
$this->openFileOrMemory($file);
366+
$filename = 'escape';
367367
}
368368
$fileHandle = $this->fileHandle;
369369

@@ -548,23 +548,33 @@ public function canRead($file): bool
548548
return false;
549549
}
550550

551-
fclose($this->fileHandle);
551+
rewind($this->fileHandle);
552552

553-
// Trust file extension if any
554-
$extension = strtolower(pathinfo($file, PATHINFO_EXTENSION));
555-
if (in_array($extension, ['csv', 'tsv'])) {
556-
return true;
553+
if (is_string($file)) {
554+
// Trust file extension if any
555+
$extension = strtolower(pathinfo($file, PATHINFO_EXTENSION));
556+
if (in_array($extension, ['csv', 'tsv'])) {
557+
return true;
558+
}
557559
}
558560

559561
// Attempt to guess mimetype
560-
$type = mime_content_type($file);
562+
$type = mime_content_type($this->fileHandle);
561563
$supportedTypes = [
562564
'application/csv',
563565
'text/csv',
564566
'text/plain',
565567
'inode/x-empty',
566568
];
567569

570+
if (is_resource($file)) {
571+
// reading mime types from a stream causes sometimes different results
572+
$supportedTypes[] = 'application/x-empty';
573+
$supportedTypes[] = 'text/html';
574+
}
575+
576+
rewind($this->fileHandle);
577+
568578
return in_array($type, $supportedTypes, true);
569579
}
570580

tests/PhpSpreadsheetTests/Reader/Csv/CsvLoadFromStringTest.php

+18
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,22 @@ public function testLoadFromString(): void
2525
self::AssertSame('7 , 8', $sheet->getCell('A3')->getValue());
2626
self::AssertSame("12\n13", $sheet->getCell('B4')->getValue());
2727
}
28+
29+
public function testLoadFromStream(): void
30+
{
31+
$data = <<<EOF
32+
1,2,3
33+
4,2+3,6
34+
"7 , 8", 9, 10
35+
11,"12
36+
13",14
37+
EOF;
38+
$stream = fopen('data://text/plain;base64,' . base64_encode($data), 'r');
39+
$reader = new Csv();
40+
$spreadsheet = $reader->load($stream);
41+
$sheet = $spreadsheet->getActiveSheet();
42+
self::AssertSame('2+3', $sheet->getCell('B2')->getValue());
43+
self::AssertSame('7 , 8', $sheet->getCell('A3')->getValue());
44+
self::AssertSame("12\n13", $sheet->getCell('B4')->getValue());
45+
}
2846
}

tests/PhpSpreadsheetTests/Reader/Csv/CsvTest.php

+17
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,23 @@ public function testCanLoad(bool $expected, string $filename): void
9696
self::assertSame($expected, $reader->canRead($filename));
9797
}
9898

99+
/**
100+
* @dataProvider providerCanLoad
101+
*/
102+
public function testCanLoadFromStream(bool $expected, string $filename): void
103+
{
104+
$reader = new Csv();
105+
$stream = fopen('php://memory', 'r+');
106+
self::assertNotFalse($stream);
107+
108+
$contents = file_get_contents($filename);
109+
self::assertNotFalse($contents);
110+
fwrite($stream, $contents);
111+
rewind($stream);
112+
113+
self::assertSame($expected, $reader->canRead($stream));
114+
}
115+
99116
public static function providerCanLoad(): array
100117
{
101118
return [

0 commit comments

Comments
 (0)