1<?php
2/**
3 * PHPExcel
4 *
5 * Copyright (c) 2006 - 2014 PHPExcel
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
20 *
21 * @category   PHPExcel
22 * @package    PHPExcel_Reader
23 * @copyright  Copyright (c) 2006 - 2014 PHPExcel (http://www.codeplex.com/PHPExcel)
24 * @license    http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt	LGPL
25 * @version    ##VERSION##, ##DATE##
26 */
27
28
29/** PHPExcel root directory */
30if (!defined('PHPEXCEL_ROOT')) {
31	/**
32	 * @ignore
33	 */
34	define('PHPEXCEL_ROOT', dirname(__FILE__) . '/../../');
35	require(PHPEXCEL_ROOT . 'PHPExcel/Autoloader.php');
36}
37
38/**
39 * PHPExcel_Reader_CSV
40 *
41 * @category   PHPExcel
42 * @package    PHPExcel_Reader
43 * @copyright  Copyright (c) 2006 - 2014 PHPExcel (http://www.codeplex.com/PHPExcel)
44 */
45class PHPExcel_Reader_CSV extends PHPExcel_Reader_Abstract implements PHPExcel_Reader_IReader
46{
47	/**
48	 * Input encoding
49	 *
50	 * @access	private
51	 * @var	string
52	 */
53	private $_inputEncoding	= 'UTF-8';
54
55	/**
56	 * Delimiter
57	 *
58	 * @access	private
59	 * @var string
60	 */
61	private $_delimiter		= ',';
62
63	/**
64	 * Enclosure
65	 *
66	 * @access	private
67	 * @var	string
68	 */
69	private $_enclosure		= '"';
70
71	/**
72	 * Line ending
73	 *
74	 * @access	private
75	 * @var	string
76	 */
77	private $_lineEnding	= PHP_EOL;
78
79	/**
80	 * Sheet index to read
81	 *
82	 * @access	private
83	 * @var	int
84	 */
85	private $_sheetIndex	= 0;
86
87	/**
88	 * Load rows contiguously
89	 *
90	 * @access	private
91	 * @var	int
92	 */
93	private $_contiguous	= false;
94
95	/**
96	 * Row counter for loading rows contiguously
97	 *
98	 * @var	int
99	 */
100	private $_contiguousRow	= -1;
101
102
103	/**
104	 * Create a new PHPExcel_Reader_CSV
105	 */
106	public function __construct() {
107		$this->_readFilter		= new PHPExcel_Reader_DefaultReadFilter();
108	}
109
110	/**
111	 * Validate that the current file is a CSV file
112	 *
113	 * @return boolean
114	 */
115	protected function _isValidFormat()
116	{
117		return TRUE;
118	}
119
120	/**
121	 * Set input encoding
122	 *
123	 * @param string $pValue Input encoding
124	 */
125	public function setInputEncoding($pValue = 'UTF-8')
126	{
127		$this->_inputEncoding = $pValue;
128		return $this;
129	}
130
131	/**
132	 * Get input encoding
133	 *
134	 * @return string
135	 */
136	public function getInputEncoding()
137	{
138		return $this->_inputEncoding;
139	}
140
141	/**
142	 * Move filepointer past any BOM marker
143	 *
144	 */
145	protected function _skipBOM()
146	{
147		rewind($this->_fileHandle);
148
149		switch ($this->_inputEncoding) {
150			case 'UTF-8':
151				fgets($this->_fileHandle, 4) == "\xEF\xBB\xBF" ?
152					fseek($this->_fileHandle, 3) : fseek($this->_fileHandle, 0);
153				break;
154			case 'UTF-16LE':
155				fgets($this->_fileHandle, 3) == "\xFF\xFE" ?
156					fseek($this->_fileHandle, 2) : fseek($this->_fileHandle, 0);
157				break;
158			case 'UTF-16BE':
159				fgets($this->_fileHandle, 3) == "\xFE\xFF" ?
160					fseek($this->_fileHandle, 2) : fseek($this->_fileHandle, 0);
161				break;
162			case 'UTF-32LE':
163				fgets($this->_fileHandle, 5) == "\xFF\xFE\x00\x00" ?
164					fseek($this->_fileHandle, 4) : fseek($this->_fileHandle, 0);
165				break;
166			case 'UTF-32BE':
167				fgets($this->_fileHandle, 5) == "\x00\x00\xFE\xFF" ?
168					fseek($this->_fileHandle, 4) : fseek($this->_fileHandle, 0);
169				break;
170			default:
171				break;
172		}
173	}
174
175	/**
176	 * Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns)
177	 *
178	 * @param 	string 		$pFilename
179	 * @throws	PHPExcel_Reader_Exception
180	 */
181	public function listWorksheetInfo($pFilename)
182	{
183		// Open file
184		$this->_openFile($pFilename);
185		if (!$this->_isValidFormat()) {
186			fclose ($this->_fileHandle);
187			throw new PHPExcel_Reader_Exception($pFilename . " is an Invalid Spreadsheet file.");
188		}
189		$fileHandle = $this->_fileHandle;
190
191		// Skip BOM, if any
192		$this->_skipBOM();
193
194		$escapeEnclosures = array( "\\" . $this->_enclosure, $this->_enclosure . $this->_enclosure );
195
196		$worksheetInfo = array();
197		$worksheetInfo[0]['worksheetName'] = 'Worksheet';
198		$worksheetInfo[0]['lastColumnLetter'] = 'A';
199		$worksheetInfo[0]['lastColumnIndex'] = 0;
200		$worksheetInfo[0]['totalRows'] = 0;
201		$worksheetInfo[0]['totalColumns'] = 0;
202
203		// Loop through each line of the file in turn
204		while (($rowData = fgetcsv($fileHandle, 0, $this->_delimiter, $this->_enclosure)) !== FALSE) {
205			$worksheetInfo[0]['totalRows']++;
206			$worksheetInfo[0]['lastColumnIndex'] = max($worksheetInfo[0]['lastColumnIndex'], count($rowData) - 1);
207		}
208
209		$worksheetInfo[0]['lastColumnLetter'] = PHPExcel_Cell::stringFromColumnIndex($worksheetInfo[0]['lastColumnIndex']);
210		$worksheetInfo[0]['totalColumns'] = $worksheetInfo[0]['lastColumnIndex'] + 1;
211
212		// Close file
213		fclose($fileHandle);
214
215		return $worksheetInfo;
216	}
217
218	/**
219	 * Loads PHPExcel from file
220	 *
221	 * @param 	string 		$pFilename
222	 * @return PHPExcel
223	 * @throws PHPExcel_Reader_Exception
224	 */
225	public function load($pFilename)
226	{
227		// Create new PHPExcel
228		$objPHPExcel = new PHPExcel();
229
230		// Load into this instance
231		return $this->loadIntoExisting($pFilename, $objPHPExcel);
232	}
233
234	/**
235	 * Loads PHPExcel from file into PHPExcel instance
236	 *
237	 * @param 	string 		$pFilename
238	 * @param	PHPExcel	$objPHPExcel
239	 * @return 	PHPExcel
240	 * @throws 	PHPExcel_Reader_Exception
241	 */
242	public function loadIntoExisting($pFilename, PHPExcel $objPHPExcel)
243	{
244		$lineEnding = ini_get('auto_detect_line_endings');
245		ini_set('auto_detect_line_endings', true);
246
247		// Open file
248		$this->_openFile($pFilename);
249		if (!$this->_isValidFormat()) {
250			fclose ($this->_fileHandle);
251			throw new PHPExcel_Reader_Exception($pFilename . " is an Invalid Spreadsheet file.");
252		}
253		$fileHandle = $this->_fileHandle;
254
255		// Skip BOM, if any
256		$this->_skipBOM();
257
258		// Create new PHPExcel object
259		while ($objPHPExcel->getSheetCount() <= $this->_sheetIndex) {
260			$objPHPExcel->createSheet();
261		}
262		$sheet = $objPHPExcel->setActiveSheetIndex($this->_sheetIndex);
263
264		$escapeEnclosures = array( "\\" . $this->_enclosure,
265								   $this->_enclosure . $this->_enclosure
266								 );
267
268		// Set our starting row based on whether we're in contiguous mode or not
269		$currentRow = 1;
270		if ($this->_contiguous) {
271			$currentRow = ($this->_contiguousRow == -1) ? $sheet->getHighestRow(): $this->_contiguousRow;
272		}
273
274		// Loop through each line of the file in turn
275		while (($rowData = fgetcsv($fileHandle, 0, $this->_delimiter, $this->_enclosure)) !== FALSE) {
276			$columnLetter = 'A';
277			foreach($rowData as $rowDatum) {
278				if ($rowDatum != '' && $this->_readFilter->readCell($columnLetter, $currentRow)) {
279					// Unescape enclosures
280					$rowDatum = str_replace($escapeEnclosures, $this->_enclosure, $rowDatum);
281
282					// Convert encoding if necessary
283					if ($this->_inputEncoding !== 'UTF-8') {
284						$rowDatum = PHPExcel_Shared_String::ConvertEncoding($rowDatum, 'UTF-8', $this->_inputEncoding);
285					}
286
287					// Set cell value
288					$sheet->getCell($columnLetter . $currentRow)->setValue($rowDatum);
289				}
290				++$columnLetter;
291			}
292			++$currentRow;
293		}
294
295		// Close file
296		fclose($fileHandle);
297
298		if ($this->_contiguous) {
299			$this->_contiguousRow = $currentRow;
300		}
301
302		ini_set('auto_detect_line_endings', $lineEnding);
303
304		// Return
305		return $objPHPExcel;
306	}
307
308	/**
309	 * Get delimiter
310	 *
311	 * @return string
312	 */
313	public function getDelimiter() {
314		return $this->_delimiter;
315	}
316
317	/**
318	 * Set delimiter
319	 *
320	 * @param	string	$pValue		Delimiter, defaults to ,
321	 * @return	PHPExcel_Reader_CSV
322	 */
323	public function setDelimiter($pValue = ',') {
324		$this->_delimiter = $pValue;
325		return $this;
326	}
327
328	/**
329	 * Get enclosure
330	 *
331	 * @return string
332	 */
333	public function getEnclosure() {
334		return $this->_enclosure;
335	}
336
337	/**
338	 * Set enclosure
339	 *
340	 * @param	string	$pValue		Enclosure, defaults to "
341	 * @return PHPExcel_Reader_CSV
342	 */
343	public function setEnclosure($pValue = '"') {
344		if ($pValue == '') {
345			$pValue = '"';
346		}
347		$this->_enclosure = $pValue;
348		return $this;
349	}
350
351	/**
352	 * Get line ending
353	 *
354	 * @return string
355	 */
356	public function getLineEnding() {
357		return $this->_lineEnding;
358	}
359
360	/**
361	 * Set line ending
362	 *
363	 * @param	string	$pValue		Line ending, defaults to OS line ending (PHP_EOL)
364	 * @return PHPExcel_Reader_CSV
365	 */
366	public function setLineEnding($pValue = PHP_EOL) {
367		$this->_lineEnding = $pValue;
368		return $this;
369	}
370
371	/**
372	 * Get sheet index
373	 *
374	 * @return integer
375	 */
376	public function getSheetIndex() {
377		return $this->_sheetIndex;
378	}
379
380	/**
381	 * Set sheet index
382	 *
383	 * @param	integer		$pValue		Sheet index
384	 * @return PHPExcel_Reader_CSV
385	 */
386	public function setSheetIndex($pValue = 0) {
387		$this->_sheetIndex = $pValue;
388		return $this;
389	}
390
391	/**
392	 * Set Contiguous
393	 *
394	 * @param boolean $contiguous
395	 */
396	public function setContiguous($contiguous = FALSE)
397	{
398		$this->_contiguous = (bool) $contiguous;
399		if (!$contiguous) {
400			$this->_contiguousRow = -1;
401		}
402
403		return $this;
404	}
405
406	/**
407	 * Get Contiguous
408	 *
409	 * @return boolean
410	 */
411	public function getContiguous() {
412		return $this->_contiguous;
413	}
414
415}
416