PHPのファイルの読み込み・書き込み・掲示板作成

📁 作成ファイル・保存場所

XAMPPがインストールされているhtdocs フォルダに以下のファイルを作成してください。

ファイル名内容
htdocs/php-basic/read.php
htdocs/php-basic/write.php
htdocs/php-basic/bbs.php
PHPとHTML
  • XAMPP(Apacheが起動していること)
  • XAMPPがインストールされているhtdocs フォルダに内に作成されていること

PHP基礎トレーニング

テキストファイルの読み込み

「sample.txt」ファイルを作成し、下記の日付と名前をカンマ区切りで作成

2025-08-30,山田太郎
2025-08-31,佐藤花子
2025-09-01,鈴木一郎

fopen関数を使って、sample.txtを読み込むプログラムを作成します。

<?php
$filename = 'sample.txt';
$errors = [];
$rows = [];

// エスケープ関数(h)を定義
function h($str) {
    return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
}

// ファイルがあるか、読み込み可能かをチェック
if (!file_exists($filename)) {
    $errors[] = 'ファイルが見つかりません。';
} elseif (!is_readable($filename)) {
    $errors[] = 'ファイルが読み込めません。';
} else {
    $fp = fopen($filename, 'r');  // ファイルを読み込みモードで開く
    if ($fp) {
    while (($line = fgets($fp)) !== false) {  // 1行ずつ読み込み 2025-08-30,山田太郎
        $line = trim($line);                 // 改行文字や空白を除去
        if ($line === ''){
          continue;
         }// 空行はスキップ
        $cols = explode(',', $line);         // カンマで分割(→ 配列)
        if (count($cols) === 2) {
            $rows[] = $cols;                 // データ行として追加
        }
    }
    fclose($fp);  // ファイルを閉じる
    } else {
    $errors[] = 'ファイルのオープンに失敗しました。';
  }

}
?>
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <title>fopenでメモリに優しい読み込み</title>
    <style>
        .red { color: red; }
    </style>
</head>

<body>
    <h1>fopenでメモリに優しい読み込み</h1>

    <?php if (!empty($errors)): ?>
        <ul class="red">
            <?php foreach ($errors as $e): ?>
                <li><?= h($e) ?></li>
            <?php endforeach; ?>
        </ul>
    <?php else: ?>
        <p>sample.txt の中身を表示します:</p>
        <ul>
            <?php foreach ($rows as $cols): ?>
                <li>
                    日付:<?= h($cols[0]) ?> 
                    名前:<?= h($cols[1]) ?>
                </li>
            <?php endforeach; ?>
        </ul>
    <?php endif; ?>
</body>

</html>
処理内容使っている関数説明
ファイルがあるか? 読めるか?file_exists()
is_readable()
ファイルが存在し、読み込み可能かどうかを確認
ファイルを開くfopen($filename, 'r')読み込みモード(r)でファイルを開く
1行ずつ読み込むfgets()ファイルを1行ずつ読み込む(メモリにやさしい)
改行を削除trim()読み込んだ1行の両端の空白・改行を除去
データを分割explode(',', $line)カンマで区切って「日付」「名前」に分ける
読み込み終了後に閉じるfclose()ファイルを閉じる(開きっぱなしにしない)

次に読み込んで・書き込んでみよう

上記の「sample.txt」ファイルがある前提で行います。

<?php
$filename = 'sample.txt';
$errors = [];
$rows = [];

// エスケープ関数 h() を定義
function h($str) {
    return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
}

// ---------------------------
// 投稿があればファイルに書き込む
// ---------------------------
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $name = trim($_POST['name'] ?? '');

if ($name === '') {
    // 名前が未入力の場合はエラーメッセージを配列に追加
    $errors[] = '名前を入力してください。';
} else {
    // 日付を取得(例:2025-09-04)
    $date = date('Y-m-d');

    // ファイルを「追記モード(a)」で開く
    $fp = fopen($filename, 'a');

    if ($fp) {
        // 書き込む内容を「日付,名前」の形式で用意し、改行を追加
        $line = $date . ',' . $name . "\n";

        // ファイルに1行書き込む(末尾に追記)
        fwrite($fp, $line);

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

        // フォームの再送信(リロード時の重複投稿)を防ぐため、同じページへリダイレクト
        header('Location: ' . $_SERVER['PHP_SELF']);
        exit; // リダイレクト後に処理を終了
    } else {
        // ファイルを開けなかった場合のエラーメッセージ
        $errors[] = 'ファイルの書き込みに失敗しました。';
    }
}

}

