PageRenderTime 34ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/vendor/maatwebsite/excel/src/Maatwebsite/Excel/Parsers/ExcelParser.php

https://gitlab.com/kimting254/wbms
PHP | 667 lines | 314 code | 97 blank | 256 comment | 26 complexity | adc5b24c8da707304717fc237cf32e91 MD5 | raw file
  1. <?php namespace Maatwebsite\Excel\Parsers;
  2. use Carbon\Carbon;
  3. use PHPExcel_Cell;
  4. use PHPExcel_Exception;
  5. use PHPExcel_Shared_Date;
  6. use Illuminate\Support\Str;
  7. use PHPExcel_Style_NumberFormat;
  8. use Illuminate\Support\Facades\Config;
  9. use Maatwebsite\Excel\Collections\RowCollection;
  10. use Maatwebsite\Excel\Collections\CellCollection;
  11. use Maatwebsite\Excel\Collections\SheetCollection;
  12. use Maatwebsite\Excel\Exceptions\LaravelExcelException;
  13. /**
  14. *
  15. * LaravelExcel Excel Parser
  16. *
  17. * @category Laravel Excel
  18. * @version 1.0.0
  19. * @package maatwebsite/excel
  20. * @copyright Copyright (c) 2013 - 2014 Maatwebsite (http://www.maatwebsite.nl)
  21. * @author Maatwebsite <info@maatwebsite.nl>
  22. * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL
  23. */
  24. class ExcelParser {
  25. /**
  26. * If file is parsed
  27. * @var boolean
  28. */
  29. public $isParsed = false;
  30. /**
  31. * Reader object
  32. * @var LaravelExcelReader
  33. */
  34. protected $reader;
  35. /**
  36. * Excel object
  37. * @var PHPExcel
  38. */
  39. protected $excel;
  40. /**
  41. * Worksheet object
  42. * @var LaravelExcelWorksheet
  43. */
  44. protected $worksheet;
  45. /**
  46. * Row object
  47. * @var PHPExcel_Worksheet_Row
  48. */
  49. protected $row;
  50. /**
  51. * Cell object
  52. * @var PHPExcel_Cell
  53. */
  54. protected $cell;
  55. /**
  56. * Indices
  57. * @var array
  58. */
  59. protected $indices;
  60. /**
  61. * Columns we want to fetch
  62. * @var array
  63. */
  64. protected $columns = array();
  65. /**
  66. * Row counter
  67. * @var integer
  68. */
  69. protected $currentRow = 1;
  70. /**
  71. * Default startrow
  72. * @var integer
  73. */
  74. protected $defaultStartRow = 1;
  75. /**
  76. * Construct excel parser
  77. * @param LaravelExcelReader $reader
  78. * @return \Maatwebsite\Excel\Parsers\ExcelParser
  79. */
  80. public function __construct($reader)
  81. {
  82. $this->reader = $reader;
  83. $this->excel = $reader->excel;
  84. $this->defaultStartRow = $this->currentRow = Config::get('excel.import.startRow', 1);
  85. // Reset
  86. $this->reset();
  87. }
  88. /**
  89. * Parse the file
  90. * @param array $columns
  91. * @return SheetCollection
  92. */
  93. public function parseFile($columns = array())
  94. {
  95. // Init new sheet collection
  96. $workbook = new SheetCollection();
  97. // Set the selected columns
  98. $this->setSelectedColumns($columns);
  99. // If not parsed yet
  100. if ( !$this->isParsed )
  101. {
  102. // Set worksheet count
  103. $this->w = 0;
  104. // Get selected sheets
  105. $iterator = $this->excel->getWorksheetIterator();
  106. // Loop through the worksheets
  107. foreach ($iterator as $this->worksheet)
  108. {
  109. // Check if the sheet might have been selected by it's index
  110. if ( $this->reader->isSelectedByIndex($iterator->key()) )
  111. {
  112. // Parse the worksheet
  113. $worksheet = $this->parseWorksheet();
  114. // If multiple sheets
  115. if ( $this->parseAsMultiple() )
  116. {
  117. // Push every sheet
  118. $workbook->push($worksheet);
  119. $workbook->setTitle($this->excel->getProperties()->getTitle());
  120. }
  121. else
  122. {
  123. // Ignore the sheet collection
  124. $workbook = $worksheet;
  125. break;
  126. }
  127. }
  128. $this->w++;
  129. }
  130. }
  131. $this->isParsed = true;
  132. // Return itself
  133. return $workbook;
  134. }
  135. /**
  136. * Check if we want to parse it as multiple sheets
  137. * @return boolean
  138. */
  139. protected function parseAsMultiple()
  140. {
  141. return ($this->excel->getSheetCount() > 1 && count($this->reader->getSelectedSheetIndices()) !== 1)
  142. || Config::get('excel.import.force_sheets_collection', false);
  143. }
  144. /**
  145. * Parse the worksheet
  146. * @return RowCollection
  147. */
  148. protected function parseWorksheet()
  149. {
  150. // Set the active worksheet
  151. $this->excel->setActiveSheetIndex($this->w);
  152. // Fetch the labels
  153. $this->indices = $this->reader->hasHeading() ? $this->getIndices() : array();
  154. // Parse the rows
  155. return $this->parseRows();
  156. }
  157. /**
  158. * Get the indices
  159. * @return array
  160. */
  161. protected function getIndices()
  162. {
  163. // Fetch the first row
  164. $this->row = $this->worksheet->getRowIterator($this->defaultStartRow)->current();
  165. // Set empty labels array
  166. $this->indices = array();
  167. // Loop through the cells
  168. foreach ($this->row->getCellIterator() as $this->cell)
  169. {
  170. $this->indices[] = $this->getIndex($this->cell);
  171. }
  172. // Return the labels
  173. return $this->indices;
  174. }
  175. /**
  176. * Get index
  177. * @param $cell
  178. * @return string
  179. */
  180. protected function getIndex($cell)
  181. {
  182. // Get heading type
  183. $config = Config::get('excel.import.heading', true);
  184. $config = $config === true ? 'slugged' : $config;
  185. // Get value
  186. $value = $this->getOriginalIndex($cell);
  187. switch ($config)
  188. {
  189. case 'slugged':
  190. return $this->getSluggedIndex($value, Config::get('excel.import.to_ascii', true));
  191. break;
  192. case 'slugged_with_count':
  193. $index = $this->getSluggedIndex($value, Config::get('excel.import.to_ascii', true));
  194. if(in_array($index,$this->indices)){
  195. $index = $this->appendOrIncreaseStringCount($index);
  196. }
  197. return $index;
  198. break;
  199. case 'ascii':
  200. return $this->getAsciiIndex($value);
  201. break;
  202. case 'hashed':
  203. return $this->getHashedIndex($value);
  204. break;
  205. case 'trans':
  206. return $this->getTranslatedIndex($value);
  207. break;
  208. case 'original':
  209. return $value;
  210. break;
  211. }
  212. }
  213. /**
  214. * Append or increase the count at the String like: test to test_1
  215. * @param string $index
  216. * @return string
  217. */
  218. protected function appendOrIncreaseStringCount($index)
  219. {
  220. do {
  221. if (preg_match("/(\d+)$/",$index,$matches) === 1)
  222. {
  223. // increase +1
  224. $index = preg_replace_callback( "/(\d+)$/",
  225. function ($matches) {
  226. return ++$matches[1];
  227. }, $index);
  228. }
  229. else
  230. {
  231. $index .= '_1';
  232. }
  233. } while(in_array($index,$this->indices));
  234. return $index;
  235. }
  236. /**
  237. * Get slugged index
  238. * @param string $value
  239. * @param bool $ascii
  240. * @return string
  241. */
  242. protected function getSluggedIndex($value, $ascii = false)
  243. {
  244. // Get original
  245. $separator = $this->reader->getSeparator();
  246. // Convert to ascii when needed
  247. if ( $ascii )
  248. $value = $this->getAsciiIndex($value);
  249. // Convert all dashes/underscores into separator
  250. $flip = $separator == '-' ? '_' : '-';
  251. $value = preg_replace('![' . preg_quote($flip) . ']+!u', $separator, $value);
  252. // Remove all characters that are not the separator, letters, numbers, or whitespace.
  253. $value = preg_replace('![^' . preg_quote($separator) . '\pL\pN\s]+!u', '', mb_strtolower($value));
  254. // Replace all separator characters and whitespace by a single separator
  255. $value = preg_replace('![' . preg_quote($separator) . '\s]+!u', $separator, $value);
  256. return trim($value, $separator);
  257. }
  258. /**
  259. * Get ASCII index
  260. * @param string $value
  261. * @return string
  262. */
  263. protected function getAsciiIndex($value)
  264. {
  265. return Str::ascii($value);
  266. }
  267. /**
  268. * Hahsed index
  269. * @param string $value
  270. * @return string
  271. */
  272. protected function getHashedIndex($value)
  273. {
  274. return md5($value);
  275. }
  276. /**
  277. * Get translated index
  278. * @param string $value
  279. * @return string
  280. */
  281. protected function getTranslatedIndex($value)
  282. {
  283. return trans($value);
  284. }
  285. /**
  286. * Get orignal indice
  287. * @param $cell
  288. * @return string
  289. */
  290. protected function getOriginalIndex($cell)
  291. {
  292. return $cell->getValue();
  293. }
  294. /**
  295. * Parse the rows
  296. * @return RowCollection
  297. */
  298. protected function parseRows()
  299. {
  300. // Set empty parsedRow array
  301. $parsedRows = new RowCollection();
  302. // set sheet title
  303. $parsedRows->setTitle($this->excel->getActiveSheet()->getTitle());
  304. // Get the start row
  305. $startRow = $this->getStartRow();
  306. try {
  307. $rows = $this->worksheet->getRowIterator($startRow);
  308. } catch(PHPExcel_Exception $e) {
  309. $rows = [];
  310. }
  311. // Loop through the rows inside the worksheet
  312. foreach ($rows as $this->row)
  313. {
  314. // Limit the results when needed
  315. if ( $this->hasReachedLimit() )
  316. break;
  317. // Push the parsed cells inside the parsed rows
  318. $parsedRows->push($this->parseCells());
  319. // Count the rows
  320. $this->currentRow++;
  321. }
  322. // Return the parsed array
  323. return $parsedRows;
  324. }
  325. /**
  326. * Get the startrow
  327. * @return integer
  328. */
  329. protected function getStartRow()
  330. {
  331. // Set default start row
  332. $startRow = $this->defaultStartRow;
  333. // If the reader has a heading, skip the first row
  334. if ( $this->reader->hasHeading() )
  335. $startRow++;
  336. // Get the amount of rows to skip
  337. $skip = $this->reader->getSkip();
  338. // If we want to skip rows, add the amount of rows
  339. if ( $skip > 0 )
  340. $startRow = $startRow + $skip;
  341. // Return the startrow
  342. return $startRow;
  343. }
  344. /**
  345. * Check for the limit
  346. * @return boolean
  347. */
  348. protected function hasReachedLimit()
  349. {
  350. // Get skip
  351. $limit = $this->reader->getLimit();
  352. // If we have a limit, check if we hit this limit
  353. return $limit && $this->currentRow > $limit ? true : false;
  354. }
  355. /**
  356. * Parse the cells of the given row
  357. * @return CellCollection
  358. */
  359. protected function parseCells()
  360. {
  361. $i = 0;
  362. $parsedCells = array();
  363. try {
  364. // Set the cell iterator
  365. $cellIterator = $this->row->getCellIterator();
  366. // Ignore empty cells if needed
  367. $cellIterator->setIterateOnlyExistingCells($this->reader->needsIgnoreEmpty());
  368. // Foreach cells
  369. foreach ($cellIterator as $this->cell)
  370. {
  371. // Check how we need to save the parsed array
  372. $index = ($this->reader->hasHeading() && isset($this->indices[$i])) ? $this->indices[$i] : $this->getIndexFromColumn();
  373. // Check if we want to select this column
  374. if ( $this->cellNeedsParsing($index) )
  375. {
  376. // Set the value
  377. $parsedCells[$index] = $this->parseCell($index);
  378. }
  379. $i++;
  380. }
  381. } catch (PHPExcel_Exception $e) {
  382. // silently ignore the 'No cells exist within the specified range' error, but rethrow any others
  383. if ($e->getMessage() != 'No cells exist within the specified range') {
  384. throw $e;
  385. }
  386. // make sure that we return an empty CellCollection
  387. $parsedCells = array();
  388. }
  389. // Return array with parsed cells
  390. return new CellCollection($parsedCells);
  391. }
  392. /**
  393. * Parse a single cell
  394. * @param integer $index
  395. * @return string
  396. */
  397. protected function parseCell($index)
  398. {
  399. // If the cell is a date time
  400. if ( $this->cellIsDate($index) )
  401. {
  402. // Parse the date
  403. return $this->parseDate();
  404. }
  405. // Check if we want calculated values or not
  406. elseif ( $this->reader->needsCalculation() )
  407. {
  408. // Get calculated value
  409. return $this->getCalculatedValue();
  410. }
  411. else
  412. {
  413. // Get real value
  414. return $this->getCellValue();
  415. }
  416. }
  417. /**
  418. * Return the cell value
  419. * @return string
  420. */
  421. protected function getCellValue()
  422. {
  423. $value = $this->cell->getValue();
  424. return $this->encode($value);
  425. }
  426. /**
  427. * Get the calculated value
  428. * @return string
  429. */
  430. protected function getCalculatedValue()
  431. {
  432. $value = $this->cell->getCalculatedValue();
  433. return $this->encode($value);
  434. }
  435. /**
  436. * Encode with iconv
  437. * @param string $value
  438. * @return string
  439. */
  440. protected function encode($value)
  441. {
  442. // Get input and output encoding
  443. list($input, $output) = array_values(Config::get('excel.import.encoding', array('UTF-8', 'UTF-8')));
  444. // If they are the same, return the value
  445. if ( $input == $output )
  446. return $value;
  447. // Encode
  448. return iconv($input, $output, $value);
  449. }
  450. /**
  451. * Parse the date
  452. * @return Carbon\Carbon|string
  453. */
  454. protected function parseDate()
  455. {
  456. // If the date needs formatting
  457. if ( $this->reader->needsDateFormatting() )
  458. {
  459. // Parse the date with carbon
  460. return $this->parseDateAsCarbon();
  461. }
  462. else
  463. {
  464. // Parse the date as a normal string
  465. return $this->parseDateAsString();
  466. }
  467. }
  468. /**
  469. * Parse and return carbon object or formatted time string
  470. * @return Carbon\Carbon
  471. */
  472. protected function parseDateAsCarbon()
  473. {
  474. // If has a date
  475. if ( $cellContent = $this->cell->getCalculatedValue() )
  476. {
  477. // Convert excel time to php date object
  478. $date = PHPExcel_Shared_Date::ExcelToPHPObject($this->cell->getCalculatedValue())->format('Y-m-d H:i:s');
  479. // Parse with carbon
  480. $date = Carbon::parse($date);
  481. // Format the date if wanted
  482. return $this->reader->getDateFormat() ? $date->format($this->reader->getDateFormat()) : $date;
  483. }
  484. return null;
  485. }
  486. /**
  487. * Return date string
  488. * @return string
  489. */
  490. protected function parseDateAsString()
  491. {
  492. //Format the date to a formatted string
  493. return (string) PHPExcel_Style_NumberFormat::toFormattedString(
  494. $this->cell->getCalculatedValue(),
  495. $this->cell->getWorksheet()->getParent()
  496. ->getCellXfByIndex($this->cell->getXfIndex())
  497. ->getNumberFormat()
  498. ->getFormatCode()
  499. );
  500. }
  501. /**
  502. * Check if cell is a date
  503. * @param integer $index
  504. * @return boolean
  505. */
  506. protected function cellIsDate($index)
  507. {
  508. // if is a date or if is a date column
  509. if ( $this->reader->getDateColumns() )
  510. {
  511. return in_array($index, $this->reader->getDateColumns());
  512. }
  513. else
  514. {
  515. return PHPExcel_Shared_Date::isDateTime($this->cell);
  516. }
  517. }
  518. /**
  519. * Check if cells needs parsing
  520. * @return array
  521. */
  522. protected function cellNeedsParsing($index)
  523. {
  524. // if no columns are selected or if the column is selected
  525. return !$this->hasSelectedColumns() || ($this->hasSelectedColumns() && in_array($index, $this->getSelectedColumns()));
  526. }
  527. /**
  528. * Get the cell index from column
  529. * @return integer
  530. */
  531. protected function getIndexFromColumn()
  532. {
  533. return PHPExcel_Cell::columnIndexFromString($this->cell->getColumn());
  534. }
  535. /**
  536. * Set selected columns
  537. * @param array $columns
  538. */
  539. protected function setSelectedColumns($columns = array())
  540. {
  541. // Set the columns
  542. $this->columns = $columns;
  543. }
  544. /**
  545. * Check if we have selected columns
  546. * @return boolean
  547. */
  548. protected function hasSelectedColumns()
  549. {
  550. return !empty($this->columns);
  551. }
  552. /**
  553. * Set selected columns
  554. * @return array
  555. */
  556. protected function getSelectedColumns()
  557. {
  558. // Set the columns
  559. return $this->columns;
  560. }
  561. /**
  562. * Reset
  563. * @return void
  564. */
  565. protected function reset()
  566. {
  567. $this->indices = array();
  568. $this->isParsed = false;
  569. }
  570. }