27. CSVデータの書き込み

CSVデータの書き込み

CSVデータを1行書き込むには fputcsv() を利用します。

fputcsv()

fputcsv() に配列データを渡すと、ファイルにCSVフォーマットで書き込みます。

fputcsv(リソース, 配列データ);

HTMLフォーム

入力画面

氏名、メールアドレス、パスワードを入力し【確認】で confirm.php にPOST送信します。

input.php

※CSSは Bootstrap5 の CDN を利用しています

<form action="confirm.php" method="post">
    <div class="mb-3">
        <label class="form-label fw-bold">氏名</label>
        <input type="text" name="name" value="<?= @$user['name'] ?>" class="form-control">
        <p class="text-danger badge"><?= @$errors['name'] ?></p>
    </div>
    <div class="mb-3">
        <label class="form-label fw-bold">メールアドレス</label>
        <input type="text" name="email" value="<?= @$user['email'] ?>" class="form-control">
        <p class="text-danger badge"><?= @$errors['email'] ?></p>
    </div>
    <div class="mb-3">
        <label class="form-label fw-bold">パスワード</label>
        <input type="password" name="password" class="form-control">
        <p class="text-danger badge"><?= @$errors['password'] ?></p>
    </div>
    <button class="btn btn-primary">確認</button>
</form>

確認画面

確認画面ではPOST送信されたデータを表示します。まあフォームで【登録】ボタンで add.php にPOST送信するようにします。

confirm.php
<form action="add.php" method="post">
    <h2 class="h2">会員登録</h2>
    <div class="mb-3">
        <label class="fw-bold">氏名</label>
        <p class="mt-2"><?= $user['name'] ?></p>
    </div>
    <div class="mb-3">
        <label class="fw-bold">メールアドレス</label>
        <p class="mt-2"><?= $user['email'] ?></p>
    </div>
    <div>
        <p>この内容でよろしいですか?</p>
        <a class="btn btn-outline-primary" href="input.php">戻る</a>
        <button class="btn btn-primary">登録</button>
    </div>
</form>

CSV初期化

add.php で会員登録処理をします。確認画面でセッションに保存したデータを users.csv に書き込みます。

CSVファイルパスの設定

PHPの定数で、CSVファイルのパスを設定しておきます。ファイルパスは data/users.csv としました。

define('CSV_PATH', 'data/users.csv');

カラムの定義

ユーザデータのカラムを配列で用意しておきます。このデータは CSVファイルの1行目に書き込みます。

$columns = ['name', 'email', 'password'];

CSV初期化

CSVファイルを開く

CSVファイルの初期化関数 initCSV() を定義します。

function initCSV($data)
{
}

CSVファイルを a+ (追記 & 読み込みモード)で開きます。もしファイルがなければ自動的に作成されます。

function initCSV($data)
{
    $file = fopen(CSV_PATH, 'a+');
}

初期データの書き込み

もしCSVファイルがなければ作成し、データがなかったら書き込みます。

function initCSV($data)
{
    $file = fopen(CSV_PATH, 'a+');
    flock($file, LOCK_EX);
    if (!fgets($file)) {
        fputcsv($file, $data);
    }
    flock($file, LOCK_UN);
    fclose($file);
}

CSV初期化の確認

確認画面から登録して、CSVファイルの初期化をしてみましょう。

define('CSV_PATH', 'data/users.csv');
$columns = ['name', 'email', 'password'];

if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    initCSV($columns);
}

data/users.csv ファイルが作成され、name,email,password が書き込まれていることを確認します。

CSVデータ書き込み

CSV書き込み処理 insert() メソッドを定義します。引数にはデータ $data とカラム $columns を渡します。

function insert($data, $columns)
{

}

パスワードの暗号化

パスワードは暗号化しておきましょう。PHPでは password_hash() で簡単に暗号化できます。

password_hash(パスワード, アルゴリズム, ソルト)
アルゴリズム 説明
PASSWORD_DEFAULT bcryptアルゴリズムで PASSWORD_BCRYPT と同じ
PASSWORD_BCRYPT bcryptアルゴリズム
PASSWORD_ARGON2I Argon2i ハッシュアルゴリズム
PASSWORD_ARGON2ID Argon2id ハッシュアルゴリズム

ソルト

ソルトはパスワードをさらに独自に強化するオプションです。今回は利用しません。

パスワードの暗号化

パスワードを PASSWORD_BCRYPT で暗号化します。

$data['password'] = password_hash($data['password'], PASSWORD_BCRYPT);

CSVデータ書き込み

CSVファイルを a(追記モード)で開き、POSTデータをCSV書き込みします。

function insert($data)
{
    foreach ($columns as $column) {
        $data[$column] = htmlspecialchars($data[$column]);
    }
    $data['password'] = password_hash($data['password'], PASSWORD_BCRYPT);

    $file = fopen(CSV_PATH, 'a');
    flock($file, LOCK_EX);
    fputcsv($file, $data);
    flock($file, LOCK_UN);
    fclose($file);
}

データ書き込みを確認

insert() でセッションデータを渡して実行します。データがない場合は、input.php にリダイレクトします。

