PHPExcelReader.php 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. <?php
  2. /**
  3. * PHPExcelReader class
  4. * @version 1.0.0
  5. * @author Janson Leung
  6. */
  7. /** PHPExcel root directory */
  8. if (! defined('PHPEXCEL_ROOT')) {
  9. define('PHPEXCEL_ROOT', dirname(__FILE__) . '/');
  10. require(PHPEXCEL_ROOT . 'PHPExcel/Autoloader.php');
  11. }
  12. class PHPExcelReader implements SeekableIterator, Countable {
  13. const TYPE_XLSX = 'XLSX';
  14. const TYPE_XLS = 'XLS';
  15. const TYPE_CSV = 'CSV';
  16. private $handle;
  17. private $type;
  18. private $index = 0;
  19. /**
  20. * @param string $filePath Path to file
  21. * @param string $originalFileName Original filename (in case of an uploaded file), used to determine file type,
  22. * optional
  23. * @param string $mimeType MIME type from an upload, used to determine file type, optional
  24. * @throws Exception
  25. */
  26. public function __construct($filePath, $originalFileName = '', $mimeType = '') {
  27. if (! is_readable($filePath)) {
  28. throw new Exception('PHPExcel_Reader: File (' . $filePath . ') not readable');
  29. }
  30. $defaultTimeZone = @date_default_timezone_get();
  31. if ($defaultTimeZone) {
  32. date_default_timezone_set($defaultTimeZone);
  33. }
  34. // Checking the other parameters for correctness
  35. // This should be a check for string but we're lenient
  36. if (! empty($originalFileName) && ! is_scalar($originalFileName)) {
  37. throw new Exception('PHPExcel_Reader: Original file (2nd parameter) is not a string or a scalar value.');
  38. }
  39. if (! empty($mimeType) && ! is_scalar($mimeType)) {
  40. throw new Exception('PHPExcel_Reader: Mime type (3nd parameter) is not a string or a scalar value.');
  41. }
  42. // 1. Determine type
  43. if (! $originalFileName) {
  44. $originalFileName = $filePath;
  45. }
  46. $mimeType = $mimeType ?: mime_content_type($filePath);
  47. $Extension = strtolower(pathinfo($originalFileName, PATHINFO_EXTENSION));
  48. if ($mimeType) {
  49. switch ($mimeType) {
  50. case 'application/octet-stream':
  51. $this->type = $Extension == 'xlsx' ? self::TYPE_XLSX : self::TYPE_CSV;
  52. break;
  53. case 'text/x-comma-separated-values':
  54. case 'text/comma-separated-values':
  55. case 'application/x-csv':
  56. case 'text/x-csv':
  57. case 'text/csv':
  58. case 'application/csv':
  59. case 'application/vnd.msexcel':
  60. case 'text/plain':
  61. $this->type = self::TYPE_CSV;
  62. break;
  63. case 'application/msexcel':
  64. case 'application/x-msexcel':
  65. case 'application/x-ms-excel':
  66. case 'application/x-excel':
  67. case 'application/x-dos_ms_excel':
  68. case 'application/xls':
  69. case 'application/x-xls':
  70. case 'application/download':
  71. case 'application/vnd.ms-office':
  72. case 'application/msword':
  73. case 'application/xlt':
  74. $this->type = self::TYPE_XLS;
  75. break;
  76. case 'application/vnd.ms-excel':
  77. case 'application/excel':
  78. $this->type = $Extension == 'csv' ? self::TYPE_CSV : self::TYPE_XLS;
  79. break;
  80. case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet':
  81. case 'application/vnd.openxmlformats-officedocument.spreadsheetml.template':
  82. case 'application/zip':
  83. case 'application/x-zip':
  84. case 'application/xlsx':
  85. case 'application/xltx':
  86. $this->type = self::TYPE_XLSX;
  87. break;
  88. }
  89. }
  90. if (! $this->type) {
  91. switch ($Extension) {
  92. case 'xlsx':
  93. case 'xltx': // XLSX template
  94. case 'xlsm': // Macro-enabled XLSX
  95. case 'xltm': // Macro-enabled XLSX template
  96. $this->type = self::TYPE_XLSX;
  97. break;
  98. case 'xls':
  99. case 'xlt':
  100. $this->type = self::TYPE_XLS;
  101. break;
  102. default:
  103. $this->type = self::TYPE_CSV;
  104. break;
  105. }
  106. }
  107. // Pre-checking XLS files, in case they are renamed CSV or XLSX files
  108. if ($this->type == self::TYPE_XLS) {
  109. $this->handle = new PHPExcel_Reader_XLS($filePath);
  110. if ($this->handle->error) {
  111. $this->handle->__destruct();
  112. if (is_resource($Ziphandle = zip_open($filePath))) {
  113. $this->type = self::TYPE_XLSX;
  114. zip_close($Ziphandle);
  115. } else {
  116. $this->type = self::TYPE_CSV;
  117. }
  118. }
  119. }
  120. // 2. Create handle
  121. switch ($this->type) {
  122. case self::TYPE_XLSX:
  123. $this->handle = new PHPExcel_Reader_XLSX($filePath);
  124. break;
  125. case self::TYPE_CSV:
  126. $this->handle = new PHPExcel_Reader_CSV($filePath, 1);
  127. break;
  128. case self::TYPE_XLS:
  129. // Everything already happens above
  130. break;
  131. }
  132. }
  133. /**
  134. * get the type of file
  135. * @return string
  136. */
  137. public function getFileType() {
  138. return $this->type;
  139. }
  140. /**
  141. * Gets information about separate sheets in the given file
  142. * @return array Associative array where key is sheet index and value is sheet name
  143. */
  144. public function Sheets() {
  145. return $this->handle->Sheets();
  146. }
  147. /**
  148. * Changes the current sheet to another from the file.
  149. * Note that changing the sheet will rewind the file to the beginning, even if
  150. * the current sheet index is provided.
  151. *
  152. * @param int $index Sheet index
  153. *
  154. * @return bool True if sheet could be changed to the specified one,
  155. * false if not (for example, if incorrect index was provided.
  156. */
  157. public function ChangeSheet($index) {
  158. return $this->handle->ChangeSheet($index);
  159. }
  160. /**
  161. * Rewind the Iterator to the first element.
  162. * Similar to the reset() function for arrays in PHP
  163. */
  164. public function rewind() {
  165. $this->index = 0;
  166. if ($this->handle) {
  167. $this->handle->rewind();
  168. }
  169. }
  170. /**
  171. * Return the current element.
  172. * Similar to the current() function for arrays in PHP
  173. * @return mixed current element from the collection
  174. */
  175. public function current() {
  176. if ($this->handle) {
  177. return $this->handle->current();
  178. }
  179. return null;
  180. }
  181. /**
  182. * Move forward to next element.
  183. * Similar to the next() function for arrays in PHP
  184. */
  185. public function next() {
  186. if ($this->handle) {
  187. $this->index++;
  188. return $this->handle->next();
  189. }
  190. return null;
  191. }
  192. /**
  193. * Return the identifying key of the current element.
  194. * Similar to the key() function for arrays in PHP
  195. * @return mixed either an integer or a string
  196. */
  197. public function key() {
  198. if ($this->handle) {
  199. return $this->handle->key();
  200. }
  201. return null;
  202. }
  203. /**
  204. * Check if there is a current element after calls to rewind() or next().
  205. * Used to check if we've iterated to the end of the collection
  206. * @return boolean FALSE if there's nothing more to iterate over
  207. */
  208. public function valid() {
  209. if ($this->handle) {
  210. return $this->handle->valid();
  211. }
  212. return false;
  213. }
  214. /**
  215. * total of file number
  216. * return int
  217. */
  218. public function count() {
  219. if ($this->handle) {
  220. return $this->handle->count();
  221. }
  222. return 0;
  223. }
  224. /**
  225. * Method for SeekableIterator interface. Takes a posiiton and traverses the file to that position
  226. * The value can be retrieved with a `current()` call afterwards.
  227. *
  228. * @param int $position position in file
  229. * @return null
  230. */
  231. public function seek($position) {
  232. if (! $this->handle) {
  233. throw new OutOfBoundsException('PHPExcel_Reader: No file opened');
  234. }
  235. $Currentindex = $this->handle->key();
  236. if ($Currentindex != $position) {
  237. if ($position < $Currentindex || is_null($Currentindex) || $position == 0) {
  238. $this->rewind();
  239. }
  240. while ($this->handle->valid() && ($position > $this->handle->key())) {
  241. $this->handle->next();
  242. }
  243. if (! $this->handle->valid()) {
  244. throw new OutOfBoundsException('PHPExcel_Reader: position ' . $position . ' not found');
  245. }
  246. }
  247. return null;
  248. }
  249. }