Merge b864f42a613f5c2f076ebd861c6ed7a2bcd64f6e into f7a682dbd6d985a8e4710f3f8fd8bfb4890e8692
This commit is contained in:
commit
287ca43da2
@ -58,8 +58,8 @@ class XLSX implements EscaperInterface
|
|||||||
*/
|
*/
|
||||||
protected function getControlCharactersEscapingMap()
|
protected function getControlCharactersEscapingMap()
|
||||||
{
|
{
|
||||||
$controlCharactersEscapingMap = [];
|
$controlCharactersEscapingMap = array();
|
||||||
$whitelistedControlCharacters = ["\t", "\r", "\n"];
|
$whitelistedControlCharacters = array("\t", "\r", "\n");
|
||||||
|
|
||||||
// control characters values are from 0 to 1F (hex values) in the ASCII table
|
// control characters values are from 0 to 1F (hex values) in the ASCII table
|
||||||
for ($charValue = 0x0; $charValue <= 0x1F; $charValue++) {
|
for ($charValue = 0x0; $charValue <= 0x1F; $charValue++) {
|
||||||
|
@ -10,4 +10,6 @@ abstract class Type
|
|||||||
{
|
{
|
||||||
const CSV = "csv";
|
const CSV = "csv";
|
||||||
const XLSX = "xlsx";
|
const XLSX = "xlsx";
|
||||||
|
const XLS = "xls";
|
||||||
|
const HTM = "htm";
|
||||||
}
|
}
|
||||||
|
@ -52,7 +52,7 @@ class CSV extends AbstractReader
|
|||||||
* The file must be UTF-8 encoded.
|
* The file must be UTF-8 encoded.
|
||||||
* @TODO add encoding detection/conversion
|
* @TODO add encoding detection/conversion
|
||||||
*
|
*
|
||||||
* @param string $filePath Path of the XLSX file to be read
|
* @param string $filePath Path of the CSV file to be read
|
||||||
* @return void
|
* @return void
|
||||||
* @throws \Box\Spout\Common\Exception\IOException
|
* @throws \Box\Spout\Common\Exception\IOException
|
||||||
*/
|
*/
|
||||||
|
@ -145,9 +145,9 @@ class SharedStringsHelper
|
|||||||
*/
|
*/
|
||||||
protected function removeSuperfluousTextNodes($parentNode)
|
protected function removeSuperfluousTextNodes($parentNode)
|
||||||
{
|
{
|
||||||
$tagsToRemove = [
|
$tagsToRemove = array(
|
||||||
'rPh', // Pronunciation of the text
|
'rPh', // Pronunciation of the text
|
||||||
];
|
);
|
||||||
|
|
||||||
foreach ($tagsToRemove as $tagToRemove) {
|
foreach ($tagsToRemove as $tagToRemove) {
|
||||||
$xpath = '//ns:' . $tagToRemove;
|
$xpath = '//ns:' . $tagToRemove;
|
||||||
|
@ -40,7 +40,7 @@ class WorksheetHelper
|
|||||||
*/
|
*/
|
||||||
public function getWorksheets()
|
public function getWorksheets()
|
||||||
{
|
{
|
||||||
$worksheets = [];
|
$worksheets = array();
|
||||||
|
|
||||||
$xmlContents = file_get_contents('zip://' . $this->filePath . '#' . self::CONTENT_TYPES_XML_FILE_PATH);
|
$xmlContents = file_get_contents('zip://' . $this->filePath . '#' . self::CONTENT_TYPES_XML_FILE_PATH);
|
||||||
|
|
||||||
|
@ -185,7 +185,7 @@ class XLSX extends AbstractReader
|
|||||||
}
|
}
|
||||||
|
|
||||||
$isInsideRowTag = false;
|
$isInsideRowTag = false;
|
||||||
$rowData = [];
|
$rowData = array();
|
||||||
|
|
||||||
while ($this->xmlReader->read()) {
|
while ($this->xmlReader->read()) {
|
||||||
if ($this->xmlReader->nodeType == \XMLReader::ELEMENT && $this->xmlReader->name === 'dimension') {
|
if ($this->xmlReader->nodeType == \XMLReader::ELEMENT && $this->xmlReader->name === 'dimension') {
|
||||||
@ -230,7 +230,7 @@ class XLSX extends AbstractReader
|
|||||||
}
|
}
|
||||||
|
|
||||||
// no data means "end of file"
|
// no data means "end of file"
|
||||||
return ($rowData !== []) ? $rowData : null;
|
return ($rowData !== array()) ? $rowData : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -38,13 +38,14 @@ abstract class AbstractWriter implements WriterInterface
|
|||||||
abstract protected function openWriter();
|
abstract protected function openWriter();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds data to the currently openned writer.
|
* Adds data to the currently opened writer.
|
||||||
*
|
*
|
||||||
* @param array $dataRow Array containing data to be streamed.
|
* @param array $dataRow Array containing data to be streamed.
|
||||||
* Example $dataRow = ['data1', 1234, null, '', 'data5'];
|
* Example $dataRow = ['data1', 1234, null, '', 'data5'];
|
||||||
|
* @param array $metaData Array containing meta-data maps for individual cells, such as 'url'
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
abstract protected function addRowToWriter(array $dataRow);
|
abstract protected function addRowToWriter(array $dataRow, array $metaData);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes the streamer, preventing any additional writing.
|
* Closes the streamer, preventing any additional writing.
|
||||||
@ -138,15 +139,16 @@ abstract class AbstractWriter implements WriterInterface
|
|||||||
*
|
*
|
||||||
* @param array $dataRow Array containing data to be streamed.
|
* @param array $dataRow Array containing data to be streamed.
|
||||||
* Example $dataRow = ['data1', 1234, null, '', 'data5'];
|
* Example $dataRow = ['data1', 1234, null, '', 'data5'];
|
||||||
|
* @param array $metaData Array containing meta-data maps for individual cells, such as 'url'
|
||||||
*
|
*
|
||||||
* @return \Box\Spout\Writer\AbstractWriter
|
* @return \Box\Spout\Writer\AbstractWriter
|
||||||
* @throws \Box\Spout\Writer\Exception\WriterNotOpenedException If this function is called before opening the writer
|
* @throws \Box\Spout\Writer\Exception\WriterNotOpenedException If this function is called before opening the writer
|
||||||
* @throws \Box\Spout\Common\Exception\IOException If unable to write data
|
* @throws \Box\Spout\Common\Exception\IOException If unable to write data
|
||||||
*/
|
*/
|
||||||
public function addRow(array $dataRow)
|
public function addRow(array $dataRow, array $metaData = array())
|
||||||
{
|
{
|
||||||
if ($this->isWriterOpened) {
|
if ($this->isWriterOpened) {
|
||||||
$this->addRowToWriter($dataRow);
|
$this->addRowToWriter($dataRow, $metaData);
|
||||||
} else {
|
} else {
|
||||||
throw new WriterNotOpenedException('The writer needs to be opened before adding row.');
|
throw new WriterNotOpenedException('The writer needs to be opened before adding row.');
|
||||||
}
|
}
|
||||||
|
@ -68,10 +68,11 @@ class CSV extends AbstractWriter
|
|||||||
*
|
*
|
||||||
* @param array $dataRow Array containing data to be written.
|
* @param array $dataRow Array containing data to be written.
|
||||||
* Example $dataRow = ['data1', 1234, null, '', 'data5'];
|
* Example $dataRow = ['data1', 1234, null, '', 'data5'];
|
||||||
|
* @param array $metaData Array containing meta-data maps for individual cells, such as 'url'
|
||||||
* @return void
|
* @return void
|
||||||
* @throws \Box\Spout\Common\Exception\IOException If unable to write data
|
* @throws \Box\Spout\Common\Exception\IOException If unable to write data
|
||||||
*/
|
*/
|
||||||
protected function addRowToWriter(array $dataRow)
|
protected function addRowToWriter(array $dataRow, array $metaData)
|
||||||
{
|
{
|
||||||
$wasWriteSuccessful = fputcsv($this->filePointer, $dataRow, $this->fieldDelimiter, $this->fieldEnclosure);
|
$wasWriteSuccessful = fputcsv($this->filePointer, $dataRow, $this->fieldDelimiter, $this->fieldEnclosure);
|
||||||
if ($wasWriteSuccessful === false) {
|
if ($wasWriteSuccessful === false) {
|
||||||
|
109
src/Spout/Writer/HTM.php
Normal file
109
src/Spout/Writer/HTM.php
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Box\Spout\Writer;
|
||||||
|
|
||||||
|
use Box\Spout\Common\Exception\IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class HTM
|
||||||
|
* This class provides support to write data to HTM files
|
||||||
|
*
|
||||||
|
* @package Box\Spout\Writer
|
||||||
|
*/
|
||||||
|
class HTM extends AbstractWriter
|
||||||
|
{
|
||||||
|
/** Number of rows to write before flushing */
|
||||||
|
const FLUSH_THRESHOLD = 500;
|
||||||
|
|
||||||
|
/** @var string Content-Type value for the header */
|
||||||
|
protected static $headerContentType = 'text/html; charset=UTF-8';
|
||||||
|
|
||||||
|
/** @var int */
|
||||||
|
protected $lastWrittenRowIndex = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the HTM streamer and makes it ready to accept data.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function openWriter()
|
||||||
|
{
|
||||||
|
fwrite($this->filePointer, "<html>\n");
|
||||||
|
fwrite($this->filePointer, "<head>\n");
|
||||||
|
fwrite($this->filePointer, "<title>" . htmlentities(basename(basename($this->outputFilePath, '.html'), '.htm')) . "</title>\n");
|
||||||
|
fwrite($this->filePointer, "</head>\n");
|
||||||
|
fwrite($this->filePointer, "<body>\n");
|
||||||
|
fwrite($this->filePointer, "<table cellspacing=\"3\" cellpadding=\"3\" border=\"1\">\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds data to the currently opened writer.
|
||||||
|
*
|
||||||
|
* @param array $dataRow Array containing data to be written.
|
||||||
|
* Example $dataRow = ['data1', 1234, null, '', 'data5'];
|
||||||
|
* @param array $metaData Array containing meta-data maps for individual cells, such as 'url'
|
||||||
|
* @return void
|
||||||
|
* @throws \Box\Spout\Common\Exception\IOException If unable to write data
|
||||||
|
*/
|
||||||
|
protected function addRowToWriter(array $dataRow, array $metaData)
|
||||||
|
{
|
||||||
|
$wasWriteSuccessful = true;
|
||||||
|
if ($this->lastWrittenRowIndex == 0) {
|
||||||
|
$wasWriteSuccessful = $wasWriteSuccessful && fwrite($this->filePointer, "<thead>\n");
|
||||||
|
}
|
||||||
|
if ($this->lastWrittenRowIndex == 1) {
|
||||||
|
$wasWriteSuccessful = $wasWriteSuccessful && fwrite($this->filePointer, "<tbody>\n");
|
||||||
|
}
|
||||||
|
$wasWriteSuccessful = $wasWriteSuccessful && fwrite($this->filePointer, "<tr>\n");
|
||||||
|
foreach ($dataRow as $i => $cell) {
|
||||||
|
$cell = nl2br(htmlentities($cell));
|
||||||
|
|
||||||
|
if (isset($metaData[$i]['url']))
|
||||||
|
{
|
||||||
|
$cell = '<a href="' . htmlentities($metaData[$i]['url']) . '">' . $cell . '</a>';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->lastWrittenRowIndex == 0) {
|
||||||
|
$wasWriteSuccessful = $wasWriteSuccessful && fwrite($this->filePointer, "\t<th>{$cell}</th>\n");
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
$wasWriteSuccessful = $wasWriteSuccessful && fwrite($this->filePointer, "\t<td>{$cell}</td>\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$wasWriteSuccessful = $wasWriteSuccessful && fwrite($this->filePointer, "</tr>\n");
|
||||||
|
if ($this->lastWrittenRowIndex == 0) {
|
||||||
|
$wasWriteSuccessful = $wasWriteSuccessful && fwrite($this->filePointer, "</thead>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($wasWriteSuccessful === false) {
|
||||||
|
throw new IOException('Unable to write data');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->lastWrittenRowIndex++;
|
||||||
|
if ($this->lastWrittenRowIndex % self::FLUSH_THRESHOLD === 0) {
|
||||||
|
$this->globalFunctionsHelper->fflush($this->filePointer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the HTM streamer, preventing any additional writing.
|
||||||
|
* If set, sets the headers and redirects output to the browser.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function closeWriter()
|
||||||
|
{
|
||||||
|
if ($this->filePointer) {
|
||||||
|
if ($this->lastWrittenRowIndex >= 1) {
|
||||||
|
fwrite($this->filePointer, "</tbody>\n");
|
||||||
|
}
|
||||||
|
fwrite($this->filePointer, "</table>\n");
|
||||||
|
fwrite($this->filePointer, "</body>\n");
|
||||||
|
fwrite($this->filePointer, "</html>\n");
|
||||||
|
|
||||||
|
$this->globalFunctionsHelper->fclose($this->filePointer);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->lastWrittenRowIndex = 0;
|
||||||
|
}
|
||||||
|
}
|
@ -20,6 +20,7 @@ class FileSystemHelper extends \Box\Spout\Common\Helper\FileSystemHelper
|
|||||||
const XL_FOLDER_NAME = 'xl';
|
const XL_FOLDER_NAME = 'xl';
|
||||||
const WORKSHEETS_FOLDER_NAME = 'worksheets';
|
const WORKSHEETS_FOLDER_NAME = 'worksheets';
|
||||||
|
|
||||||
|
const STYLES_FILE_NAME = 'styles.xml';
|
||||||
const RELS_FILE_NAME = '.rels';
|
const RELS_FILE_NAME = '.rels';
|
||||||
const APP_XML_FILE_NAME = 'app.xml';
|
const APP_XML_FILE_NAME = 'app.xml';
|
||||||
const CORE_XML_FILE_NAME = 'core.xml';
|
const CORE_XML_FILE_NAME = 'core.xml';
|
||||||
@ -81,7 +82,8 @@ class FileSystemHelper extends \Box\Spout\Common\Helper\FileSystemHelper
|
|||||||
->createRootFolder()
|
->createRootFolder()
|
||||||
->createRelsFolderAndFile()
|
->createRelsFolderAndFile()
|
||||||
->createDocPropsFolderAndFiles()
|
->createDocPropsFolderAndFiles()
|
||||||
->createXlFolderAndSubFolders();
|
->createXlFolderAndSubFolders()
|
||||||
|
->createStylesFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -179,7 +181,8 @@ EOD;
|
|||||||
*/
|
*/
|
||||||
protected function createCoreXmlFile()
|
protected function createCoreXmlFile()
|
||||||
{
|
{
|
||||||
$createdDate = (new \DateTime())->format('c');
|
$dt = new \DateTime();
|
||||||
|
$createdDate = $dt->format('c');
|
||||||
$coreXmlFileContents = <<<EOD
|
$coreXmlFileContents = <<<EOD
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
@ -205,6 +208,7 @@ EOD;
|
|||||||
$this->xlFolder = $this->createFolder($this->rootFolder, self::XL_FOLDER_NAME);
|
$this->xlFolder = $this->createFolder($this->rootFolder, self::XL_FOLDER_NAME);
|
||||||
$this->createXlRelsFolder();
|
$this->createXlRelsFolder();
|
||||||
$this->createXlWorksheetsFolder();
|
$this->createXlWorksheetsFolder();
|
||||||
|
$this->createXlWorksheetsRelsFolder();
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
@ -233,6 +237,60 @@ EOD;
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the "_rels" folder under the "worksheets" folder
|
||||||
|
*
|
||||||
|
* @return FileSystemHelper
|
||||||
|
* @throws \Box\Spout\Common\Exception\IOException If unable to create the folder
|
||||||
|
*/
|
||||||
|
protected function createXlWorksheetsRelsFolder()
|
||||||
|
{
|
||||||
|
$this->createFolder($this->xlWorksheetsFolder, self::RELS_FOLDER_NAME);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the "styles.xml" file under the "xl" folder
|
||||||
|
*
|
||||||
|
* @return FileSystemHelper
|
||||||
|
* @throws \Box\Spout\Common\Exception\IOException If unable to create the file
|
||||||
|
*/
|
||||||
|
protected function createStylesFile()
|
||||||
|
{
|
||||||
|
$stylesFileContents = <<<EOD
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
|
<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="x14ac" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac">
|
||||||
|
<fonts count="2" x14ac:knownFonts="1">
|
||||||
|
<font>
|
||||||
|
<sz val="12"/>
|
||||||
|
<color theme="1"/>
|
||||||
|
<name val="Calibri"/>
|
||||||
|
<family val="2"/>
|
||||||
|
<scheme val="minor"/>
|
||||||
|
</font>
|
||||||
|
<font>
|
||||||
|
<b/>
|
||||||
|
<sz val="12"/>
|
||||||
|
<color theme="1"/>
|
||||||
|
<name val="Calibri"/>
|
||||||
|
<family val="2"/>
|
||||||
|
<scheme val="minor"/>
|
||||||
|
</font>
|
||||||
|
</fonts>
|
||||||
|
<fills count="2"><fill><patternFill patternType="none"/></fill><fill><patternFill patternType="gray125"/></fill></fills>
|
||||||
|
<borders count="1"><border><left/><right/><top/><bottom/><diagonal/></border></borders>
|
||||||
|
<cellXfs count="2"><xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0"/><xf numFmtId="0" fontId="1" fillId="0" borderId="0" xfId="0" applyFont="1"/></cellXfs>
|
||||||
|
<cellStyles count="1"><cellStyle name="Normal" xfId="0" builtinId="0"/></cellStyles>
|
||||||
|
<dxfs count="0"/>
|
||||||
|
</styleSheet>
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
|
||||||
|
$this->createFileWithContents($this->xlFolder, self::STYLES_FILE_NAME, $stylesFileContents);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the "[Content_Types].xml" file under the root folder
|
* Creates the "[Content_Types].xml" file under the root folder
|
||||||
*
|
*
|
||||||
@ -247,6 +305,7 @@ EOD;
|
|||||||
<Default ContentType="application/xml" Extension="xml"/>
|
<Default ContentType="application/xml" Extension="xml"/>
|
||||||
<Default ContentType="application/vnd.openxmlformats-package.relationships+xml" Extension="rels"/>
|
<Default ContentType="application/vnd.openxmlformats-package.relationships+xml" Extension="rels"/>
|
||||||
<Override ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml" PartName="/xl/workbook.xml"/>
|
<Override ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml" PartName="/xl/workbook.xml"/>
|
||||||
|
<Override ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml" PartName="/xl/styles.xml"/>
|
||||||
|
|
||||||
EOD;
|
EOD;
|
||||||
|
|
||||||
@ -313,6 +372,7 @@ EOD;
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
||||||
<Relationship Id="rIdSharedStrings" Target="sharedStrings.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings"/>
|
<Relationship Id="rIdSharedStrings" Target="sharedStrings.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings"/>
|
||||||
|
<Relationship Id="rId3" Target="styles.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles"/>
|
||||||
|
|
||||||
EOD;
|
EOD;
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ class Workbook
|
|||||||
protected $sharedStringsHelper;
|
protected $sharedStringsHelper;
|
||||||
|
|
||||||
/** @var Worksheet[] Array containing the workbook's sheets */
|
/** @var Worksheet[] Array containing the workbook's sheets */
|
||||||
protected $worksheets = [];
|
protected $worksheets = array();
|
||||||
|
|
||||||
/** @var Worksheet The worksheet where data will be written to */
|
/** @var Worksheet The worksheet where data will be written to */
|
||||||
protected $currentWorksheet;
|
protected $currentWorksheet;
|
||||||
@ -165,11 +165,12 @@ class Workbook
|
|||||||
*
|
*
|
||||||
* @param array $dataRow Array containing data to be written.
|
* @param array $dataRow Array containing data to be written.
|
||||||
* Example $dataRow = ['data1', 1234, null, '', 'data5'];
|
* Example $dataRow = ['data1', 1234, null, '', 'data5'];
|
||||||
|
* @param array $metaData Array containing meta-data maps for individual cells, such as 'url'
|
||||||
* @return void
|
* @return void
|
||||||
* @throws \Box\Spout\Common\Exception\IOException If trying to create a new sheet and unable to open the sheet for writing
|
* @throws \Box\Spout\Common\Exception\IOException If trying to create a new sheet and unable to open the sheet for writing
|
||||||
* @throws \Box\Spout\Writer\Exception\WriterException If unable to write data
|
* @throws \Box\Spout\Writer\Exception\WriterException If unable to write data
|
||||||
*/
|
*/
|
||||||
public function addRowToCurrentWorksheet($dataRow)
|
public function addRowToCurrentWorksheet($dataRow, $metaData)
|
||||||
{
|
{
|
||||||
$currentWorksheet = $this->getCurrentWorksheet();
|
$currentWorksheet = $this->getCurrentWorksheet();
|
||||||
$hasReachedMaxRows = $this->hasCurrentWorkseetReachedMaxRows();
|
$hasReachedMaxRows = $this->hasCurrentWorkseetReachedMaxRows();
|
||||||
@ -179,12 +180,12 @@ class Workbook
|
|||||||
// ... continue writing in a new sheet if option set
|
// ... continue writing in a new sheet if option set
|
||||||
if ($this->shouldCreateNewSheetsAutomatically) {
|
if ($this->shouldCreateNewSheetsAutomatically) {
|
||||||
$currentWorksheet = $this->addNewSheetAndMakeItCurrent();
|
$currentWorksheet = $this->addNewSheetAndMakeItCurrent();
|
||||||
$currentWorksheet->addRow($dataRow);
|
$currentWorksheet->addRow($dataRow, $metaData);
|
||||||
} else {
|
} else {
|
||||||
// otherwise, do nothing as the data won't be read anyways
|
// otherwise, do nothing as the data won't be read anyways
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$currentWorksheet->addRow($dataRow);
|
$currentWorksheet->addRow($dataRow, $metaData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,6 +25,9 @@ EOD;
|
|||||||
/** @var string Path to the XML file that will contain the sheet data */
|
/** @var string Path to the XML file that will contain the sheet data */
|
||||||
protected $worksheetFilePath;
|
protected $worksheetFilePath;
|
||||||
|
|
||||||
|
/** @var string Path to the XML file that will contain the sheet rels data */
|
||||||
|
protected $worksheetRelsFilePath;
|
||||||
|
|
||||||
/** @var \Box\Spout\Writer\Helper\XLSX\SharedStringsHelper Helper to write shared strings */
|
/** @var \Box\Spout\Writer\Helper\XLSX\SharedStringsHelper Helper to write shared strings */
|
||||||
protected $sharedStringsHelper;
|
protected $sharedStringsHelper;
|
||||||
|
|
||||||
@ -40,6 +43,9 @@ EOD;
|
|||||||
/** @var int */
|
/** @var int */
|
||||||
protected $lastWrittenRowIndex = 0;
|
protected $lastWrittenRowIndex = 0;
|
||||||
|
|
||||||
|
/** @var array */
|
||||||
|
protected $urls = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param \Box\Spout\Writer\Sheet $externalSheet The associated "external" sheet
|
* @param \Box\Spout\Writer\Sheet $externalSheet The associated "external" sheet
|
||||||
* @param string $tempFolder Temporary folder where the files to create the XLSX will be stored
|
* @param string $tempFolder Temporary folder where the files to create the XLSX will be stored
|
||||||
@ -55,6 +61,7 @@ EOD;
|
|||||||
$this->stringsEscaper = new \Box\Spout\Common\Escaper\XLSX();
|
$this->stringsEscaper = new \Box\Spout\Common\Escaper\XLSX();
|
||||||
|
|
||||||
$this->worksheetFilePath = $worksheetFilesFolder . DIRECTORY_SEPARATOR . strtolower($this->externalSheet->getName()) . '.xml';
|
$this->worksheetFilePath = $worksheetFilesFolder . DIRECTORY_SEPARATOR . strtolower($this->externalSheet->getName()) . '.xml';
|
||||||
|
$this->worksheetRelsFilePath = $worksheetFilesFolder . DIRECTORY_SEPARATOR . '_rels' . DIRECTORY_SEPARATOR . strtolower($this->externalSheet->getName()) . '.xml.rels';
|
||||||
$this->startSheet();
|
$this->startSheet();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,11 +123,17 @@ EOD;
|
|||||||
*
|
*
|
||||||
* @param array $dataRow Array containing data to be written.
|
* @param array $dataRow Array containing data to be written.
|
||||||
* Example $dataRow = ['data1', 1234, null, '', 'data5'];
|
* Example $dataRow = ['data1', 1234, null, '', 'data5'];
|
||||||
|
* @param array $metaData Array containing meta-data maps for individual cells, such as 'url'
|
||||||
* @return void
|
* @return void
|
||||||
* @throws \Box\Spout\Common\Exception\IOException If the data cannot be written
|
* @throws \Box\Spout\Common\Exception\IOException If the data cannot be written
|
||||||
*/
|
*/
|
||||||
public function addRow($dataRow)
|
public function addRow($dataRow, array $metaData = array())
|
||||||
{
|
{
|
||||||
|
if (count($dataRow) == 0) {
|
||||||
|
// Without this fix, we get a repair issue in regular Microsoft Excel
|
||||||
|
$dataRow=array('');
|
||||||
|
}
|
||||||
|
|
||||||
$cellNumber = 0;
|
$cellNumber = 0;
|
||||||
$rowIndex = $this->lastWrittenRowIndex + 1;
|
$rowIndex = $this->lastWrittenRowIndex + 1;
|
||||||
$numCells = count($dataRow);
|
$numCells = count($dataRow);
|
||||||
@ -129,12 +142,14 @@ EOD;
|
|||||||
|
|
||||||
foreach($dataRow as $cellValue) {
|
foreach($dataRow as $cellValue) {
|
||||||
$columnIndex = CellHelper::getCellIndexFromColumnIndex($cellNumber);
|
$columnIndex = CellHelper::getCellIndexFromColumnIndex($cellNumber);
|
||||||
$data .= ' <c r="' . $columnIndex . $rowIndex . '"';
|
$cellPath = $columnIndex . $rowIndex;
|
||||||
|
|
||||||
|
$data .= ' <c' . (($rowIndex == 1) ? ' s="1"' : '') . ' r="' . $cellPath . '"';
|
||||||
|
|
||||||
if (empty($cellValue)) {
|
if (empty($cellValue)) {
|
||||||
$data .= '/>' . PHP_EOL;
|
$data .= '/>' . PHP_EOL;
|
||||||
} else {
|
} else {
|
||||||
if (is_numeric($cellValue)) {
|
if (trim($cellValue, '0123456789.') == '' /*similar to is_numeric without having PHPs regular quirkiness*/) {
|
||||||
$data .= '><v>' . $cellValue . '</v></c>' . PHP_EOL;
|
$data .= '><v>' . $cellValue . '</v></c>' . PHP_EOL;
|
||||||
} else {
|
} else {
|
||||||
if ($this->shouldUseInlineStrings) {
|
if ($this->shouldUseInlineStrings) {
|
||||||
@ -146,6 +161,10 @@ EOD;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isset($metaData[$cellNumber]['url'])) {
|
||||||
|
$this->urls[$cellPath] = $metaData[$cellNumber]['url'];
|
||||||
|
}
|
||||||
|
|
||||||
$cellNumber++;
|
$cellNumber++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,6 +187,34 @@ EOD;
|
|||||||
public function close()
|
public function close()
|
||||||
{
|
{
|
||||||
fwrite($this->sheetFilePointer, ' </sheetData>' . PHP_EOL);
|
fwrite($this->sheetFilePointer, ' </sheetData>' . PHP_EOL);
|
||||||
|
|
||||||
|
// Write out any hyperlinks
|
||||||
|
if (count($this->urls) != 0) {
|
||||||
|
fwrite($this->sheetFilePointer, ' <hyperlinks>' . PHP_EOL);
|
||||||
|
$i = 0;
|
||||||
|
foreach ($this->urls as $cellPath => $url) {
|
||||||
|
$refID = 'rId' . ($i + 1);
|
||||||
|
fwrite($this->sheetFilePointer, ' <hyperlink ref="' . $cellPath . '" r:id="' . $refID . '"/>' . PHP_EOL);
|
||||||
|
$i++;
|
||||||
|
}
|
||||||
|
fwrite($this->sheetFilePointer, ' </hyperlinks>' . PHP_EOL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write rels file
|
||||||
|
$sheetRelsFilePointer = fopen($this->worksheetRelsFilePath, 'w');
|
||||||
|
if (!$sheetRelsFilePointer) throw new IOException('Unable to open rels sheet for writing.');
|
||||||
|
fwrite($sheetRelsFilePointer, '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' . PHP_EOL);
|
||||||
|
fwrite($sheetRelsFilePointer, '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">' . PHP_EOL);
|
||||||
|
$i = 0;
|
||||||
|
foreach ($this->urls as $url) {
|
||||||
|
$refID = 'rId' . ($i + 1);
|
||||||
|
fwrite($sheetRelsFilePointer, '<Relationship Id="' . $refID . '" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink" Target="' . htmlentities($url) . '" TargetMode="External"/>' . PHP_EOL);
|
||||||
|
$i++;
|
||||||
|
}
|
||||||
|
fwrite($sheetRelsFilePointer, '</Relationships>');
|
||||||
|
fclose($sheetRelsFilePointer);
|
||||||
|
|
||||||
|
// Finish file
|
||||||
fwrite($this->sheetFilePointer, '</worksheet>');
|
fwrite($this->sheetFilePointer, '</worksheet>');
|
||||||
fclose($this->sheetFilePointer);
|
fclose($this->sheetFilePointer);
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,12 @@ class WriterFactory
|
|||||||
case Type::XLSX:
|
case Type::XLSX:
|
||||||
$writer = new XLSX();
|
$writer = new XLSX();
|
||||||
break;
|
break;
|
||||||
|
case Type::XLS:
|
||||||
|
$writer = new XLS();
|
||||||
|
break;
|
||||||
|
case Type::HTM:
|
||||||
|
$writer = new HTM();
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new UnsupportedTypeException('No writers supporting the given type: ' . $writerType);
|
throw new UnsupportedTypeException('No writers supporting the given type: ' . $writerType);
|
||||||
}
|
}
|
||||||
|
@ -34,11 +34,12 @@ interface WriterInterface
|
|||||||
*
|
*
|
||||||
* @param array $dataRow Array containing data to be streamed.
|
* @param array $dataRow Array containing data to be streamed.
|
||||||
* Example $dataRow = ['data1', 1234, null, '', 'data5'];
|
* Example $dataRow = ['data1', 1234, null, '', 'data5'];
|
||||||
|
* @param array $metaData Array containing meta-data maps for individual cells, such as 'url'
|
||||||
* @return \Box\Spout\Writer\WriterInterface
|
* @return \Box\Spout\Writer\WriterInterface
|
||||||
* @throws \Box\Spout\Writer\Exception\WriterNotOpenedException If the writer has not been opened yetthe writer
|
* @throws \Box\Spout\Writer\Exception\WriterNotOpenedException If the writer has not been opened yetthe writer
|
||||||
* @throws \Box\Spout\Common\Exception\IOException If unable to write data
|
* @throws \Box\Spout\Common\Exception\IOException If unable to write data
|
||||||
*/
|
*/
|
||||||
public function addRow(array $dataRow);
|
public function addRow(array $dataRow, array $metaData = array());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write given data to the output. New data will be appended to end of stream.
|
* Write given data to the output. New data will be appended to end of stream.
|
||||||
|
194
src/Spout/Writer/XLS.php
Normal file
194
src/Spout/Writer/XLS.php
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Box\Spout\Writer;
|
||||||
|
|
||||||
|
use Box\Spout\Common\Exception\IOException;
|
||||||
|
|
||||||
|
// Based on https://gist.github.com/ihumanable/929039
|
||||||
|
// ... with some bug fixes by Chris Graham, ocProducts.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class XLS
|
||||||
|
* This class provides support to write data to XLS files
|
||||||
|
*
|
||||||
|
* @package Box\Spout\Writer
|
||||||
|
*/
|
||||||
|
class XLS extends AbstractWriter
|
||||||
|
{
|
||||||
|
/** Number of rows to write before flushing */
|
||||||
|
const FLUSH_THRESHOLD = 500;
|
||||||
|
|
||||||
|
/** @var string Content-Type value for the header */
|
||||||
|
protected static $headerContentType = 'application/vnd.ms-excel; charset=UTF-8';
|
||||||
|
|
||||||
|
/** @var int */
|
||||||
|
private $col;
|
||||||
|
|
||||||
|
/** @var int */
|
||||||
|
private $row;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the XLS streamer and makes it ready to accept data.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function openWriter()
|
||||||
|
{
|
||||||
|
$this->col = 0;
|
||||||
|
$this->row = 0;
|
||||||
|
$this->bofMarker();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes the Excel Beginning of File marker
|
||||||
|
*
|
||||||
|
* @see pack()
|
||||||
|
* @return nothing
|
||||||
|
*/
|
||||||
|
private function bofMarker()
|
||||||
|
{
|
||||||
|
fwrite($this->filePointer, pack("ssssss", 0x809, 0x8, 0x0, 0x10, 0x0, 0x0));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves internal cursor all the way left, col = 0
|
||||||
|
*
|
||||||
|
* @return nothing
|
||||||
|
*/
|
||||||
|
function home()
|
||||||
|
{
|
||||||
|
$this->col = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves internal cursor right by the amount specified
|
||||||
|
*
|
||||||
|
* @param optional integer $amount The amount to move right by, defaults to 1
|
||||||
|
* @return integer The current column after the move
|
||||||
|
*/
|
||||||
|
function right($amount = 1)
|
||||||
|
{
|
||||||
|
$this->col += $amount;
|
||||||
|
return $this->col;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves internal cursor down by amount
|
||||||
|
*
|
||||||
|
* @param optional integer $amount The amount to move down by, defaults to 1
|
||||||
|
* @return integer The current row after the move
|
||||||
|
*/
|
||||||
|
function down($amount = 1)
|
||||||
|
{
|
||||||
|
$this->row += $amount;
|
||||||
|
return $this->row;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes a number to the Excel Spreadsheet
|
||||||
|
*
|
||||||
|
* @see pack()
|
||||||
|
* @param integer $value The value to write out
|
||||||
|
* @return boolean Success status
|
||||||
|
*/
|
||||||
|
function number($value)
|
||||||
|
{
|
||||||
|
$wasWriteSuccessful = true;
|
||||||
|
$wasWriteSuccessful = $wasWriteSuccessful && fwrite($this->filePointer, pack("sssss", 0x203, 14, $this->row, $this->col, 0x0));
|
||||||
|
$wasWriteSuccessful = $wasWriteSuccessful && fwrite($this->filePointer, pack("d", $value));
|
||||||
|
return $wasWriteSuccessful;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes a string (or label) to the Excel Spreadsheet
|
||||||
|
*
|
||||||
|
* @see pack()
|
||||||
|
* @param string $value The value to write out
|
||||||
|
* @return boolean Success status
|
||||||
|
*/
|
||||||
|
function label($value)
|
||||||
|
{
|
||||||
|
$value = str_replace(array("\r\n", "\r"), "\n", $value);
|
||||||
|
|
||||||
|
// We're doing BIFF5, not BIFF8 - meaning a 255 char limit. If you want something good, use XLSX, else XLS for compatibility.
|
||||||
|
if (strlen($value) >= 255) {
|
||||||
|
$value = substr($value, 0, 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
$length = strlen($value);
|
||||||
|
$wasWriteSuccessful = true;
|
||||||
|
$wasWriteSuccessful = $wasWriteSuccessful && fwrite($this->filePointer, pack("ssssss", 0x204, 8 + $length, $this->row, $this->col, 0x0, $length));
|
||||||
|
if ($value !='') {
|
||||||
|
$wasWriteSuccessful = $wasWriteSuccessful && fwrite($this->filePointer, $value);
|
||||||
|
}
|
||||||
|
return $wasWriteSuccessful;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds data to the currently opened writer.
|
||||||
|
*
|
||||||
|
* @param array $dataRow Array containing data to be written.
|
||||||
|
* Example $dataRow = ['data1', 1234, null, '', 'data5'];
|
||||||
|
* @param array $metaData Array containing meta-data maps for individual cells, such as 'url'
|
||||||
|
* @return void
|
||||||
|
* @throws \Box\Spout\Common\Exception\IOException If unable to write data
|
||||||
|
*/
|
||||||
|
protected function addRowToWriter(array $dataRow, array $metaData)
|
||||||
|
{
|
||||||
|
if ($this->row == 65536) {
|
||||||
|
// Hit the limit. You should have chosen XLSX
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$wasWriteSuccessful = true;
|
||||||
|
|
||||||
|
$this->home();
|
||||||
|
foreach ($dataRow as $cell) {
|
||||||
|
if (trim($cell, '0123456789.') == '' /*similar to is_numeric without having PHPs regular quirkiness*/) {
|
||||||
|
$wasWriteSuccessful = $wasWriteSuccessful && $this->number($cell);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
$wasWriteSuccessful = $wasWriteSuccessful && $this->label($cell);
|
||||||
|
}
|
||||||
|
$this->right();
|
||||||
|
}
|
||||||
|
$this->down();
|
||||||
|
|
||||||
|
if ($wasWriteSuccessful === false) {
|
||||||
|
throw new IOException('Unable to write data');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->row % self::FLUSH_THRESHOLD === 0) {
|
||||||
|
$this->globalFunctionsHelper->fflush($this->filePointer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes the Excel End of File marker
|
||||||
|
*
|
||||||
|
* @see pack()
|
||||||
|
* @return nothing
|
||||||
|
*/
|
||||||
|
private function eofMarker()
|
||||||
|
{
|
||||||
|
fwrite($this->filePointer, pack("ss", 0x0A, 0x00));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the XLS streamer, preventing any additional writing.
|
||||||
|
* If set, sets the headers and redirects output to the browser.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function closeWriter()
|
||||||
|
{
|
||||||
|
if ($this->filePointer) {
|
||||||
|
$this->eofMarker();
|
||||||
|
|
||||||
|
$this->globalFunctionsHelper->fclose($this->filePointer);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->row = 0;
|
||||||
|
$this->col = 0;
|
||||||
|
}
|
||||||
|
}
|
@ -88,7 +88,7 @@ class XLSX extends AbstractWriter
|
|||||||
{
|
{
|
||||||
$this->throwIfBookIsNotAvailable();
|
$this->throwIfBookIsNotAvailable();
|
||||||
|
|
||||||
$externalSheets = [];
|
$externalSheets = array();
|
||||||
$worksheets = $this->book->getWorksheets();
|
$worksheets = $this->book->getWorksheets();
|
||||||
|
|
||||||
/** @var Internal\XLSX\Worksheet $worksheet */
|
/** @var Internal\XLSX\Worksheet $worksheet */
|
||||||
@ -160,14 +160,15 @@ class XLSX extends AbstractWriter
|
|||||||
*
|
*
|
||||||
* @param array $dataRow Array containing data to be written.
|
* @param array $dataRow Array containing data to be written.
|
||||||
* Example $dataRow = ['data1', 1234, null, '', 'data5'];
|
* Example $dataRow = ['data1', 1234, null, '', 'data5'];
|
||||||
|
* @param array $metaData Array containing meta-data maps for individual cells, such as 'url'
|
||||||
* @return void
|
* @return void
|
||||||
* @throws \Box\Spout\Writer\Exception\WriterNotOpenedException If the book is not created yet
|
* @throws \Box\Spout\Writer\Exception\WriterNotOpenedException If the book is not created yet
|
||||||
* @throws \Box\Spout\Common\Exception\IOException If unable to write data
|
* @throws \Box\Spout\Common\Exception\IOException If unable to write data
|
||||||
*/
|
*/
|
||||||
protected function addRowToWriter(array $dataRow)
|
protected function addRowToWriter(array $dataRow, array $metaData)
|
||||||
{
|
{
|
||||||
$this->throwIfBookIsNotAvailable();
|
$this->throwIfBookIsNotAvailable();
|
||||||
$this->book->addRowToCurrentWorksheet($dataRow);
|
$this->book->addRowToCurrentWorksheet($dataRow, $metaData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
x
Reference in New Issue
Block a user