Navigation

Php

How to Handle CSV File Reading and Writing

Master CSV file operations in PHP using built-in functions for data import/export, spreadsheet processing, and structured data handling.

Table Of Contents

Here's How

CSV handling is essential for data exchange between systems. PHP provides powerful built-in functions that handle parsing complexities automatically, including quoted fields, escaped characters, and different delimiters.

<?php

// Read CSV file efficiently
function readCSVFile(string $filename, bool $hasHeader = true): array {
    $data = [];
    $handle = fopen($filename, 'r');
    
    if (!$handle) {
        throw new RuntimeException("Cannot open CSV file: $filename");
    }
    
    // Read header row if present
    $header = $hasHeader ? fgetcsv($handle) : null;
    
    while (($row = fgetcsv($handle)) !== false) {
        if ($hasHeader && $header) {
            // Create associative array with header keys
            $data[] = array_combine($header, $row);
        } else {
            $data[] = $row;
        }
    }
    
    fclose($handle);
    return $data;
}

// Write CSV file with proper formatting
function writeCSVFile(string $filename, array $data, array $header = null): bool {
    $handle = fopen($filename, 'w');
    
    if (!$handle) {
        return false;
    }
    
    // Write header if provided
    if ($header) {
        fputcsv($handle, $header);
    }
    
    // Write data rows
    foreach ($data as $row) {
        if (is_array($row)) {
            fputcsv($handle, $row);
        } else {
            // Convert object to array if needed
            fputcsv($handle, (array)$row);
        }
    }
    
    fclose($handle);
    return true;
}

// Advanced CSV reader with configuration
class CSVHandler {
    private string $delimiter = ',';
    private string $enclosure = '"';
    private string $escape = '\\';
    
    public function setDelimiter(string $delimiter): self {
        $this->delimiter = $delimiter;
        return $this;
    }
    
    public function setEnclosure(string $enclosure): self {
        $this->enclosure = $enclosure;
        return $this;
    }
    
    public function read(string $filename, callable $rowProcessor = null): array {
        $data = [];
        $handle = fopen($filename, 'r');
        
        if (!$handle) {
            throw new RuntimeException("Cannot open file: $filename");
        }
        
        $lineNumber = 0;
        while (($row = fgetcsv($handle, 0, $this->delimiter, $this->enclosure, $this->escape)) !== false) {
            $lineNumber++;
            
            if ($rowProcessor) {
                try {
                    $processedRow = $rowProcessor($row, $lineNumber);
                    if ($processedRow !== null) {
                        $data[] = $processedRow;
                    }
                } catch (Exception $e) {
                    error_log("CSV processing error on line $lineNumber: " . $e->getMessage());
                }
            } else {
                $data[] = $row;
            }
        }
        
        fclose($handle);
        return $data;
    }
    
    public function write(string $filename, array $data): bool {
        $handle = fopen($filename, 'w');
        
        if (!$handle) {
            return false;
        }
        
        foreach ($data as $row) {
            fputcsv($handle, $row, $this->delimiter, $this->enclosure, $this->escape);
        }
        
        fclose($handle);
        return true;
    }
}

// Process large CSV files memory-efficiently
function processLargeCSV(string $filename, callable $processor): array {
    $stats = ['processed' => 0, 'errors' => 0, 'skipped' => 0];
    $handle = fopen($filename, 'r');
    
    if (!$handle) {
        throw new RuntimeException("Cannot open CSV file: $filename");
    }
    
    $header = fgetcsv($handle); // Read header
    $lineNumber = 1;
    
    while (($row = fgetcsv($handle)) !== false) {
        $lineNumber++;
        
        try {
            // Convert to associative array
            $data = array_combine($header, $row);
            
            $result = $processor($data, $lineNumber);
            
            if ($result === true) {
                $stats['processed']++;
            } elseif ($result === false) {
                $stats['skipped']++;
            }
        } catch (Exception $e) {
            $stats['errors']++;
            error_log("CSV error on line $lineNumber: " . $e->getMessage());
        }
    }
    
    fclose($handle);
    return $stats;
}

