ilbee/csv-response

Symfony component allow you to respond CSV contents directly in your controller

Maintainers

Package info

github.com/ilbee/csv-response

pkg:composer/ilbee/csv-response

Statistics

Installs: 162 275

Dependents: 1

Suggesters: 0

Stars: 2

Open Issues: 0


README

CI PHP Symfony License

A Symfony component that lets you return CSV file downloads directly from your controllers.

Table of Contents

Features

  • Two response classes for different use cases:
Class Extends Best for
CSVResponse Response Small to medium datasets (buffered in memory)
StreamedCSVResponse StreamedResponse Large datasets (streamed row by row, constant memory)
  • Automatic header row generation from array keys (can be disabled)
  • Configurable separator (semicolon by default, comma, etc.)
  • Custom file name support
  • DateTime objects are automatically formatted (configurable format)
  • Optional UTF-8 BOM for Excel compatibility
  • CSV injection protection (formula sanitization)
  • Optional row limit (maxRows) to prevent unbounded memory usage
  • Strict type handling: clear errors for unsupported object types
  • Accepts arrays, iterables, generators, or callables as data source
  • No configuration required — just install and use

Installation

composer require ilbee/csv-response

Usage

Basic example

use Ilbee\CSVResponse\CSVResponse;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Attribute\Route;

class ExportController extends AbstractController
{
    #[Route('/export', name: 'export_csv')]
    public function export(): CSVResponse
    {
        $data = [
            ['firstName' => 'Marcel', 'lastName' => 'TOTO'],
            ['firstName' => 'Maurice', 'lastName' => 'TATA'],
        ];

        return new CSVResponse($data);
    }
}

This triggers a download of CSVExport.csv with the content:

firstName;lastName
Marcel;TOTO
Maurice;TATA

Streaming large exports

use Ilbee\CSVResponse\StreamedCSVResponse;

class ExportController extends AbstractController
{
    #[Route('/export/large', name: 'export_large_csv')]
    public function exportLarge(UserRepository $repository): StreamedCSVResponse
    {
        // Callable is invoked at send-time — no data buffered in memory
        return new StreamedCSVResponse(function () use ($repository) {
            foreach ($repository->findAllIterator() as $user) {
                yield [
                    'id' => $user->getId(),
                    'email' => $user->getEmail(),
                    'name' => $user->getName(),
                ];
            }
        });
    }
}

Custom file name and separator

use Ilbee\CSVResponse\CSVResponseInterface;

return new CSVResponse($data, 'users.csv', CSVResponseInterface::COMMA);

UTF-8 BOM (for Excel)

return new CSVResponse($data, 'users.csv', CSVResponseInterface::SEMICOLON, true);

Custom date format

return new CSVResponse($data, 'users.csv', CSVResponseInterface::SEMICOLON, false, 'd/m/Y');

Limit number of rows

// Throws OverflowException if data exceeds 10 000 rows
return new CSVResponse($data, 'users.csv', CSVResponseInterface::SEMICOLON, false, 'Y-m-d H:i:s', true, true, 10000);

Without header row

return new CSVResponse(
    $data,
    'users.csv',
    CSVResponseInterface::SEMICOLON,
    false,
    'Y-m-d H:i:s',
    false
);

Which class should I use?

Scenario Class
Small datasets (< 1000 rows) CSVResponse
Need to access content after creation (getContent()) CSVResponse
Large datasets or unknown size StreamedCSVResponse
Database cursor / generator source StreamedCSVResponse
Memory-constrained environment StreamedCSVResponse

Both classes share the same constructor signature and support the same features. The only difference is how data is written to the response.

Contributing

composer install
composer test          # PHPUnit
composer phpstan       # Static analysis
composer cs-check      # Code style check
composer cs-fix        # Auto-fix code style

Credits

Special thanks to Paul Mitchum and Dan Feder for their contributions.

License

MIT