All writers can write to stream

This commit is contained in:
Adrien Crivelli 2020-05-16 20:12:28 +09:00
parent 9cdbddf3bf
commit fcf7820467
No known key found for this signature in database
GPG Key ID: B182FD79DC6DE92E
10 changed files with 111 additions and 151 deletions

View File

@ -22,7 +22,6 @@ namespace PhpOffice\PhpSpreadsheet\Shared\OLE\PPS;
// //
use PhpOffice\PhpSpreadsheet\Shared\OLE; use PhpOffice\PhpSpreadsheet\Shared\OLE;
use PhpOffice\PhpSpreadsheet\Shared\OLE\PPS; use PhpOffice\PhpSpreadsheet\Shared\OLE\PPS;
use PhpOffice\PhpSpreadsheet\Writer\Exception as WriterException;
/** /**
* Class for creating Root PPS's for OLE containers. * Class for creating Root PPS's for OLE containers.
@ -33,23 +32,11 @@ use PhpOffice\PhpSpreadsheet\Writer\Exception as WriterException;
*/ */
class Root extends PPS class Root extends PPS
{ {
/**
* Directory for temporary files.
*
* @var string
*/
protected $tempDirectory;
/** /**
* @var resource * @var resource
*/ */
private $fileHandle; private $fileHandle;
/**
* @var string
*/
private $tempFilename;
/** /**
* @var int * @var int
*/ */
@ -67,8 +54,6 @@ class Root extends PPS
*/ */
public function __construct($time_1st, $time_2nd, $raChild) public function __construct($time_1st, $time_2nd, $raChild)
{ {
$this->tempDirectory = \PhpOffice\PhpSpreadsheet\Shared\File::sysGetTempDir();
parent::__construct(null, OLE::ascToUcs('Root Entry'), OLE::OLE_PPS_TYPE_ROOT, null, null, null, $time_1st, $time_2nd, null, $raChild); parent::__construct(null, OLE::ascToUcs('Root Entry'), OLE::OLE_PPS_TYPE_ROOT, null, null, null, $time_1st, $time_2nd, null, $raChild);
} }
@ -79,14 +64,14 @@ class Root extends PPS
* If a resource pointer to a stream created by fopen() is passed * If a resource pointer to a stream created by fopen() is passed
* it will be used, but you have to close such stream by yourself. * it will be used, but you have to close such stream by yourself.
* *
* @param resource|string $filename the name of the file or stream where to save the OLE container * @param resource $fileHandle the name of the file or stream where to save the OLE container
*
* @throws WriterException
* *
* @return bool true on success * @return bool true on success
*/ */
public function save($filename) public function save($fileHandle)
{ {
$this->fileHandle = $fileHandle;
// Initial Setting for saving // Initial Setting for saving
$this->bigBlockSize = pow( $this->bigBlockSize = pow(
2, 2,
@ -97,23 +82,6 @@ class Root extends PPS
(isset($this->smallBlockSize)) ? self::adjust2($this->smallBlockSize) : 6 (isset($this->smallBlockSize)) ? self::adjust2($this->smallBlockSize) : 6
); );
if (is_resource($filename)) {
$this->fileHandle = $filename;
} elseif ($filename == '-' || $filename == '') {
if ($this->tempDirectory === null) {
$this->tempDirectory = \PhpOffice\PhpSpreadsheet\Shared\File::sysGetTempDir();
}
$this->tempFilename = tempnam($this->tempDirectory, 'OLE_PPS_Root');
$this->fileHandle = fopen($this->tempFilename, 'w+b');
if ($this->fileHandle == false) {
throw new WriterException("Can't create temporary file.");
}
} else {
$this->fileHandle = fopen($filename, 'wb');
}
if ($this->fileHandle == false) {
throw new WriterException("Can't open $filename. It may be in use or protected.");
}
// Make an array of PPS's (for Save) // Make an array of PPS's (for Save)
$aList = []; $aList = [];
PPS::_savePpsSetPnt($aList, [$this]); PPS::_savePpsSetPnt($aList, [$this]);
@ -132,10 +100,6 @@ class Root extends PPS
// Write Big Block Depot and BDList and Adding Header informations // Write Big Block Depot and BDList and Adding Header informations
$this->_saveBbd($iSBDcnt, $iBBcnt, $iPPScnt); $this->_saveBbd($iSBDcnt, $iBBcnt, $iPPScnt);
if (!is_resource($filename)) {
fclose($this->fileHandle);
}
return true; return true;
} }

View File

@ -35,6 +35,16 @@ abstract class BaseWriter implements IWriter
*/ */
private $diskCachingDirectory = './'; private $diskCachingDirectory = './';
/**
* @var resource
*/
protected $fileHandle;
/**
* @var bool
*/
private $shouldCloseFile;
public function getIncludeCharts() public function getIncludeCharts()
{ {
return $this->includeCharts; return $this->includeCharts;
@ -83,4 +93,39 @@ abstract class BaseWriter implements IWriter
{ {
return $this->diskCachingDirectory; return $this->diskCachingDirectory;
} }
/**
* Open file handle.
*
* @param resource|string $filename
*/
public function openFileHandle($filename): void
{
if (is_resource($filename)) {
$this->fileHandle = $filename;
$this->shouldCloseFile = false;
return;
}
$fileHandle = fopen($filename, 'wb+');
if ($fileHandle === false) {
throw new Exception('Could not open file ' . $filename . ' for writing.');
}
$this->fileHandle = $fileHandle;
$this->shouldCloseFile = true;
}
/**
* Close file handle only we opened it ourselves.
*/
protected function maybeCloseFileHandle(): void
{
if ($this->shouldCloseFile) {
if (!fclose($this->fileHandle)) {
throw new Exception('Could not close file after writing.');
}
}
}
} }

View File

@ -131,11 +131,6 @@ class Csv extends BaseWriter
$this->writeLine($fileHandle, $cellsArray[0]); $this->writeLine($fileHandle, $cellsArray[0]);
} }
// Close file
rewind($fileHandle);
fclose($fileHandle);
Calculation::setArrayReturnType($saveArrayReturnType); Calculation::setArrayReturnType($saveArrayReturnType);
Calculation::getInstance($this->spreadsheet)->getDebugLog()->setWriteDebugLog($saveDebugLog); Calculation::getInstance($this->spreadsheet)->getDebugLog()->setWriteDebugLog($saveDebugLog);
} }

