Csv.php 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. <?php
  2. namespace PhpOffice\PhpSpreadsheet\Writer;
  3. use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
  4. use PhpOffice\PhpSpreadsheet\Spreadsheet;
  5. class Csv extends BaseWriter
  6. {
  7. /**
  8. * PhpSpreadsheet object.
  9. *
  10. * @var Spreadsheet
  11. */
  12. private $spreadsheet;
  13. /**
  14. * Delimiter.
  15. *
  16. * @var string
  17. */
  18. private $delimiter = ',';
  19. /**
  20. * Enclosure.
  21. *
  22. * @var string
  23. */
  24. private $enclosure = '"';
  25. /**
  26. * Line ending.
  27. *
  28. * @var string
  29. */
  30. private $lineEnding = PHP_EOL;
  31. /**
  32. * Sheet index to write.
  33. *
  34. * @var int
  35. */
  36. private $sheetIndex = 0;
  37. /**
  38. * Whether to write a BOM (for UTF8).
  39. *
  40. * @var bool
  41. */
  42. private $useBOM = false;
  43. /**
  44. * Whether to write a Separator line as the first line of the file
  45. * sep=x.
  46. *
  47. * @var bool
  48. */
  49. private $includeSeparatorLine = false;
  50. /**
  51. * Whether to write a fully Excel compatible CSV file.
  52. *
  53. * @var bool
  54. */
  55. private $excelCompatibility = false;
  56. /**
  57. * Create a new CSV.
  58. *
  59. * @param Spreadsheet $spreadsheet Spreadsheet object
  60. */
  61. public function __construct(Spreadsheet $spreadsheet)
  62. {
  63. $this->spreadsheet = $spreadsheet;
  64. }
  65. /**
  66. * Save PhpSpreadsheet to file.
  67. *
  68. * @param string $pFilename
  69. *
  70. * @throws Exception
  71. */
  72. public function save($pFilename)
  73. {
  74. // Fetch sheet
  75. $sheet = $this->spreadsheet->getSheet($this->sheetIndex);
  76. $saveDebugLog = Calculation::getInstance($this->spreadsheet)->getDebugLog()->getWriteDebugLog();
  77. Calculation::getInstance($this->spreadsheet)->getDebugLog()->setWriteDebugLog(false);
  78. $saveArrayReturnType = Calculation::getArrayReturnType();
  79. Calculation::setArrayReturnType(Calculation::RETURN_ARRAY_AS_VALUE);
  80. // Open file
  81. $fileHandle = fopen($pFilename, 'wb+');
  82. if ($fileHandle === false) {
  83. throw new Exception("Could not open file $pFilename for writing.");
  84. }
  85. if ($this->excelCompatibility) {
  86. $this->setUseBOM(true); // Enforce UTF-8 BOM Header
  87. $this->setIncludeSeparatorLine(true); // Set separator line
  88. $this->setEnclosure('"'); // Set enclosure to "
  89. $this->setDelimiter(';'); // Set delimiter to a semi-colon
  90. $this->setLineEnding("\r\n");
  91. }
  92. if ($this->useBOM) {
  93. // Write the UTF-8 BOM code if required
  94. fwrite($fileHandle, "\xEF\xBB\xBF");
  95. }
  96. if ($this->includeSeparatorLine) {
  97. // Write the separator line if required
  98. fwrite($fileHandle, 'sep=' . $this->getDelimiter() . $this->lineEnding);
  99. }
  100. // Identify the range that we need to extract from the worksheet
  101. $maxCol = $sheet->getHighestDataColumn();
  102. $maxRow = $sheet->getHighestDataRow();
  103. // Write rows to file
  104. for ($row = 1; $row <= $maxRow; ++$row) {
  105. // Convert the row to an array...
  106. $cellsArray = $sheet->rangeToArray('A' . $row . ':' . $maxCol . $row, '', $this->preCalculateFormulas);
  107. // ... and write to the file
  108. $this->writeLine($fileHandle, $cellsArray[0]);
  109. }
  110. // Close file
  111. fclose($fileHandle);
  112. Calculation::setArrayReturnType($saveArrayReturnType);
  113. Calculation::getInstance($this->spreadsheet)->getDebugLog()->setWriteDebugLog($saveDebugLog);
  114. }
  115. /**
  116. * Get delimiter.
  117. *
  118. * @return string
  119. */
  120. public function getDelimiter()
  121. {
  122. return $this->delimiter;
  123. }
  124. /**
  125. * Set delimiter.
  126. *
  127. * @param string $pValue Delimiter, defaults to ','
  128. *
  129. * @return CSV
  130. */
  131. public function setDelimiter($pValue)
  132. {
  133. $this->delimiter = $pValue;
  134. return $this;
  135. }
  136. /**
  137. * Get enclosure.
  138. *
  139. * @return string
  140. */
  141. public function getEnclosure()
  142. {
  143. return $this->enclosure;
  144. }
  145. /**
  146. * Set enclosure.
  147. *
  148. * @param string $pValue Enclosure, defaults to "
  149. *
  150. * @return CSV
  151. */
  152. public function setEnclosure($pValue)
  153. {
  154. if ($pValue == '') {
  155. $pValue = null;
  156. }
  157. $this->enclosure = $pValue;
  158. return $this;
  159. }
  160. /**
  161. * Get line ending.
  162. *
  163. * @return string
  164. */
  165. public function getLineEnding()
  166. {
  167. return $this->lineEnding;
  168. }
  169. /**
  170. * Set line ending.
  171. *
  172. * @param string $pValue Line ending, defaults to OS line ending (PHP_EOL)
  173. *
  174. * @return CSV
  175. */
  176. public function setLineEnding($pValue)
  177. {
  178. $this->lineEnding = $pValue;
  179. return $this;
  180. }
  181. /**
  182. * Get whether BOM should be used.
  183. *
  184. * @return bool
  185. */
  186. public function getUseBOM()
  187. {
  188. return $this->useBOM;
  189. }
  190. /**
  191. * Set whether BOM should be used.
  192. *
  193. * @param bool $pValue Use UTF-8 byte-order mark? Defaults to false
  194. *
  195. * @return CSV
  196. */
  197. public function setUseBOM($pValue)
  198. {
  199. $this->useBOM = $pValue;
  200. return $this;
  201. }
  202. /**
  203. * Get whether a separator line should be included.
  204. *
  205. * @return bool
  206. */
  207. public function getIncludeSeparatorLine()
  208. {
  209. return $this->includeSeparatorLine;
  210. }
  211. /**
  212. * Set whether a separator line should be included as the first line of the file.
  213. *
  214. * @param bool $pValue Use separator line? Defaults to false
  215. *
  216. * @return CSV
  217. */
  218. public function setIncludeSeparatorLine($pValue)
  219. {
  220. $this->includeSeparatorLine = $pValue;
  221. return $this;
  222. }
  223. /**
  224. * Get whether the file should be saved with full Excel Compatibility.
  225. *
  226. * @return bool
  227. */
  228. public function getExcelCompatibility()
  229. {
  230. return $this->excelCompatibility;
  231. }
  232. /**
  233. * Set whether the file should be saved with full Excel Compatibility.
  234. *
  235. * @param bool $pValue Set the file to be written as a fully Excel compatible csv file
  236. * Note that this overrides other settings such as useBOM, enclosure and delimiter
  237. *
  238. * @return CSV
  239. */
  240. public function setExcelCompatibility($pValue)
  241. {
  242. $this->excelCompatibility = $pValue;
  243. return $this;
  244. }
  245. /**
  246. * Get sheet index.
  247. *
  248. * @return int
  249. */
  250. public function getSheetIndex()
  251. {
  252. return $this->sheetIndex;
  253. }
  254. /**
  255. * Set sheet index.
  256. *
  257. * @param int $pValue Sheet index
  258. *
  259. * @return CSV
  260. */
  261. public function setSheetIndex($pValue)
  262. {
  263. $this->sheetIndex = $pValue;
  264. return $this;
  265. }
  266. /**
  267. * Write line to CSV file.
  268. *
  269. * @param resource $pFileHandle PHP filehandle
  270. * @param array $pValues Array containing values in a row
  271. */
  272. private function writeLine($pFileHandle, array $pValues)
  273. {
  274. // No leading delimiter
  275. $writeDelimiter = false;
  276. // Build the line
  277. $line = '';
  278. foreach ($pValues as $element) {
  279. // Escape enclosures
  280. $element = str_replace($this->enclosure, $this->enclosure . $this->enclosure, $element);
  281. // Add delimiter
  282. if ($writeDelimiter) {
  283. $line .= $this->delimiter;
  284. } else {
  285. $writeDelimiter = true;
  286. }
  287. // Add enclosed string
  288. $line .= $this->enclosure . $element . $this->enclosure;
  289. }
  290. // Add line ending
  291. $line .= $this->lineEnding;
  292. // Write to file
  293. fwrite($pFileHandle, $line);
  294. }
  295. }