/src/sb/Excel/Writer.php

https://github.com/surebert/surebert-framework · PHP · 244 lines · 130 code · 34 blank · 80 comment · 16 complexity · 85c428e7fa869394f3caffe984fbb7aa MD5 · raw file

  1. <?php
  2. /**
  3. * Writes simple excel files based on http://code.google.com/p/hexcel/ @copyright Adrian Duffell 2007
  4. * @package Excel
  5. *
  6. */
  7. namespace sb\Excel;
  8. class Writer
  9. {
  10. /**
  11. * The beginning of file marker
  12. * @var string
  13. */
  14. protected $bof;
  15. /**
  16. * The end of file marker
  17. * @var string
  18. */
  19. protected $eof;
  20. /**
  21. * The contents of the file as it is being constructed
  22. * @var string
  23. */
  24. protected $contents;
  25. /**
  26. * Creates a new xls file for output or to save
  27. *
  28. * @example
  29. * <code>
  30. * $excel = new \sb\Excel_Writer();
  31. * $excel->setCell('A1', 'Hello World!');
  32. * $excel->setCell('D1', 'Hello World!');
  33. * $excel->setColumn('C', Array(1,2,3,4));
  34. * $excel->outputWithHeaders('hello.xls');
  35. * </code>
  36. */
  37. public function __construct()
  38. {
  39. $this->bof = \pack("s*", 0x809, 0x08, 0x00, 0x10, 0x0042, 0x04E4);
  40. $this->eof = \pack("s*", 0x0A, 0x00);
  41. $this->contents = $this->bof;
  42. }
  43. /**
  44. * Set the value of an individual cell
  45. *
  46. * @param mixed An excel cell reference such as A1, or an array in the
  47. * format of ($row, $col) usign zero-based integers
  48. * @param mixed the value to put in this cell
  49. * @param the type of value (string|integer). Autodetects by default
  50. * @return boolean Success
  51. * */
  52. public function setCell($cell, $value, $type = "auto")
  53. {
  54. if ($type == "auto") {
  55. $type = \gettype($value);
  56. }
  57. if (\is_array($cell)) {
  58. $parts["row"] = $cell[0];
  59. $parts["col"] = $cell[1];
  60. } else {
  61. $parts = $this->refToArray($cell);
  62. }
  63. if (!\is_array($parts)) {
  64. \trigger_error("Cell reference should be in the format A1 or array(0,0).", E_USER_ERROR);
  65. return false;
  66. }
  67. $row = $parts["row"];
  68. $col = $parts["col"];
  69. switch ($type) {
  70. case "string" :
  71. /**
  72. * @todo it would be nice if we were able to keep that
  73. * characters UTF-8 or unicode
  74. */
  75. $value = \mb_convert_encoding($value, "Windows-1252", "UTF-8");
  76. $length = \mb_strlen($value, "Windows-1252");
  77. if ($length > 255) {
  78. \trigger_error("String '$value' is too long. "
  79. ."Please keep to a max of 255 characters.",
  80. E_USER_ERROR);
  81. return false;
  82. }
  83. $this->contents .= \pack("s*", 0x0204, 8 + $length, $row, $col, 0x00, $length);
  84. $this->contents .= $value;
  85. break;
  86. case "integer" :
  87. $this->contents .= \pack("s*", 0x0203, 14, $row, $col, 0x00);
  88. $this->contents .= \pack("d", $value);
  89. break;
  90. }
  91. return true;
  92. }
  93. /**
  94. * Set the values for a row
  95. *
  96. * @param integer The Excel row number to place these values
  97. * @param array An array of values
  98. * @return boolean Success
  99. * */
  100. public function setRow($row, $values)
  101. {
  102. if (!\is_array($values)) {
  103. \trigger_error("Values must be an array.", E_USER_ERROR);
  104. return false;
  105. }
  106. if (intval($row) < 1) {
  107. \trigger_error("Row number must be an integer greater than 1.", E_USER_ERROR);
  108. return false;
  109. }
  110. $i = 0;
  111. foreach ($values as $value) {
  112. $this->setCell(array($row - 1, $i), $value);
  113. $i++;
  114. }
  115. return true;
  116. }
  117. /**
  118. * undocumented function
  119. *
  120. * @param string The Excel column letter
  121. * @param array An array of values
  122. * @return Success
  123. * */
  124. public function setColumn($col, $values)
  125. {
  126. if (!\is_array($values)) {
  127. \trigger_error("Values must be an array.", E_USER_ERROR);
  128. return false;
  129. }
  130. if (\is_numeric($col)) {
  131. \trigger_error("Column must be a letter, eg column D.", E_USER_ERROR);
  132. return false;
  133. }
  134. //todo check array
  135. $i = 0;
  136. foreach ($values as $value) {
  137. $this->setCell($col . ($i + 1), $value);
  138. $i++;
  139. }
  140. return true;
  141. }
  142. /**
  143. * Stream this file over HTTP
  144. *
  145. * @return void
  146. * */
  147. public function outputWithHeaders($filename = 'output.xls')
  148. {
  149. \header("Expires: " . date("r", 0));
  150. \header("Last-Modified: " . gmdate("r") . " GMT");
  151. \header("Content-Type: application/x-msexcel");
  152. \header("Content-Disposition: attachment; filename=" . $filename);
  153. echo $this->__toString();
  154. }
  155. /**
  156. * Save the contents to file
  157. * @param string $filepath The filepath to save to
  158. * @return boolean
  159. */
  160. public function toFile($filepath)
  161. {
  162. if (\is_file($filepath)) {
  163. return \file_put_contents($filepath, $this->__toString());
  164. }
  165. return false;
  166. }
  167. /**
  168. * Retrieve the xls file contents
  169. *
  170. * @return an XLS string
  171. * */
  172. public function __toString()
  173. {
  174. return $this->contents . $this->eof;
  175. }
  176. /**
  177. * Convert a Excel Cell Reference to an array of component integers -- A5 becomes (6, 0);
  178. *
  179. * @param string An Excel cell reference such as A1, B7
  180. * @return array An associative array of row/column integers
  181. * */
  182. protected function refToArray($ref)
  183. {
  184. $offset = 64;
  185. $len = strlen($ref);
  186. $div = 0;
  187. for ($i = 0; $i < $len; $i++) {
  188. $char = substr($ref, $i, 1);
  189. if (is_numeric($char)) {
  190. $div = $i;
  191. break;
  192. }
  193. }
  194. if ($div < 1) {
  195. return false;
  196. }
  197. $row = substr($ref, $div);
  198. $place_col = 0;
  199. $col = 0;
  200. for ($i = $div - 1; $i >= 0; $i--) {
  201. $place_weight = pow(26, $place_col);
  202. $place_value = ord(substr($ref, $i, 1)) - $offset;
  203. $col += $place_value * $place_weight;
  204. $place_col++;
  205. }
  206. //return as 0 based
  207. return array("row" => $row - 1, "col" => $col - 1);
  208. }
  209. }