View File

@ -164,32 +164,23 @@ class Html extends BaseWriter
$this->buildCSS(!$this->useInlineCss); $this->buildCSS(!$this->useInlineCss);
// Open file // Open file
if (is_resource($pFilename)) { $this->openFileHandle($pFilename);
$fileHandle = $pFilename;
} else {
$fileHandle = fopen($pFilename, 'wb+');
}
if ($fileHandle === false) {
throw new WriterException("Could not open file $pFilename for writing.");
}
// Write headers // Write headers
fwrite($fileHandle, $this->generateHTMLHeader(!$this->useInlineCss)); fwrite($this->fileHandle, $this->generateHTMLHeader(!$this->useInlineCss));
// Write navigation (tabs) // Write navigation (tabs)
if ((!$this->isPdf) && ($this->generateSheetNavigationBlock)) { if ((!$this->isPdf) && ($this->generateSheetNavigationBlock)) {
fwrite($fileHandle, $this->generateNavigation()); fwrite($this->fileHandle, $this->generateNavigation());
} }
// Write data // Write data
fwrite($fileHandle, $this->generateSheetData()); fwrite($this->fileHandle, $this->generateSheetData());
// Write footer // Write footer
fwrite($fileHandle, $this->generateHTMLFooter()); fwrite($this->fileHandle, $this->generateHTMLFooter());
// Close file $this->maybeCloseFileHandle();
fclose($fileHandle);
Calculation::setArrayReturnType($saveArrayReturnType); Calculation::setArrayReturnType($saveArrayReturnType);
Calculation::getInstance($this->spreadsheet)->getDebugLog()->setWriteDebugLog($saveDebugLog); Calculation::getInstance($this->spreadsheet)->getDebugLog()->setWriteDebugLog($saveDebugLog);

