PHPで配列をCSV出力する

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

### 今回の内容
csvとして出力したデータをボタンを押すとダウンロードする

こんな感じ
![csv-test.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/464231/a34d222f-260f-a979-8e52-902e16763abb.gif)



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


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

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

- 出力したいデータ  

```
$user = array(
    array(
        'id' => 1,
        'name' => 'Aさん',
        'email' => 'aaa@a.com',
        'password' => 'aaaaa'
    ),
    array(
        'id' => 2,
        'name' => 'Bさん',
        'email' => 'bbb@b.com',
        'password' => 'bbbbb'
    ),
    array(
        'id' => 3,
        'name' => 'Cさん',
        'email' => 'ccc@c.com',
        'password' => 'ccccc'
    ),
);
```
※こういうデータをDBから取ってきて配列に入れる。

***
- ダウンロードボタン表示

```html
<h1>Hello World!<h1>
<a href="./csv.php">
    <button>csvダウンロード</button>
</a>
```
![スクリーンショット 2020-10-25 11.31.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/464231/5f0394eb-21db-da22-4bde-bd5f9f20a40c.png)
***
- csv出力andダウンロード処理


```php

$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](https://www.php.net/manual/ja/function.unlink.php)でファイルを削除する。
- [tmpfile](https://www.php.net/manual/ja/function.tmpfile.php)で自動的に一時ファイルを削除する。
- `fopen`の`filename`に[php://output](https://www.php.net/manual/ja/wrappers.php.php#wrappers.php.output)を指定して一時ファイルを作らずcsv出力する。

※[一時ファイル](http://e-words.jp/w/%E4%B8%80%E6%99%82%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB.html#:~:text=%E4%B8%80%E6%99%82%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%A8%E3%81%AF%E3%80%81%E3%82%BD%E3%83%95%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A2,%E3%81%95%E3%82%8C%E3%82%8B%E3%81%93%E3%81%A8%E3%81%8C%E5%A4%9A%E3%81%84%E3%80%82)


***

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

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

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

## 出力結果  
![スクリーンショット 2020-10-25 11.41.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/464231/fc1619e9-2b81-c347-5391-819a404051be.png)
`mb_convert_variables('SJIS', 'UTF-8', $dataInfo);`で文字コードを変換しているので日本語部分の**「さん」**がちゃんと表示されている。
これがないとExcelで開いたときに文字化けしてしまう。

以上です