PHPで配列をCSV出力する


きっかけ

業務でDBのデータをcsv出力したいという要件から実装
これからも必要になってくると思ったのでまとめました。

今回の内容

csvとして出力したデータをボタンを押すとダウンロードする

こんな感じ

phpでcsv出力する際、fputcsv関数SplFileObjectクラスを使用する方法があるみたいですが、今回はfputcsv関数を用いました。
この関数やクラスを使わなくても実現は可能

csvとは?

「CSV」とは "Comma Separated Value" の略で、データをカンマ(" , ")区切った値の事です。アプリケーション間でデータをやり取りする際に使われます。CSV形式で保存されたファイルを「CSVファイル」と呼びます。

Excelとの違いはExcelは文字に色とか罫線とかデータに装飾可能ですが、csvはただテキストで構成されているデータみたいです。
余分な情報がないのでアプリケーション間でテキストデータのやり取りが可能になります。

いざ実装

  • 出力したいデータ
$user = array(
    array(
        'id' => 1,
        'name' => 'Aさん',
        'email' => '[email protected]',
        'password' => 'aaaaa'
    ),
    array(
        'id' => 2,
        'name' => 'Bさん',
        'email' => '[email protected]',
        'password' => 'bbbbb'
    ),
    array(
        'id' => 3,
        'name' => 'Cさん',
        'email' => '[email protected]',
        'password' => 'ccccc'
    ),
);

※こういうデータをDBから取ってきて配列に入れる。


  • ダウンロードボタン表示
<h1>Hello World!<h1>
<a href="./csv.php">
    <button>csvダウンロード</button>
</a>


  • csv出力andダウンロード処理

$user =[配列]

function putCsv($data) {

    try {

        //CSV形式で情報をファイルに出力のための準備
        $csvFileName = '/tmp/' . time() . rand() . '.csv';
        $fileName = time() . rand() . '.csv';
        $res = fopen($csvFileName, 'w');
        if ($res === FALSE) {
            throw new Exception('ファイルの書き込みに失敗しました。');
        }

        // 項目名先に出力
        $header = ["id", "name", "email", "password"];
        fputcsv($res, $header);

        // ループしながら出力
        foreach($data as $dataInfo) {
            // 文字コード変換。エクセルで開けるようにする
            mb_convert_variables('SJIS', 'UTF-8', $dataInfo);

            // ファイルに書き出しをする
            fputcsv($res, $dataInfo);
        }

        // ファイルを閉じる
        fclose($res);

        // ダウンロード開始

        // ファイルタイプ(csv)
        header('Content-Type: application/octet-stream');

        // ファイル名
        header('Content-Disposition: attachment; filename=' . $fileName); 
        // ファイルのサイズ ダウンロードの進捗状況が表示
        header('Content-Length: ' . filesize($csvFileName)); 
        header('Content-Transfer-Encoding: binary');
        // ファイルを出力する
        readfile($csvFileName);

    } catch(Exception $e) {

        // 例外処理をここに書きます
        echo $e->getMessage();

    }
}

putCsv($user);

※追記(@rana_kualu さんのコメントを受けて)

上記の方法では出力するために作った$csvFileNameファイルが/tmp/に残ったままになっています。

  • ファイルを出力した後にunlinkでファイルを削除する。
  • tmpfileで自動的に一時ファイルを削除する。
  • fopenfilenamephp://outputを指定して一時ファイルを作らずcsv出力する。

一時ファイル


■fopen関数で出力するファイルを指定して開く
第2引数にwを指定して書き込みモードにする。

■fputcsv関数で上記で開いたファイルにcsvを出力していく
foreachで回す前にヘッダーとなる部分を出力する。
その後にforeachで配列を回して出力する。

■ダウンロードのためにHTTPヘッダーを設定する
Content-Type以下でphpの出力形式を指定する。
今回はcsvファイルとして出力する指定をしているので、ブラウザで閲覧できるページとしては出力されない。

出力結果


mb_convert_variables('SJIS', 'UTF-8', $dataInfo);で文字コードを変換しているので日本語部分の「さん」がちゃんと表示されている。
これがないとExcelで開いたときに文字化けしてしまう。

以上です