PageRenderTime 81ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/spout/src/Spout/Writer/XLSX/Internal/Worksheet.php

http://github.com/moodle/moodle
PHP | 185 lines | 96 code | 28 blank | 61 comment | 12 complexity | ee5084d1fe9d735050e2df9ed141d9f4 MD5 | raw file
Possible License(s): MIT, AGPL-3.0, MPL-2.0-no-copyleft-exception, LGPL-3.0, GPL-3.0, Apache-2.0, LGPL-2.1, BSD-3-Clause
  1. <?php
  2. namespace Box\Spout\Writer\XLSX\Internal;
  3. use Box\Spout\Common\Exception\InvalidArgumentException;
  4. use Box\Spout\Common\Exception\IOException;
  5. use Box\Spout\Writer\Common\Helper\CellHelper;
  6. use Box\Spout\Writer\Common\Internal\WorksheetInterface;
  7. /**
  8. * Class Worksheet
  9. * Represents a worksheet within a XLSX file. The difference with the Sheet object is
  10. * that this class provides an interface to write data
  11. *
  12. * @package Box\Spout\Writer\XLSX\Internal
  13. */
  14. class Worksheet implements WorksheetInterface
  15. {
  16. const SHEET_XML_FILE_HEADER = <<<EOD
  17. <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  18. <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
  19. EOD;
  20. /** @var \Box\Spout\Writer\Common\Sheet The "external" sheet */
  21. protected $externalSheet;
  22. /** @var string Path to the XML file that will contain the sheet data */
  23. protected $worksheetFilePath;
  24. /** @var \Box\Spout\Writer\XLSX\Helper\SharedStringsHelper Helper to write shared strings */
  25. protected $sharedStringsHelper;
  26. /** @var bool Whether inline or shared strings should be used */
  27. protected $shouldUseInlineStrings;
  28. /** @var \Box\Spout\Common\Escaper\XLSX Strings escaper */
  29. protected $stringsEscaper;
  30. /** @var Resource Pointer to the sheet data file (e.g. xl/worksheets/sheet1.xml) */
  31. protected $sheetFilePointer;
  32. /** @var int Index of the last written row */
  33. protected $lastWrittenRowIndex = 0;
  34. /**
  35. * @param \Box\Spout\Writer\Common\Sheet $externalSheet The associated "external" sheet
  36. * @param string $worksheetFilesFolder Temporary folder where the files to create the XLSX will be stored
  37. * @param \Box\Spout\Writer\XLSX\Helper\SharedStringsHelper $sharedStringsHelper Helper for shared strings
  38. * @param bool $shouldUseInlineStrings Whether inline or shared strings should be used
  39. * @throws \Box\Spout\Common\Exception\IOException If the sheet data file cannot be opened for writing
  40. */
  41. public function __construct($externalSheet, $worksheetFilesFolder, $sharedStringsHelper, $shouldUseInlineStrings)
  42. {
  43. $this->externalSheet = $externalSheet;
  44. $this->sharedStringsHelper = $sharedStringsHelper;
  45. $this->shouldUseInlineStrings = $shouldUseInlineStrings;
  46. /** @noinspection PhpUnnecessaryFullyQualifiedNameInspection */
  47. $this->stringsEscaper = new \Box\Spout\Common\Escaper\XLSX();
  48. $this->worksheetFilePath = $worksheetFilesFolder . '/' . strtolower($this->externalSheet->getName()) . '.xml';
  49. $this->startSheet();
  50. }
  51. /**
  52. * Prepares the worksheet to accept data
  53. *
  54. * @return void
  55. * @throws \Box\Spout\Common\Exception\IOException If the sheet data file cannot be opened for writing
  56. */
  57. protected function startSheet()
  58. {
  59. $this->sheetFilePointer = fopen($this->worksheetFilePath, 'w');
  60. $this->throwIfSheetFilePointerIsNotAvailable();
  61. fwrite($this->sheetFilePointer, self::SHEET_XML_FILE_HEADER);
  62. fwrite($this->sheetFilePointer, '<sheetData>');
  63. }
  64. /**
  65. * Checks if the book has been created. Throws an exception if not created yet.
  66. *
  67. * @return void
  68. * @throws \Box\Spout\Common\Exception\IOException If the sheet data file cannot be opened for writing
  69. */
  70. protected function throwIfSheetFilePointerIsNotAvailable()
  71. {
  72. if (!$this->sheetFilePointer) {
  73. throw new IOException('Unable to open sheet for writing.');
  74. }
  75. }
  76. /**
  77. * @return \Box\Spout\Writer\Common\Sheet The "external" sheet
  78. */
  79. public function getExternalSheet()
  80. {
  81. return $this->externalSheet;
  82. }
  83. /**
  84. * @return int The index of the last written row
  85. */
  86. public function getLastWrittenRowIndex()
  87. {
  88. return $this->lastWrittenRowIndex;
  89. }
  90. /**
  91. * @return int The ID of the worksheet
  92. */
  93. public function getId()
  94. {
  95. // sheet index is zero-based, while ID is 1-based
  96. return $this->externalSheet->getIndex() + 1;
  97. }
  98. /**
  99. * Adds data to the worksheet.
  100. *
  101. * @param array $dataRow Array containing data to be written. Cannot be empty.
  102. * Example $dataRow = ['data1', 1234, null, '', 'data5'];
  103. * @param \Box\Spout\Writer\Style\Style $style Style to be applied to the row. NULL means use default style.
  104. * @return void
  105. * @throws \Box\Spout\Common\Exception\IOException If the data cannot be written
  106. * @throws \Box\Spout\Common\Exception\InvalidArgumentException If a cell value's type is not supported
  107. */
  108. public function addRow($dataRow, $style)
  109. {
  110. $cellNumber = 0;
  111. $rowIndex = $this->lastWrittenRowIndex + 1;
  112. $numCells = count($dataRow);
  113. $rowXML = '<row r="' . $rowIndex . '" spans="1:' . $numCells . '">';
  114. foreach($dataRow as $cellValue) {
  115. $columnIndex = CellHelper::getCellIndexFromColumnIndex($cellNumber);
  116. $cellXML = '<c r="' . $columnIndex . $rowIndex . '"';
  117. $cellXML .= ' s="' . $style->getId() . '"';
  118. if (CellHelper::isNonEmptyString($cellValue)) {
  119. if ($this->shouldUseInlineStrings) {
  120. $cellXML .= ' t="inlineStr"><is><t>' . $this->stringsEscaper->escape($cellValue) . '</t></is></c>';
  121. } else {
  122. $sharedStringId = $this->sharedStringsHelper->writeString($cellValue);
  123. $cellXML .= ' t="s"><v>' . $sharedStringId . '</v></c>';
  124. }
  125. } else if (CellHelper::isBoolean($cellValue)) {
  126. $cellXML .= ' t="b"><v>' . intval($cellValue) . '</v></c>';
  127. } else if (CellHelper::isNumeric($cellValue)) {
  128. $cellXML .= '><v>' . $cellValue . '</v></c>';
  129. } else if (empty($cellValue)) {
  130. // don't write empty cells (not appending to $cellXML is the right behavior!)
  131. $cellXML = '';
  132. } else {
  133. throw new InvalidArgumentException('Trying to add a value with an unsupported type: ' . gettype($cellValue));
  134. }
  135. $rowXML .= $cellXML;
  136. $cellNumber++;
  137. }
  138. $rowXML .= '</row>';
  139. $wasWriteSuccessful = fwrite($this->sheetFilePointer, $rowXML);
  140. if ($wasWriteSuccessful === false) {
  141. throw new IOException("Unable to write data in {$this->worksheetFilePath}");
  142. }
  143. // only update the count if the write worked
  144. $this->lastWrittenRowIndex++;
  145. }
  146. /**
  147. * Closes the worksheet
  148. *
  149. * @return void
  150. */
  151. public function close()
  152. {
  153. fwrite($this->sheetFilePointer, '</sheetData>');
  154. fwrite($this->sheetFilePointer, '</worksheet>');
  155. fclose($this->sheetFilePointer);
  156. }
  157. }