View File

@ -32,11 +32,6 @@ class Ods extends BaseWriter
*/ */
private $spreadSheet; private $spreadSheet;
/**
* @var resource
*/
private $fileHandle;
/** /**
* Create a new Ods. * Create a new Ods.
* *
@ -93,25 +88,7 @@ class Ods extends BaseWriter
// garbage collect // garbage collect
$this->spreadSheet->garbageCollect(); $this->spreadSheet->garbageCollect();
$originalFilename = $pFilename; $this->openFileHandle($pFilename);
if (is_resource($pFilename)) {
$this->fileHandle = $pFilename;
} else {
// If $pFilename is php://output or php://stdout, make it a temporary file...
if (in_array(strtolower($pFilename), ['php://output', 'php://stdout'], true)) {
$pFilename = @tempnam(File::sysGetTempDir(), 'phpxltmp');
if ($pFilename === '') {
$pFilename = $originalFilename;
}
}
$fileHandle = fopen($pFilename, 'wb+');
if ($fileHandle === false) {
throw new WriterException('Could not open file ' . $pFilename . ' for writing.');
}
$this->fileHandle = $fileHandle;
}
$zip = $this->createZip(); $zip = $this->createZip();
@ -130,23 +107,7 @@ class Ods extends BaseWriter
throw new WriterException('Could not close resource.'); throw new WriterException('Could not close resource.');
} }
rewind($this->fileHandle); $this->maybeCloseFileHandle();
// If a temporary file was used, copy it to the correct file stream
if ($originalFilename !== $pFilename) {
$destinationFileHandle = fopen($originalFilename, 'wb+');
if (!is_resource($destinationFileHandle)) {
throw new WriterException("Could not open resource $originalFilename for writing.");
}
if (stream_copy_to_stream($this->fileHandle, $destinationFileHandle) === false) {
throw new WriterException("Could not copy temporary zip file $pFilename to $originalFilename.");
}
if (is_string($pFilename) && !unlink($pFilename)) {
throw new WriterException('Could not unlink temporary zip file.');
}
}
} }
/** /**

View File

@ -255,17 +255,14 @@ abstract class Pdf extends Html
Calculation::setArrayReturnType(Calculation::RETURN_ARRAY_AS_VALUE); Calculation::setArrayReturnType(Calculation::RETURN_ARRAY_AS_VALUE);
// Open file // Open file
$fileHandle = fopen($pFilename, 'w'); $this->openFileHandle($pFilename);
if ($fileHandle === false) {
throw new WriterException("Could not open file $pFilename for writing.");
}
// Set PDF // Set PDF
$this->isPdf = true; $this->isPdf = true;
// Build CSS // Build CSS
$this->buildCSS(true); $this->buildCSS(true);
return $fileHandle; return $this->fileHandle;
} }
/** /**
@ -275,8 +272,7 @@ abstract class Pdf extends Html
*/ */
protected function restoreStateAfterSave($fileHandle) protected function restoreStateAfterSave($fileHandle)
{ {
// Close file $this->maybeCloseFileHandle();
fclose($fileHandle);
Calculation::setArrayReturnType($this->saveArrayReturnType); Calculation::setArrayReturnType($this->saveArrayReturnType);
} }

View File

@ -65,7 +65,7 @@ class Mpdf extends Pdf
} }
// Create PDF // Create PDF
$config = ['tempDir' => $this->tempDir]; $config = ['tempDir' => $this->tempDir . '/mpdf'];
$pdf = $this->createExternalWriterInstance($config); $pdf = $this->createExternalWriterInstance($config);
$ortmp = $orientation; $ortmp = $orientation;
$pdf->_setPageSize(strtoupper($paperSize), $ortmp); $pdf->_setPageSize(strtoupper($paperSize), $ortmp);

View File

