Klasse zum lesen und verarbeiten einer CSV-Datei in PHP

Das Lesen einer csv-Datei in PHP ist eigentlich sehr einfach. Mit folgender Klasse wird der Zugriff auf CSV-Dateien noch komfortabler.

Die Klasse bietet folgende Funktionen:

  • Einlesen einer CSV-Datei und Zugriff auf die einzelnen Werte über den Spaltennamen
    Hier kann angegeben werden, wieviel Kopfzeilen die Datei hat und ab welcher Zeile die Daten beginnen. Um auf die einzelnen Werte mittels Name zugreifen zu können, muß die erste Zeile in der CSV-Datei die Spaltennamen beinhalten. Ansonsten kann nur mit den Indexen zugegriffen werden.
  • Erstellen eines CSV-Downloads

Beispiel: Einlesen einer CSV-Datei

$csv = new Csv();

$csv->read('test.csv');
while (!$csv->eof()) {
   echo $csv->getVal('Spaltenname1') . ' ' . $csv->getVal('Spaltenname2');
   // oder mit Spalten-Index
   echo $csv->getVal(1) . ' ' . $csv->getVal(2);
   // oder direkt über Spaltenname
   echo $csv->Spaltenname1 . ' ' . $csv->Spaltenname2;

   // nächste Zeile
   $csv->next();
}

Beispiel: Erstellen eines CSV-Downloads

$csv = new Csv();

$csv->setColumnNames(array('Spalte1', 'Spalte2'));
// hinzufügen einer Spalte
$csv->addColumnName('Spalte3');

// mit Beispiel-Inhalt befüllen
for ($row = 1; $row < 5; $row++) {
   for ($col = 1; $row <= 3; $col++) {
      $csv->addRowArray(array('Wert1', 'Wert2', 'Wert3'));
   }
}

$csv->download('test.csv');

CSV-Klasse:

<?php

class Csv {

    /**
     * Column names found or created
     * @var array
     */
    private $columnNames = array();
    
    /**
     * Number of rows found or created
     * @var integer
     */
    private $rowNum = 0;
    
    /**
     * Actual row number
     * @var integer
     */
    private $aktRow = 1;
    
    /**
     * Whether the values should be automatically utf-8 encoded when read
     * @var boolean
     */
    private $doUtfEncode = true;
    
    /**
     * Whether the values should be automatically utf-8 decoded when set
     * @var boolean
     */
    private $doUtfDecode = true;
    
    /**
     * Internal data representation
     * @var array
     */
    private $data;
    
    /**
     * Whether quotes in values added should change the single " to double ""
     * @var boolean
     */
    private $autoQuote = true;

    public function __construct() {        
    }

    /**
     * Analyse csv file and save it in array  
     * 
     * @param string $filename filename/path to csv file
     * @param integer $headersRow number of header rows
     * @param integer $dataStartRow line-number where the data to read starts 
     */
    public function read($filename, $headersRow = 1, $dataStartRow = 2) {
        $fp = fopen($filename, 'r');

        $row = 1;
        $dataRow = 0;

        // Initialize
        $this->columnNames = array();
        $this->rowNum = 0;
        $this->data = array();
        $this->aktRow = 1;

        while (($vals = fgetcsv($fp, 0, ';')) !== FALSE) {
            $col = 1;

            if ($row >= $dataStartRow) {
                $this->rowNum ++;
                $dataRow ++;
            }

            foreach ($vals as $val) {

                if ($row == $headersRow) {
                    $this->columnNames[$col] = strtolower(trim($val));
                }

                if ($row >= $dataStartRow) {
                    $this->data[$dataRow][$col] = $val;
                }

                $col++;
            }
            $row ++;
        }
        fclose($fp);
    }

