PHP CSV Handling (fgetcsv / str_getcsv)
Also Known As
fgetcsv
str_getcsv
fputcsv
PHP CSV parser
TL;DR
PHP provides fgetcsv() for reading CSV line-by-line from a file handle and str_getcsv() for parsing a CSV string — both handle quoted fields, embedded commas, and escaped characters correctly where explode() does not.
Explanation
CSV parsing looks simple — split on commas — until you encounter quoted fields that contain commas, newlines inside quotes, or escaped quote characters. PHP's fgetcsv() reads one CSV row from an open file handle, correctly parsing all RFC 4180 quoting rules. str_getcsv() does the same for an in-memory string. For writing, fputcsv() formats an array as a CSV row with correct quoting. Key parameters: the delimiter (default comma), enclosure character (default double-quote), and escape character. In PHP 8.4, the escape parameter default changed — set it explicitly to avoid version-dependent behaviour. For large files, fgetcsv() in a loop with a generator avoids loading the entire file into memory.
Common Misconception
✗ str_getcsv() can parse an entire multi-line CSV file. It parses a single CSV record (one logical row). Use fgetcsv() in a loop with an open file handle for multi-row files, or file() + array_map('str_getcsv') for small files already in memory.
Why It Matters
Naïve explode(',', $line) CSV parsing breaks on the first quoted field containing a comma — a common case in address fields, names, and any data with commas in it. Using the built-in functions handles all edge cases correctly and is faster than any pure-PHP alternative.
Common Mistakes
- Not specifying escape: '' in PHP 8.4+ — the default escape character changed; explicitly set it to avoid version-dependent parsing differences.
- Using file() + array_map('str_getcsv') on files with quoted newlines — file() splits on newlines, breaking multi-line quoted fields; only fgetcsv() handles embedded newlines correctly.
- Forgetting to handle BOM (Byte Order Mark) on Windows-generated CSV files — strip the BOM from the first field: ltrim($firstField, "\xEF\xBB\xBF").
- Not validating the number of fields per row — fgetcsv() returns arrays of varying length if rows have different field counts; always check count($row) === $expectedColumns.
Code Examples
✗ Vulnerable
<?php
// ❌ Naïve CSV parsing — breaks on quoted fields
$lines = file('data.csv');
foreach ($lines as $line) {
$fields = explode(',', trim($line)); // Breaks on: "Smith, John",30,"New York, NY"
processRow($fields);
}
// Also broken — loading entire large file into memory
$content = file_get_contents('huge_export.csv'); // 500MB in memory
$lines = explode("\n", $content);
✓ Fixed
<?php
// ✅ fgetcsv() — correct parsing, memory-efficient for large files
function readCsv(string $path): Generator
{
$handle = fopen($path, 'r');
if ($handle === false) throw new RuntimeException("Cannot open $path");
$header = fgetcsv($handle); // First row is headers
while (($row = fgetcsv($handle, escape: '')) !== false) {
yield array_combine($header, $row);
}
fclose($handle);
}
foreach (readCsv('export.csv') as $row) {
processRow($row); // One row in memory at a time
}
// Writing CSV
$handle = fopen('output.csv', 'w');
fputcsv($handle, ['name', 'email', 'city']); // Header
fputcsv($handle, ['Smith, John', 'john@example.com', 'New York, NY']);
fclose($handle);
References
Tags
🤝 Adopt this term
£79/year · your link shown here
Added
23 Mar 2026
Views
19
🤖 AI Guestbook educational data only
|
|
Last 30 days
Agents 0
No pings yet today
No pings yesterday
Amazonbot 6
Google 4
Perplexity 2
ChatGPT 1
Ahrefs 1
Also referenced
How they use it
crawler 13
crawler_json 1
Related categories
⚡
DEV INTEL
Tools & Severity
⚙ Fix effort: Low
⚡ Quick Fix
Replace explode(',', $line) with str_getcsv($line) for single lines, and replace file-reading loops with fgetcsv($handle) for large files.
📦 Applies To
PHP 5.3+
web
cli