@ -221,7 +221,9 @@ class Xls extends BaseWriter
$root = new Root(time(), time(), $arrRootData); $root = new Root(time(), time(), $arrRootData);
// save the OLE file // save the OLE file
$root->save($pFilename); $this->openFileHandle($pFilename);
$root->save($this->fileHandle);
$this->maybeCloseFileHandle();
Functions::setReturnDateType($saveDateReturnType); Functions::setReturnDateType($saveDateReturnType);
Calculation::getInstance($this->spreadsheet)->getDebugLog()->setWriteDebugLog($saveDebugLog); Calculation::getInstance($this->spreadsheet)->getDebugLog()->setWriteDebugLog($saveDebugLog);

View File

@ -107,11 +107,6 @@ class Xlsx extends BaseWriter
*/ */
private $drawingHashTable; private $drawingHashTable;
/**
* @var resource
*/
private $fileHandle;
/** /**
* Create a new Xlsx Writer. * Create a new Xlsx Writer.
* *
@ -184,25 +179,7 @@ class Xlsx extends BaseWriter
// garbage collect // garbage collect
$this->spreadSheet->garbageCollect(); $this->spreadSheet->garbageCollect();
$originalFilename = $pFilename; $this->openFileHandle($pFilename);
if (is_resource($pFilename)) {
$this->fileHandle = $pFilename;
} else {
// If $pFilename is php://output or php://stdout, make it a temporary file...
if (in_array(strtolower($pFilename), ['php://output', 'php://stdout'], true)) {
$pFilename = @tempnam(File::sysGetTempDir(), 'phpxltmp');
if ($pFilename === '') {
$pFilename = $originalFilename;
}
}
$fileHandle = fopen($pFilename, 'wb+');
if ($fileHandle === false) {
throw new WriterException('Could not open file ' . $pFilename . ' for writing.');
}
$this->fileHandle = $fileHandle;
}
$saveDebugLog = Calculation::getInstance($this->spreadSheet)->getDebugLog()->getWriteDebugLog(); $saveDebugLog = Calculation::getInstance($this->spreadSheet)->getDebugLog()->getWriteDebugLog();
Calculation::getInstance($this->spreadSheet)->getDebugLog()->setWriteDebugLog(false); Calculation::getInstance($this->spreadSheet)->getDebugLog()->setWriteDebugLog(false);
@ -425,23 +402,7 @@ class Xlsx extends BaseWriter
throw new WriterException('Could not close resource.'); throw new WriterException('Could not close resource.');
} }
rewind($this->fileHandle); $this->maybeCloseFileHandle();
// If a temporary file was used, copy it to the correct file stream
if ($originalFilename !== $pFilename) {
$destinationFileHandle = fopen($originalFilename, 'wb+');
if (!is_resource($destinationFileHandle)) {
throw new WriterException("Could not open resource $originalFilename for writing.");
}
if (stream_copy_to_stream($this->fileHandle, $destinationFileHandle) === false) {
throw new WriterException("Could not copy temporary zip file $pFilename to $originalFilename.");
}
if (is_string($pFilename) && !unlink($pFilename)) {
throw new WriterException('Could not unlink temporary zip file.');
}
}
} else { } else {
throw new WriterException('PhpSpreadsheet object unassigned.'); throw new WriterException('PhpSpreadsheet object unassigned.');
} }

View File

@ -0,0 +1,45 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Functional;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PHPUnit\Framework\TestCase;
class StreamTest extends TestCase
{
public function providerFormats(): array
{
return [
['Xls'],
['Xlsx'],
['Ods'],
['Csv'],
['Html'],
['Tcpdf'],
['Dompdf'],
['Mpdf'],
];
}
/**
* @dataProvider providerFormats
*
* @param string $format
*/
public function testAllWritersCanWriteToStream(string $format): void
{
$spreadsheet = new Spreadsheet();
$spreadsheet->getActiveSheet()->setCellValue('A1', 'foo');
$writer = IOFactory::createWriter($spreadsheet, $format);
$stream = fopen('php://memory', 'wb+');
self::assertSame(0, fstat($stream)['size']);
$writer->save($stream);
self::assertIsResource($stream, 'should not close the stream for further usage out of PhpSpreadsheet');
self::assertGreaterThan(0, fstat($stream)['size'], 'something should have been written to the stream');
self::assertGreaterThan(0, ftell($stream), 'should not be rewinded, because not all streams support it');
}
}