// ---------------------------
// ファイル読み込み処理(fopen + fgets)
// ---------------------------
if (!file_exists($filename)) {
    $errors[] = 'ファイルが見つかりません。';
} elseif (!is_readable($filename)) {
    $errors[] = 'ファイルが読み込めません。';
} else {
    $fp = fopen($filename, 'r');
    if ($fp) {
        while (($line = fgets($fp)) !== false) {
            $line = trim($line);
            if ($line === '') continue;
            $cols = explode(',', $line);
            if (count($cols) === 2) {
                $rows[] = $cols;
            }
        }
        fclose($fp);
        $rows = array_reverse($rows);
    } else {
        $errors[] = 'ファイルのオープンに失敗しました。';
    }
}
?>
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <title>PHP:名前を投稿してファイルに保存</title>
    <style>
        .red { color: red; }
        form { margin-bottom: 2em; }
    </style>
</head>

<body>
    <h1>名前の投稿フォーム</h1>

    <form method="post">
        <label for="name">名前を入力してください:</label>
        <input type="text" id="name" name="name" value="<?= h($_POST['name'] ?? '') ?>">
        <button type="submit">投稿</button>
    </form>

    <?php if (!empty($errors)): ?>
        <ul class="red">
            <?php foreach ($errors as $e): ?>
                <li><?= h($e) ?></li>
            <?php endforeach; ?>
        </ul>
    <?php endif; ?>

    <h2>投稿一覧</h2>
    <?php if (empty($rows)): ?>
        <p>まだ投稿はありません。</p>
    <?php else: ?>
        <ul>
            <?php foreach ($rows as $cols): ?>
                <li>
                    日付:<?= h($cols[0]) ?> 
                    名前:<?= h($cols[1]) ?>
                </li>
            <?php endforeach; ?>
        </ul>
    <?php endif; ?>
</body>

</html>

このコードの処理まとめ(簡単な解説)

処理内容使っている関数説明
名前をフォームで受け取る$_POST['name']入力された名前を受け取って変数に代入(空チェックもする)
ファイルに追記するfopen($filename, 'a')
fwrite()
追記モードでファイルを開き、名前と日付を1行書き込む
書き込み後に再読み込みするheader('Location: ...')フォームの再送信を防ぐため、自分自身にリダイレクト(PRGパターン)
ファイルを開いて読み込むfopen($filename, 'r')
fgets()
読み込みモードで開いて、1行ずつ読み込む(メモリにやさしい)
読み込んだ行を分割するexplode(',', $line)カンマで分割して「日付」と「名前」に分ける
表示の順番を新しい順にするarray_reverse($rows)古い順で読み込まれた投稿を逆にして、新しい順に並べ替える
ファイルを閉じるfclose($fp)ファイルを開いた後は必ず閉じる(リソース解放)

??(null合体演算子)の使い方

$name = trim($_POST[‘name’] ?? ”);

このコードでは、フォームから送られてきた「名前」の値を取得しています。
ただし、POSTで送られていない場合(未定義)は空文字にしたい、という目的です。

従来のif文を使って書くと・・・

if (isset($_POST['name'])) {
    $name = trim($_POST['name']);
} else {
    $name = '';
}

  • $_POST['name'] がセットされていれば(isset)→ その値を使う
  • そうでなければ(未送信や空)→ 空文字 '' を使う
  • trim()前後の空白も取り除いている

少し進化して、isset() + 三項演算子を使った省略形

同じことを1行で書く省略形

$name = isset($_POST['name']) ? trim($_POST['name']) : '';
  • isset($_POST['name']) が true のとき → trim($_POST['name']) を使う
  • false のとき → ''(空文字)を使う
  • ?: は「三項演算子」と呼ばれ、if文を短く書くための記法

さらに進化した null合体演算子(??)を使った書き方(PHP7以降)

PHP7からは、さらにスッキリ書けるようになりました。

$name = trim($_POST['name'] ?? '');
  • ?? は「null合体演算子」と呼ばれます
  • $_POST['name']存在し、かつ null でなければ それを使う
  • 存在しなければ '' を使う
  • 三項演算子+isset の省略形として非常に使いやすい

PHP応用トレーニング

ファイル構成bbs.phpとbbs.txtを同じフォルダに作成

<?php
$filename = 'bbs.txt';
$errors = [];
$rows = [];
$maxCommentLength = 100;

// エスケープ関数
function h($str)
{
    return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
}

