データベースからセレクトしたデータを、PHPでCSV形式に変換し、CSVファイルとしてダウンロードさせる方法。
ロジックの中核部分は、php://memory(読み書き可能なストリームで、一時データをファイルのように保存できるラッパー)を使用してファイルを生成していることと、 fputcsv を使用して行をCSV形式にフォーマットし、ファイルポインタに書き込むことの二点である。

また、Windowsユーザにダウンロードさせることを前提として、改行コードや文字コードをWindows 7で都合がいいように\r\n,SJISとしている。

// ヘッダー定義
$header = array('col1', 'col2', 'col3');
// データベースからダウンロード用データ取得
$data = $db->load();
// ヘッダーとデータを合体
array_unshift($data, $header);
// ファイル名
$filename = 'ファイル名';
$fileContent = createCsvDownload($data, "\r\n");
$filename = mb_convert_encoding($filename,"SJIS-win","UTF-8");
header('Content-Disposition: attachment; filename=' . $filename);
header('Content-Type: application/csv');
print $fileContent;

/**
 * CSVファイルダウンロード用データ生成関数
 * @param array $content
 * @param $newline default: LF
 * 
 * @return 文字列
 */
function createCsvDownload(array $content, $newline = "\n") {
    $out = fopen('php://memory', 'w');
    
    if ($newline == "\r\n") {
        stream_filter_register("msLineEnding", "ms_line_ending_filter");
        stream_filter_append($out, "msLineEnding");
    }
    
    foreach ($content as $line) {
        fputcsv($out, array_map(function($arr) { return mb_convert_encoding($arr,"SJIS-win","UTF-8"); }, $line));
    }
    rewind($out);
    $fileContent = stream_get_contents($out);
    
    fclose($out);
    return $fileContent;
}

/**
 * 行末改行記号をCRLFに変換するユーザ定義フィルタクラス
 */
class ms_line_ending_filter extends \php_user_filter {
    function filter($in, $out, &$consumed, $closing) {
        while ($bucket = stream_bucket_make_writeable($in)) {
            $bucket->data = preg_replace("/\n$/", "", $bucket->data);
            $bucket->data = preg_replace("/\r$/", "", $bucket->data);
            $bucket->data = $bucket->data . "\r\n";
            $consumed += $bucket->datalen;
            stream_bucket_append($out, $bucket);
        }
        return PSFS_PASS_ON;
    }
}

SymfonyのResponseオブジェクトを使う場合

use Symfony\Component\HttpFoundation\Response;
/**
 * 
 * @param array $content
 * @param $filename
 * @param $newline default: LF
 * 
 * @return Response
 */
function download(array $content, $filename, $newline = "\n") {
    $filename = mb_convert_encoding($filename,"SJIS-win","UTF-8");
    $fileContent = createCsvDownload($content, $newline);
    return new Response($fileContent,200,array("Content-Type"=>"application/csv","Content-Disposition"=>"attachment; filename=$filename"));
}