    /**
     * Get a val of the Array, either with column name or column index and the row (optinoal)
     * 
     * @param int $columnNameOrCol Can be the column name or a integer which indicates its a column index
     * @param int $row Optional: the row index (uses the actual row if not given)
     * @return string
     */
    public function getVal($columnNameOrCol, $row = -1) {
        if ($row == -1) {
            $row = $this->aktRow;
        }

        if (is_int($columnNameOrCol)) {
            $col = $columnNameOrCol;
        } else {
            if (($col = array_search(strtolower(trim($columnNameOrCol)), $this->columnNames)) === FALSE) {
                return FALSE;
            }
        }

        if (isset($this->data[$row][$col])) {
            $val = $this->data[$row][$col];
            if ($this->doUtfEncode) {
                $val = utf8_encode($val);
            }
            return $val;
        } else {
            return FALSE;
        }
    }

    /**
     * Short for getVal()
     * 
     * @param int $columnNameOrCol
     * @param int $row
     * @return string
     */
    public function val($columnNameOrCol, $row = -1) {
        return $this->getVal($columnNameOrCol, $row);
    }

    /**
     * Magic method to get a value by its column name
     * 
     * @param string $name column name
     * @return string
     */
    public function __get($name) {
        return $this->getVal($name);
    }

    /**
     * Checks if end of file is reached
     * 
     * @return boolean
     */
    public function eof() {
        if ($this->aktRow > $this->rowNum) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * Go to next row
     */
    public function next() {
        $this->aktRow ++;
    }

    public function nextRow() {
        $this->next();
    }

    /**
     * Get number of rows
     * 
     * @return integer
     */
    public function getRowNum() {
        return $this->rowNum;
    }

    /**
     * Get the column names
     * 
     * @return array
     */
    public function getColumnNames() {
        return $this->columnNames;
    }

    /** 
     * Add data array to the actual row
     * 
     * @param array $rowArray
     */
    public function addRowArray($rowArray) {
        $this->data[$this->aktRow] = [];
        foreach ($rowArray as $val) {
            $this->addVal();
        }
        $this->aktRow++;
    }

    /**
     * Add a value to the actual row
     * 
     * @param type $val
     */
    public function addVal($val) {
        if ($this->doUtfDecode) {
            $val = utf8_decode($val);
        }
        if ($this->autoQuote) {
            $val = str_replace('"', '""', $val);
        }

        $this->data[$this->aktRow][] = '"' . $val . '"';
    }

    /**
     * Set column names from array
     * 
     * @param array $columnNames
     */
    public function setColumnNames($columnNames) {
        $this->columnNames = $columnNames;
    }

    /**
     * Add a column name
     * 
     * @param string $columnName
     */
    public function addColumnName($columnName) {
        $this->columnNames[] = $columnName;
    }

    /**
     * Start download of the data as csv file
     * 
     * @param type $filename
     * @param type $addDateToFilename
     */
    public function download($filename, $addDateToFilename = true) {
        $f = fopen('php://memory', 'w');
        if (sizeof($this->columnNames) > 0) {
            fwrite($f, implode(';', $this->columnNames));
            fwrite($f, "\n");
        }
        foreach ($this->data as $line) {
            // generate csv lines from the inner arrays
            fwrite($f, implode(';', $line));
            fwrite($f, "\n");
        }
        fseek($f, 0);
        header('Content-Type: application/csv');

        $filename = str_ireplace('.csv', '', $filename);
        if ($addDateToFilename) {
            $date = new \DateTime();
            $filename .= '_' . $date->format('Y_m_d__H_i');
        }
        $filename .= '.csv';
        header('Content-Disposition: attachment; filename="' . $filename . '.csv";');
        fpassthru($f);
        die();
    }

    /**
     * Dump the data for debugging
     */
    public function dump() {
        echo '<pre>';
        print_r($this->data);
        echo '</pre>';
    }

    // -------- Getters and setters ---------
    
    public function getAutoQuote() {
        return $this->autoQuote;
    }

    public function setAutoQuote($autoQuote) {
        $this->autoQuote = $autoQuote;
    }

    public function getDoUtfDecode() {
        return $this->doUtfDecode;
    }

    public function setDoUtfDecode($doUtfDecode) {
        $this->doUtfDecode = $doUtfDecode;
    }

    public function getUtfEncode() {
        return $this->doUtfEncode;
    }

    public function setUtfEncode($doUtfEncode) {
        $this->doUtfEncode = $doUtfEncode;
    }
}