// Convert array to CSV string
function arrayToCSVString(array $data, array $header = null): string {
    $output = fopen('php://temp', 'r+');
    
    if ($header) {
        fputcsv($output, $header);
    }
    
    foreach ($data as $row) {
        fputcsv($output, $row);
    }
    
    rewind($output);
    $csvString = stream_get_contents($output);
    fclose($output);
    
    return $csvString;
}

// Parse CSV string to array
function parseCSVString(string $csvString): array {
    $lines = explode("\n", trim($csvString));
    $data = [];
    
    foreach ($lines as $line) {
        if (trim($line) !== '') {
            $data[] = str_getcsv($line);
        }
    }
    
    return $data;
}

// Filter and export CSV data
function filterAndExportCSV(string $inputFile, string $outputFile, callable $filter): int {
    $exported = 0;
    
    $inputHandle = fopen($inputFile, 'r');
    $outputHandle = fopen($outputFile, 'w');
    
    if (!$inputHandle || !$outputHandle) {
        throw new RuntimeException('Cannot open input or output file');
    }
    
    // Copy header
    $header = fgetcsv($inputHandle);
    fputcsv($outputHandle, $header);
    
    while (($row = fgetcsv($inputHandle)) !== false) {
        $data = array_combine($header, $row);
        
        if ($filter($data)) {
            fputcsv($outputHandle, $row);
            $exported++;
        }
    }
    
    fclose($inputHandle);
    fclose($outputHandle);
    
    return $exported;
}

// Usage examples
try {
    // Read CSV with headers
    $userData = readCSVFile('users.csv', true);
    foreach ($userData as $user) {
        echo "User: {$user['name']} - Email: {$user['email']}\n";
    }
    
    // Write new CSV file
    $newData = [
        ['John Doe', 'john@example.com', 25],
        ['Jane Smith', 'jane@example.com', 30],
        ['Bob Johnson', 'bob@example.com', 35]
    ];
    
    writeCSVFile('output.csv', $newData, ['Name', 'Email', 'Age']);
    
    // Use advanced CSV handler
    $csv = new CSVHandler();
    $csv->setDelimiter(';')->setEnclosure("'");
    
    $data = $csv->read('european.csv', function($row, $lineNumber) {
        // Process each row
        if (count($row) < 3) {
            throw new Exception('Invalid row format');
        }
        return array_map('trim', $row);
    });
    
    // Process large CSV efficiently
    $stats = processLargeCSV('large_data.csv', function($data, $lineNumber) {
        // Validate and process data
        if (empty($data['email']) || !filter_var($data['email'], FILTER_VALIDATE_EMAIL)) {
            return false; // Skip invalid rows
        }
        
        // Process valid data here
        return true;
    });
    
    echo "Processed: {$stats['processed']}, Errors: {$stats['errors']}, Skipped: {$stats['skipped']}\n";
    
    // Convert array to CSV string
    $csvString = arrayToCSVString($newData, ['Name', 'Email', 'Age']);
    echo "CSV String:\n$csvString\n";
    
    // Filter and export
    $exportedCount = filterAndExportCSV('all_users.csv', 'active_users.csv', function($data) {
        return $data['status'] === 'active';
    });
    
    echo "Exported $exportedCount active users\n";
    
} catch (Exception $e) {
    echo "CSV operation error: " . $e->getMessage() . "\n";
}

Explanation

PHP's CSV functions handle the complexities of the CSV format automatically, including proper escaping of quotes, handling multi-line fields, and dealing with different delimiters. The fgetcsv() and fputcsv() functions are optimized for performance and memory usage.

The key advantage is that these functions parse CSV according to RFC 4180 standards, handling edge cases like embedded commas, quotes, and newlines within fields. They also support different configurations for international data formats.

For large files, streaming approach prevents memory exhaustion, while callback-based processing allows for real-time data validation and transformation. Essential for data import/export, reporting systems, and any application dealing with spreadsheet data.

Share this article

Add Comment

No comments yet. Be the first to comment!

More from Php