Location: PHPKode > scripts > b3rtCSVReader > b3rtcsvreader3/class.b3rtCSVReader.php
<?php
/*
*	b3rtCSVReader 1.03
*	===========
*	Class for reading data from a CSV file
*
*	Copyright (c) 2007, H. Poort
*	All rights reserved.
*
*	Redistribution and use in source and binary forms, with or without
*	modification, are permitted provided that the following conditions are met:
*	  * 	Redistributions of source code must retain the above copyright
*		notice, this list of conditions and the following disclaimer.
*	  *	Redistributions in binary form must reproduce the above copyright
*		notice, this list of conditions and the following disclaimer in the
*		documentation and/or other materials provided with the distribution.
*	  *	The name the copyright holder may not be used to endorse or promote
*		products derived from this software without specific prior written
*		permission.
*
*	THIS SOFTWARE IS PROVIDED BY H. Poort ``AS IS'' AND ANY
*	EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
*	WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
*	DISCLAIMED. IN NO EVENT SHALL H. Poort BE LIABLE FOR ANY
*	DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
*	(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
*	LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
*	ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
*	(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
*	SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*	EXAMPLE
*	=======
*	require 'class.b3rtCSVReader.php';
*
*	$csvReader = new b3rtCSVReader();
*	$csvReader->setFilename('./example.csv');
*	$csvReader->setDelimiter(';');
*
*	while ($csvRecord = $csvReader->fetchRecord())
*		print_r($csvRecord);
*
*	if ($csvReader->getErrors())
*	{
*		echo "\nErrors:\n";
*		print_r($csvReader->getErrors());
*	}
*
*	unset($csvReader);
*/

class b3rtCSVReader
{
	private $filename;
	private $delimiter;

	private $fileHandle;
	private $fileBuffer;
	private $fileBufferSize;

	private $errorList;

	/*function b3rtCSVReader()
	{
	}*/

	public function __construct()
	{
		$this->filename = '';
		$this->delimiter = ',';

		$this->fileHandle = NULL;
		$this->fileBuffer = '';
		$this->fileBufferSize = 4096;

		$this->errorList = array();
	}

	public function __destruct()
	{
		$this->closeFile();
	}

	public function setFilename($filename)
	{
		$this->filename = $filename;

		return $this->openFile();
	}

	public function setDelimiter($delimiter)
	{
		if (strlen($delimiter) != 1)
		{
			$this->setError('Invalid delimiter');
			return FALSE;
		}

		$this->delimiter = $delimiter;
		return TRUE;
	}

	public function getErrors()
	{
		return $this->errorList;
	}

	public function fetchRecord()
	{
		if ($this->errorList)
			return NULL;

		$csvRecord = array();
		$fieldIndex = 0;
		$csvRecord[$fieldIndex] = '';
		$inQuotedField = FALSE;

		// Keep reading data from the CSV file until the end of the record is reached
		$keepReading = TRUE;
		while ($keepReading)
		{
			// Check if there's data available in fileBuffer from a previous call
			if ($this->fileBuffer != '')
			{
				$csvData = $this->fileBuffer;
				$this->fileBuffer = '';
			}
			else // Else get data from file
				$csvData = $this->readFromFile();

			// Handle no more data available
			if ($csvData == '')
			{
				// Only return record when it actually contains something
				if (($fieldIndex == 0) && ($csvRecord[0] == ''))
					return NULL;
				else
					return $csvRecord;
			}

			// Parse csvData, char by char
			for ($charIdx = 0, $charCnt = strlen($csvData); $charIdx < $charCnt; $charIdx++)
			{
				$currentChar = $csvData[$charIdx];

				if ($currentChar == '"')
				{
					// Set 'inQuotedField' if:
					// - not already in a quoted field
					// - current field is still empty which permits unescaped " characters
					if (!$inQuotedField)
					{
						if ($csvRecord[$fieldIndex] == '')
						{
							$inQuotedField = TRUE;
							continue; // Contine with next iteration
						}
					}
					else // Handle " when currently in a quoted field
					{
						if ((($charIdx + 1) < $charCnt) && // Check if there is a next char available
							($csvData[$charIdx + 1] == '"')) // Check if the next char is a "
						{
							// Skip next char in next iteration by increasing index, " will be added later on
							$charIdx++;
						}
						else
						{ // Unset 'inQuotedField'
							$inQuotedField = FALSE;
							continue; // Contine with next char
						}
					}
				} // End if ($currentChar == '"')

				if (!$inQuotedField)
				{
					// Check for delimiter
					if ($currentChar == $this->delimiter)
					{
						// Start new field
						$fieldIndex++;
						$csvRecord[$fieldIndex] = '';
						continue; // Contine with next char
					}
					
					// Check for EOL, which signifies the end of the record
					$reachedEOL = FALSE;
					if ($currentChar == "\n")
					{
						$charIdx++; // Eat \n, necessary for fileBuffer
						$reachedEOL = TRUE;
					}
					else if ($currentChar == "\r")
					{
						// Check if the next char is a \n
						if ((($charIdx + 1) < $charCnt) && // Check if there is a next char available
							($csvData[$charIdx + 1] == "\n")) // Check if the next char is a \n
						{
							$charIdx += 2; // Eat \r\n, necessary for fileBuffer
							$reachedEOL = TRUE;
						}
					}

					if ($reachedEOL)
					{
						// Save any unparsed data to fileBuffer
						if ($charIdx < $charCnt)
							$this->fileBuffer = substr($csvData, $charIdx);

						// Stop looping (while and for)
						$keepReading = FALSE;
						break;
					}
				} // End if (!$inQuotedField)

				// Add character to field
				$csvRecord[$fieldIndex] .= $currentChar;
			} // End for
		} // End while

		return $csvRecord;
	}

	private function openFile()
	{
		$this->closeFile();

		if ($this->filename == '')
			$this->setError('Invalid filename');

		if ($this->errorList)
			return FALSE;

		if (!is_readable($this->filename))
		{
			$this->setError('File doesn\'t exist or isn\'t readable');
			return FALSE;
		}

		$this->fileHandle = @fopen($this->filename, 'rb');
		if (!$this->fileHandle)
		{
			$this->setError('Could not open file');
			$this->fileHandle = NULL;
			return FALSE;
		}

		return TRUE;
	}

	private function readFromFile()
	{
		if ($this->fileHandle === NULL)
		{
			$this->setError('No file specified');
			return '';
		}

		// Handle EOF
		if (feof($this->fileHandle))
			return '';

		$readData = @fread($this->fileHandle, $this->fileBufferSize);
		if (($readData === FALSE) || ($readData == ''))
			return '';

		// Continue reading chars while last char is " or \r
		$lastChar = $readData[strlen($readData) - 1];
		while (($lastChar == '"') || ($lastChar == "\r"))
		{
			$lastChar = @fread($this->fileHandle, 1);
			if ($lastChar === FALSE)
				return $readData;

			$readData .= $lastChar;
		}

		return $readData;
	}

	private function closeFile()
	{
		if (is_resource($this->fileHandle))
		{
			if(!@fclose($this->fileHandle))
				$this->setError('Could not close file');
		}

		$this->fileHandle = NULL;
	}

	private function setError($error)
	{
		$this->errorList[] = $error;
	}
}
?>
Return current item: b3rtCSVReader