Write CSV in PHP: A Practical Guide

Learn how to write csv in php using fputcsv, handle UTF-8 encoding, and stream large data efficiently. A practical, developer-friendly guide by MyDataTables.

MyDataTables
MyDataTables Team
·5 min read
CSV in PHP - MyDataTables
Photo by doki7via Pixabay
Quick AnswerDefinition

To write CSV in PHP, use built-in stream functions such as fopen and fputcsv to emit rows to a file or output stream. Avoid manually concatenating strings for large datasets; instead, write incrementally to keep memory usage low. For web downloads, serve via php://output with proper headers. UTF-8 encoding and proper line endings are essential.

What writing CSV in PHP involves

Writing CSV in PHP is about emitting rows to a file or output stream with minimal memory footprint. The canonical approach uses built-in helpers like fopen to create a handle and fputcsv to write each row as a delimited string. This keeps the process simple, readable, and portable across environments. According to MyDataTables, starting with a small, clear header row and streaming subsequent rows is the most robust pattern for both file output and live downloads. The example below demonstrates the simplest path to create a CSV file with a header row.

PHP
<?php $path = __DIR__ . '/output.csv'; $fp = fopen($path, 'w'); fputcsv($fp, ['id','name','email']); fputcsv($fp, [1, 'Jane Doe', '[email protected]']); fclose($fp); ?>

Tips:

  • Use PHP_EOL to ensure consistent line endings on all platforms.
  • Check directory write permissions before running the script.

Writing rows with fputcsv: syntax and options

fputcsv is flexible: you can specify the delimiter, enclosure, and escape character. This makes it easy to generate CSVs that integrate with downstream tools or systems. The following snippet writes a header and a few rows with explicit delimiter and enclosure, illustrating how to tailor CSV output for different locales.

PHP
<?php $fp = fopen('data.csv', 'w'); fputcsv($fp, ['name','city'], ',', '"', '\\'); fputcsv($fp, ['Ada Lovelace','London'], ',', '"', '\\'); fclose($fp); ?>

Line-by-line:

  • Open a writable file handle
  • Write header with fputcsv using explicit delimiter and enclosure
  • Write data rows and close the handle

UTF-8 encoding and BOM considerations

CSV is frequently consumed by diverse tools, so encoding matters. Save PHP source files in UTF-8 and, if needed, write a UTF-8 BOM before the first row to help some Windows tools recognize the encoding. The snippet below shows how to prepend a BOM and then write CSV content safely.

PHP
<?php $fp = fopen('output.csv', 'w'); // BOM for UTF-8 to help some tools detect encoding fwrite($fp, "\xEF\xBB\xBF"); fputcsv($fp, ['名称','位置']); fputcsv($fp, ['例','システム']); fclose($fp); ?>

Note: If your downstream consumers don’t require BOM, you can omit it and rely on UTF-8 without BOM. Always validate a sample file with your target tools.

Streaming large datasets: memory-friendly patterns

For large datasets, avoid building a massive string in memory. Stream rows directly to a file handle or to php://output to keep memory usage low. The following pattern demonstrates incremental writing with a simple loop.