// ---------------------------
// 投稿があればファイルに書き込む
// ---------------------------
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $name = trim($_POST['name'] ?? '');
    $comment = trim($_POST['comment'] ?? '');

    // 入力チェック
    if ($name === '') {
        $errors[] = '名前を入力してください。';
    }
    if ($comment === '') {
        $errors[] = 'コメントを入力してください。';
    } elseif (mb_strlen($comment) > $maxCommentLength) {
        $errors[] = 'コメントは' . $maxCommentLength . '文字以内で入力してください。';
    }

    if (count($errors)===0) {
        $date = date('Y-m-d');

        $fp = fopen($filename, 'a');
        if ($fp) {
            $line = $date . ',' . $name . ',' . $comment . "\n";
            fwrite($fp, $line);
            fclose($fp);
            header('Location: ' . $_SERVER['PHP_SELF']);
            exit;
        } else {
            $errors[] = 'ファイルの書き込みに失敗しました。';
        }
    }
}

// ---------------------------
// ファイル読み込み
// ---------------------------
if (!file_exists($filename)) {
    $errors[] = 'ファイルが見つかりません。';
} elseif (!is_readable($filename)) {
    $errors[] = 'ファイルが読み込めません。';
} else {
    $fp = fopen($filename, 'r');
    if ($fp) {
        while (($line = fgets($fp)) !== false) {
            $line = trim($line);
            if ($line === '') {
                continue;
            }

            $cols = explode(',', $line, 3); // 日付, 名前, コメント
            if (count($cols) === 3) {
                $rows[] = $cols;
            }
        }
        fclose($fp);
        $rows = array_reverse($rows); // 新しい順に
    } else {
        $errors[] = 'ファイルのオープンに失敗しました。';
    }
}
?>
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <title>PHP:名前とコメントを投稿する掲示板</title>
    <style>
        .red {
            color: red;
        }

        h1 {
            text-align: center;
        }

        main {
            max-width: 600px;
            margin: 0 auto;
        }

        form {
            margin-bottom: 2em;
        }

        textarea {
            width: 100%;
            height: 4em;
        }

        ul li {
            margin-bottom: 1em;
        }
    </style>
</head>

<body>
    <h1>ミニ掲示板</h1>

    <main>
        <form method="post">
            <label for="name">名前:</label><br>
            <input type="text" id="name" name="name"><br><br>

            <label for="comment">コメント(最大<?= h($maxCommentLength) ?>文字):</label><br>
            <textarea id="comment" name="comment"></textarea><br><br>

            <button type="submit">投稿する</button>
        </form>

        <?php if (!empty($errors)): ?>
            <ul class="red">
                <?php foreach ($errors as $e): ?>
                    <li><?= h($e) ?></li>
                <?php endforeach; ?>
            </ul>
        <?php endif; ?>
        <hr>
        <h2>投稿一覧(新しい順)</h2>
        <?php if (empty($rows)): ?>
            <p>まだ投稿はありません。</p>
        <?php else: ?>
            <ul>
                <?php foreach ($rows as $cols): ?>
                    <li>
                        <strong><?= h($cols[1]) ?></strong>(<?= h($cols[0]) ?>)<br>
                        <?= nl2br(h($cols[2])) ?>
                    </li>
                <?php endforeach; ?>
            </ul>
        <?php endif; ?>

    </main>
</body>

</html>
処理内容使用している関数・構文解説
エスケープ関数を定義function h($str) { ... }出力時に htmlspecialchars を短く書けるようにした関数
フォームが送信されたか判定$_SERVER['REQUEST_METHOD'] === 'POST'POSTリクエストかどうかを判定して投稿処理に入る
入力値の取得とトリム処理trim($_POST['name'])
trim($_POST['comment'])
空白を取り除いた状態で変数に格納
入力チェックif ($name === '')
mb_strlen()
空欄か、文字数制限を超えていないかチェック
ファイルを追記モードで開くfopen($filename, 'a')投稿を末尾に追加する(a = append)
書き込む内容を組み立てて保存$line = 日付,名前,コメント
fwrite()
カンマ区切りの文字列を1行書き込む
再送信防止リダイレクト(PRG)header('Location: ...')
exit
フォームの再送信による重複投稿を防ぐ
ファイルの存在と読み込みチェックfile_exists()
is_readable()
ファイルがなければエラーメッセージを表示
ファイルを読み込むfopen($filename, 'r')
fgets()
ファイルを1行ずつ読み込み、配列に追加
カンマで分割explode(',', $line, 3)書式:日付,名前,コメント に分解(3つに限定)
新しい順に並べ替えるarray_reverse($rows)投稿一覧を新しい順に表示したいので並び替え
HTML出力時のエスケープh()
nl2br()
出力を安全にしつつ、コメントの改行も見やすく表示