Excel.php 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Framework\Convert;
  7. use Magento\Framework\Filesystem\File\WriteInterface;
  8. /**
  9. * Convert the data to XML Excel
  10. */
  11. class Excel
  12. {
  13. /**
  14. * \ArrayIterator Object
  15. *
  16. * @var \Iterator|null
  17. */
  18. protected $_iterator = null;
  19. /**
  20. * Method Callback Array
  21. *
  22. * @var array
  23. */
  24. protected $_rowCallback = [];
  25. /**
  26. * Grid Header Array
  27. *
  28. * @var array
  29. */
  30. protected $_dataHeader = [];
  31. /**
  32. * Grid Footer Array
  33. *
  34. * @var array
  35. */
  36. protected $_dataFooter = [];
  37. /**
  38. * Class Constructor
  39. *
  40. * @param \Iterator $iterator
  41. * @param array $rowCallback
  42. */
  43. public function __construct(\Iterator $iterator, $rowCallback = [])
  44. {
  45. $this->_iterator = $iterator;
  46. $this->_rowCallback = $rowCallback;
  47. }
  48. /**
  49. * Retrieve Excel XML Document Header XML Fragment
  50. * Append data header if it is available
  51. *
  52. * @param string $sheetName
  53. * @return string
  54. */
  55. protected function _getXmlHeader($sheetName = '')
  56. {
  57. if (empty($sheetName)) {
  58. $sheetName = 'Sheet 1';
  59. }
  60. $sheetName = htmlspecialchars($sheetName);
  61. $xmlHeader = '<' .
  62. '?xml version="1.0"?' .
  63. '><' .
  64. '?mso-application progid="Excel.Sheet"?' .
  65. '><Workbook' .
  66. ' xmlns="urn:schemas-microsoft-com:office:spreadsheet"' .
  67. ' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"' .
  68. ' xmlns:x="urn:schemas-microsoft-com:office:excel"' .
  69. ' xmlns:x2="http://schemas.microsoft.com/office/excel/2003/xml"' .
  70. ' xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"' .
  71. ' xmlns:o="urn:schemas-microsoft-com:office:office"' .
  72. ' xmlns:html="http://www.w3.org/TR/REC-html40"' .
  73. ' xmlns:c="urn:schemas-microsoft-com:office:component:spreadsheet">' .
  74. '<OfficeDocumentSettings xmlns="urn:schemas-microsoft-com:office:office">' .
  75. '</OfficeDocumentSettings>' .
  76. '<ExcelWorkbook xmlns="urn:schemas-microsoft-com:office:excel">' .
  77. '</ExcelWorkbook>' .
  78. '<Worksheet ss:Name="' .
  79. $sheetName .
  80. '">' .
  81. '<Table>';
  82. if ($this->_dataHeader) {
  83. $xmlHeader .= $this->_getXmlRow($this->_dataHeader, false);
  84. }
  85. return $xmlHeader;
  86. }
  87. /**
  88. * Retrieve Excel XML Document Footer XML Fragment
  89. * Append data footer if it is available
  90. *
  91. * @return string
  92. */
  93. protected function _getXmlFooter()
  94. {
  95. $xmlFooter = '';
  96. if ($this->_dataFooter) {
  97. $xmlFooter = $this->_getXmlRow($this->_dataFooter, false);
  98. }
  99. $xmlFooter .= '</Table></Worksheet></Workbook>';
  100. return $xmlFooter;
  101. }
  102. /**
  103. * Get a Single XML Row
  104. *
  105. * @param array $row
  106. * @param boolean $useCallback
  107. * @return string
  108. *
  109. * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  110. */
  111. protected function _getXmlRow($row, $useCallback)
  112. {
  113. if ($useCallback && $this->_rowCallback) {
  114. $row = call_user_func($this->_rowCallback, $row);
  115. }
  116. $xmlData = [];
  117. $xmlData[] = '<Row>';
  118. foreach ($row as $value) {
  119. $value = htmlspecialchars($value);
  120. $dataType = is_numeric($value) && $value[0] !== '+' && $value[0] !== '0' ? 'Number' : 'String';
  121. /**
  122. * Security enhancement for CSV data processing by Excel-like applications.
  123. * @see https://bugzilla.mozilla.org/show_bug.cgi?id=1054702
  124. *
  125. * @var $value string|\Magento\Framework\Phrase
  126. */
  127. if (!is_string($value)) {
  128. $value = (string)$value;
  129. }
  130. if (isset($value[0]) && in_array($value[0], ['=', '+', '-'])) {
  131. $value = ' ' . $value;
  132. $dataType = 'String';
  133. }
  134. $value = str_replace("\r\n", '&#10;', $value);
  135. $value = str_replace("\r", '&#10;', $value);
  136. $value = str_replace("\n", '&#10;', $value);
  137. $xmlData[] = '<Cell><Data ss:Type="' . $dataType . '">' . $value . '</Data></Cell>';
  138. }
  139. $xmlData[] = '</Row>';
  140. return join('', $xmlData);
  141. }
  142. /**
  143. * Set Data Header
  144. *
  145. * @param array $data
  146. * @return void
  147. */
  148. public function setDataHeader($data)
  149. {
  150. $this->_dataHeader = $data;
  151. }
  152. /**
  153. * Set Data Footer
  154. *
  155. * @param array $data
  156. * @return void
  157. */
  158. public function setDataFooter($data)
  159. {
  160. $this->_dataFooter = $data;
  161. }
  162. /**
  163. * Convert Data to Excel XML Document
  164. *
  165. * @param string $sheetName
  166. * @return string
  167. */
  168. public function convert($sheetName = '')
  169. {
  170. $xml = $this->_getXmlHeader($sheetName);
  171. foreach ($this->_iterator as $dataRow) {
  172. $xml .= $this->_getXmlRow($dataRow, true);
  173. }
  174. $xml .= $this->_getXmlFooter();
  175. return $xml;
  176. }
  177. /**
  178. * Write Converted XML Data to Temporary File
  179. *
  180. * @param WriteInterface $stream
  181. * @param string $sheetName
  182. * @return void
  183. */
  184. public function write(WriteInterface $stream, $sheetName = '')
  185. {
  186. $stream->write($this->_getXmlHeader($sheetName));
  187. foreach ($this->_iterator as $dataRow) {
  188. $stream->write($this->_getXmlRow($dataRow, true));
  189. }
  190. $stream->write($this->_getXmlFooter());
  191. }
  192. }