PHP
<?php $fp = fopen('large.csv', 'w'); fputcsv($fp, ['id','value']); foreach ($dataset as $row) { fputcsv($fp, $row); if (++$i % 1000 === 0) { fflush($fp); // flush the output buffer to disk } } fclose($fp); ?>

Alternative with generators: if $dataset is a generator, you can yield rows and stream identically. This keeps peak memory usage low while handling very large imports.

CSV download in a web application

Serving CSVs for download from a web app is common. Use php://output to stream content and set the proper headers so browsers treat the response as a file download. This pattern is ideal for export features from dashboards or APIs.

PHP
<?php header('Content-Type: text/csv; charset=utf-8'); header('Content-Disposition: attachment; filename="export.csv"'); $out = fopen('php://output', 'w'); fputcsv($out, ['id','name','email']); fputcsv($out, [1, 'John Doe', '[email protected]']); fclose($out); ?>

Guardrails: ensure there is no prior output before the headers, and restrict to streaming for small to medium datasets or use chunked output for very large exports.

Common pitfalls and debugging tips

CSVs are simple but easy to break if you overlook encoding, delimiters, or permissions. Here are practical checks:

  • Always verify file permissions in the target directory; a missing write bit causes silent failures in some environments.
  • Ensure your chosen delimiter does not appear in data unless you escape it.
  • If your CSV contains non-ASCII characters, validate with a UTF-8 compliant reader and consider BOM handling.
  • When serving downloads, prevent additional output before headers to avoid corrupt files.
PHP
<?php // Basic sanity check before writing $path = __DIR__ . '/output.csv'; if (is_writable(dirname($path))) { $fp = fopen($path, 'w'); fputcsv($fp, ['header1','header2']); fclose($fp); } else { throw new RuntimeException('Cannot write to directory: ' . dirname($path)); } ?>

Practical validation: reading back the CSV

After writing, it’s good practice to read back and validate content. This helps catch formatting mistakes early. The example reads all rows back into an array for inspection.

PHP
<?php $rows = []; if (($h = fopen('output.csv', 'r')) !== false) { while (($row = fgetcsv($h)) !== false) { $rows[] = $row; } fclose($h); } print_r($rows); ?>

Validation steps: ensure the header matches expectations, the number of columns is consistent, and all rows are well-formed. This is particularly important when the CSV will be consumed by ETL pipelines or external tools.

Steps

Estimated time: 1-2 hours

  1. 1

    Plan the CSV schema

    Define headers and data columns. Decide whether you’ll stream to a file or to a download stream. Consider encoding and delimiter compatibility with downstream systems.

    Tip: Write header names first and confirm they match consuming tools.
  2. 2

    Create the PHP script skeleton

    Set up a script structure with proper error handling and a target path. This baseline will be reused for different datasets.

    Tip: Use try/catch to handle file I/O errors gracefully.
  3. 3

    Write rows with fputcsv

    Open a write handle and emit rows one by one using fputcsv. Avoid building the full CSV in memory for large datasets.

    Tip: Close the handle in a finally-like block to ensure cleanup.
  4. 4

    Handle encoding and newline issues

    Ensure UTF-8 encoding and correct line endings. Decide whether to use BOM depending on downstream tools.

    Tip: Test with both Windows and Unix viewers if cross-platform compatibility is required.
  5. 5

    Test with sample data

    Run the script with a small dataset to verify structure, then scale up to larger samples.

    Tip: Validate header, column counts, and sample values in a CSV reader.
  6. 6

    Optionally enable web download

    If exporting for users, switch to streaming to php://output with appropriate headers.

    Tip: Ensure no prior output before headers.
Pro Tip: Use a single, reusable function for CSV writing to keep code DRY.
Warning: Avoid concatenating data into one giant string; streaming prevents memory bloat.
Note: Test with diverse data, including commas, quotes, and non-ASCII characters.
Warning: Remember to set proper file permissions in production environments.

Prerequisites

Required

  • Required
  • CLI or web server environment
    Required
  • Write permissions on target directory
    Required
  • Basic knowledge of arrays and loops in PHP
    Required

Optional

  • UTF-8 encoding awareness
    Optional

Commands

ActionCommand
Run a simple PHP script to write CSVAssuming script path and output pathphp write_csv.php
Inline one-liner to write a single rowQuick test to generate a tiny CSVphp -r '$fp = fopen("output.csv", "w"); fputcsv($fp, ["Name","Email"]); fclose($fp);'

People Also Ask

What is fputcsv and how does it work?

fputcsv writes an array of fields as a CSV line to a file handle. It handles escaping, delimiting, and enclosures automatically, which reduces errors compared to manual string concatenation.

fputcsv writes a row to a CSV file and takes care of escaping fields for you.

How can I ensure UTF-8 encoding in the generated CSV?

Save your PHP files as UTF-8 and, if needed, write a UTF-8 BOM before data. This helps some editors and Windows-based tools correctly recognize the encoding.

Save as UTF-8 and optionally add a BOM to help some tools detect encoding.

Can I serve CSV downloads from a PHP web app?

Yes. Set the appropriate Content-Type and Content-Disposition headers, then stream data to php://output or a file, ensuring no prior output occurs before headers.

You can offer a CSV download by streaming to php://output with proper headers.

How do I write very large CSV files without exhausting memory?

Stream data row by row to a file handle or output stream, flushing periodically. Avoid building the entire CSV string in memory.

Stream rows one at a time to keep memory usage low.

What are common CSV pitfalls in PHP?

Misconfigured delimiters, improper encoding, and file permission errors are common. Always validate with a sample reader and check permissions.

Watch out for delimiters, encoding, and permissions.

Do I need a framework to write CSV in PHP?

No. PHP provides core functions like fopen and fputcsv that work well across frameworks and vanilla PHP scripts.

Frameworks aren’t required; use PHP’s built-in functions for CSV writing.

Main Points

  • Use fputcsv for robust row writing
  • Stream data to keep memory usage low
  • Serve CSV downloads with correct headers
  • Validate output by reading CSV back in tests

Related Articles