Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

237 lines (204 sloc) 5.553 kb
<?php
/**
* Class to handle parsing of CSV files, where the column headers are in the first row.
* The idea is that you pass it another object to handle the actual procesing of the data in the CSV file.
*
* Usage:
* <code>
* $parser = new CSVParser('myfile.csv');
* $parser->mapColumns(
* 'first name' => 'FirstName'
* 'lastname' => 'Surname',
* 'last name' => 'Surname'
* ));
* foreach($parser as $row) {
* // $row is a map of column name => column value
* $obj = new MyDataObject();
* $obj->update($row);
* $obj->write();
* }
* </code>
*
* @package framework
* @subpackage bulkloading
*/
class CSVParser extends Object implements Iterator {
protected $filename;
protected $fileHandle;
/**
* Map of source columns to output columns
* Once they get into this variable, all of the source columns are in lowercase
*/
protected $columnMap = array();
/**
* The header row used to map data in the CSV file
* To begin with, this is null. Once it has been set, data will get returned from the CSV file
*/
protected $headerRow = null;
/**
* A custom header row provided by the caller
*/
protected $providedHeaderRow = null;
/**
* The data of the current row
*/
protected $currentRow = null;
/**
* The current row number
* 1 is the first data row in the CSV file; the header row, if it exists, is ignored
*/
protected $rowNum = 0;
/**
* The character for separating columns
*/
protected $delimiter = ",";
/**
* The character for quoting colums
*/
protected $enclosure = '"';
/**
* Open a CSV file for parsing.
* You can use the object returned in a foreach loop to extract the data
* @param $filename The name of the file. If relative, it will be relative to the site's base dir
* @param $delimiter The character for seperating columns
* @param $enclosure The character for quoting or enclosing columns
*/
public function __construct($filename, $delimiter = ",", $enclosure = '"') {
$filename = Director::getAbsFile($filename);
$this->filename = $filename;
$this->delimiter = $delimiter;
$this->enclosure = $enclosure;
parent::__construct();
}
/**
* Re-map columns in the CSV file.
* This can be useful for identifying synonyms in the file
* For example:
* <code>
* $csv->mapColumns(array(
* 'firstname' => 'FirstName',
* 'last name' => 'Surname',
* ));
* </code>
*/
public function mapColumns($columnMap) {
if($columnMap) {
$lowerColumnMap = array();
foreach($columnMap as $k => $v) {
$lowerColumnMap[strtolower($k)] = $v;
}
$this->columnMap = array_merge($this->columnMap, $lowerColumnMap);
}
}
/**
* If your CSV file doesn't have a header row, then you can call this function to provide one.
* If you call this function, then the first row of the CSV will be included in the data returned.
*/
public function provideHeaderRow($headerRow) {
$this->providedHeaderRow = $headerRow;
}
/**
* Open the CSV file for reading
*/
protected function openFile() {
ini_set('auto_detect_line_endings',1);
$this->fileHandle = fopen($this->filename,'r');
if($this->providedHeaderRow) {
$this->headerRow = $this->remapHeader($this->providedHeaderRow);
}
}
/**
* Close the CSV file and re-set all of the internal variables
*/
protected function closeFile() {
if($this->fileHandle) fclose($this->fileHandle);
$this->fileHandle = null;
$this->rowNum = 0;
$this->currentRow = null;
$this->headerRow = null;
}
/**
* Get a header row from the CSV file
*/
protected function fetchCSVHeader() {
$srcRow = fgetcsv($this->fileHandle, 0, $this->delimiter, $this->enclosure);
$this->headerRow = $this->remapHeader($srcRow);
}
/**
* Map the contents of a header array using $this->mappedColumns
*/
protected function remapHeader($header) {
$mappedHeader = array();
foreach($header as $item) {
if(isset($this->columnMap[strtolower($item)])) $item = $this->columnMap[strtolower($item)];
$mappedHeader[] = $item;
}
return $mappedHeader;
}
/**
* Get a row from the CSV file and update $this->currentRow;
*/
protected function fetchCSVRow() {
if(!$this->fileHandle) $this->openFile();
if(!$this->headerRow) $this->fetchCSVHeader();
$this->rowNum++;
$srcRow = fgetcsv($this->fileHandle, 0, $this->delimiter, $this->enclosure);
if($srcRow) {
$row = array();
foreach($srcRow as $i => $value) {
// Allow escaping of quotes and commas in the data
$value = str_replace(
array('\\'.$this->enclosure,'\\'.$this->delimiter),
array($this->enclosure,$this->delimiter),$value);
if(array_key_exists($i, $this->headerRow)) {
if($this->headerRow[$i]) $row[$this->headerRow[$i]] = $value;
} else {
user_error("No heading for column $i on row $this->rowNum", E_USER_WARNING);
}
}
$this->currentRow = $row;
} else {
$this->closeFile();
}
return $this->currentRow;
}
/**
* @ignore
*/
public function __destruct() {
$this->closeFile();
}
//// ITERATOR FUNCTIONS
/**
* @ignore
*/
public function rewind() {
$this->closeFile();
$this->fetchCSVRow();
}
/**
* @ignore
*/
public function current() {
return $this->currentRow;
}
/**
* @ignore
*/
public function key() {
return $this->rowNum;
}
/**
* @ignore
*/
public function next() {
$this->fetchCSVRow();
return $this->currentRow;
}
/**
* @ignore
*/
public function valid() {
return $this->currentRow ? true : false;
}
}
Jump to Line
Something went wrong with that request. Please try again.