if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_SESSION['user'])) {
    initCSV($columns);
    insert($_SESSION['user']);

    unset($_SESSION['user']);
    unset($_SESSION['errors']);
} else {
    header('Location: input.php');
}

HTMLフォームでデータ送信して確認してみましょう。

users.csv にデータが追記されました。

ソース

商品一覧

item_list.php

<?php
$file_path = 'data/items.csv';
$items = loadCSV($file_path);

function loadCSV($file_path)
{
    if (!file_exists($file_path)) return;

    $items = [];
    $file = fopen($file_path, 'r');
    $columns = fgetcsv($file);
    while ($data = fgetcsv($file)) {
        foreach ($columns as $index => $column) {
            $item[$column] = $data[$index];
        }
        $items[] = $item;
    }
    fclose($file);
    return $items;
}
?>
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
</head>

<body>
    <div class="container">
        <h3 class="h3">商品一覧</h3>
        <dl>
            <?php if ($items) : ?>
            <?php foreach ($items as $item) : ?>
                <dt><?= $item['name'] ?></dt>
                <dd><?= $item['price'] ?>円</dd>
            <?php endforeach ?>
            <?php endif ?>
        </dl>
    </div>
</body>

</html>
input.php
<?php
session_start();
$user = ['name' => '', 'email' => '', 'password' => ''];
if (isset($_SESSION['user'])) $user = $_SESSION['user'];
if (isset($_SESSION['errors'])) $errors = $_SESSION['errors'];
?>
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link 
    href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" 
    rel="stylesheet">
</head>

<body>
    <div class="container">
        <h2 class="h2">会員登録</h2>
        <form action="confirm.php" method="post">
            <div class="mb-3">
                <label class="form-label fw-bold">氏名</label>
                <input type="text" name="name" value="<?= @$user['name'] ?>"
                 class="form-control">
                 <p class="text-danger badge"><?= @$errors['name'] ?></p>
            </div>
            <div class="mb-3">
                <label class="form-label fw-bold">メールアドレス</label>
                <input type="text" name="email" value="<?= @$user['email'] ?>"
                 class="form-control">
                 <p class="text-danger badge"><?= @$errors['email'] ?></p>
            </div>
            <div class="mb-3">
                <label class="form-label fw-bold">パスワード</label>
                <input type="password" name="password"
                 class="form-control">
                 <p class="text-danger badge"><?= @$errors['password'] ?></p>
            </div>
            <button class="btn btn-primary">確認</button>
        </form>
    </div>
</body>

</html>
confirm.php
<?php
session_start();
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    $user = $_POST;
    $errors = validate($user);

    $_SESSION['user'] = $user;
    $_SESSION['errors'] = $errors;
}
if (empty($user) || !empty($errors)) {
    header('Location: input.php');
    exit;
}

function validate($user)
{
    $errors = [];
    if (empty($user['name'])) {
        $errors['name'] = '氏名を入力してください';
    }
    if (empty($user['email'])) {
        $errors['email'] = 'メールアドレスを入力してください';
    }
    if (empty($user['password'])) {
        $errors['password'] = 'パスワードを入力してください';
    }
    return $errors;
}
?>
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link 
    href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" 
    rel="stylesheet">
</head>

<body>
    <div class="container">
        <form action="add.php" method="post">
            <h2 class="h2">会員登録</h2>
            <div class="mb-3">
                <label class="fw-bold">氏名</label>
                <p class="mt-2"><?= $user['name'] ?></p>
            </div>
            <div class="mb-3">
                <label class="fw-bold">メールアドレス</label>
                <p class="mt-2"><?= $user['email'] ?></p>
            </div>
            <div>
                <p>この内容でよろしいですか?</p>
                <a class="btn btn-outline-primary" href="input.php">戻る</a>
                <button class="btn btn-primary">登録</button>
            </div>
        </form>
    </div>
</body>

</html>
add.php
<?php
session_start();
define('CSV_PATH', 'data/users.csv');
$columns = ['name', 'email', 'password'];

if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    exit;
}
if (empty($_SESSION['user'])) {
    header('Location: input.php');
}

initCSV($columns);
insert($_SESSION['user'], $columns);

unset($_SESSION['user']);
unset($_SESSION['errors']);

function initCSV($data)
{
    $file = fopen(CSV_PATH, 'a+');
    flock($file, LOCK_EX);
    if (!fgets($file)) {
        fputcsv($file, $data);
    }
    flock($file, LOCK_UN);
    fclose($file);
}

function insert($data)
{
    foreach ($columns as $column) {
        $data[$column] = htmlspecialchars($data[$column]);
    }
    $data['password'] = password_hash($data['password'], PASSWORD_BCRYPT);

    $file = fopen(CSV_PATH, 'a');
    flock($file, LOCK_EX);
    fputcsv($file, $data);
    flock($file, LOCK_UN);
    fclose($file);
}
?>
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link 
    href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" 
    rel="stylesheet">
</head>

<body>
    <div class="container">
        <h2 class="h2">会員登録完了</h2>
        <p>会員登録が完了しました。</p>
        <div>
            <a href="input.php" class="btn btn-outline-primary">戻る</a>
        </div>
    </div>
</body>

</html>

PHP超入門