1. PHPのファイル操作とは?

PHPはサーバーサイドで動作するプログラミング言語であるため、サーバー内にあるファイルの作成、読み込み、書き込み、削除といった操作を自由に行うことができます。これを「ファイル操作」と呼びます。

なぜファイル操作が必要なのか?

Web開発の現場では、データベースを使うまでもない小規模なデータの保存や、システムの状態を記録するためにファイル操作が頻繁に使われます。

  • ログの保存: エラー情報やアクセス履歴をテキストファイルに残す。
  • CSVの出力: 顧客データや商品リストをExcelで開ける形式でダウンロードさせる。
  • 画像のアップロード: ユーザーが投稿したプロフィール画像をサーバーに保存する。
  • 設定ファイルの読み込み: JSON形式などの設定データを読み込んでプログラムに反映する。

JavaScript(フロントエンド)とは異なり、サーバー内の重要ファイルにアクセスできるため、セキュリティへの意識も非常に重要になります。

2. 【基本】 ファイルの読み書き(fopen, fwrite, fclose)の基本

PHPでファイル操作を行う最も基本的な方法は、「ファイルを開く(Open)→ 操作する(Write/Read)→ 閉じる(Close)」という3つのステップを踏むことです。
これは、私たちがノートに文字を書くときに「ノートを開いて、ペンで書いて、閉じる」という動作と同じイメージです。

基本的な関数の役割

  • fopen(): ファイルを開く(ファイルポインタを取得する)。
  • fwrite(): ファイルに文字を書き込む。
  • fgets() / fread(): ファイルの内容を読み込む。
  • fclose(): ファイルを閉じる(重要: メモリ解放のため必ず閉じる)。

実践コード:ファイルへの書き込み

ここでは、「test.txt」というファイルを作成し、文字を書き込む例を見てみましょう。

<?php
// 書き込み対象のファイルパスを指定します。
$filename = 'test.txt';

/*
* 1. fopen(ファイル名, モード)
*
* - ファイルを**オープン**し、ファイルリソース(ファイルポインタ)を返します。
* - $filename (ファイル名): 開くファイルパスを指定します。
* - 'w' (モード): ファイルのオープンモードを指定します。
* - **'w' (write)**: **書き込み専用**モードでファイルを開きます。
* - ファイルポインタは**ファイルの先頭**に設定されます。
* - ファイルが**既に存在する場合、その内容はすべて削除(上書き)されます**。
* - ファイルが**存在しない場合、新しくファイルが作成**されます。
* - 成功した場合はファイルリソース ($fp) を、失敗した場合は **false** を返します。
*/
$fp = fopen($filename, 'w');

// ファイルが正常にオープンできたか($fp が false ではないか)を確認します。
if ($fp) {
    /*
    * 2. fwrite(ファイルポインタ, データ, [書き込みサイズ])
    *
    * - ファイルポインタ ($fp) が指すファイルにデータを**書き込み**ます。
    * - 第二引数に書き込む文字列を指定します。"\n" は改行コードです。
    * - 成功した場合は書き込まれたバイト数を返し、失敗した場合は **false** を返します。
    */
    fwrite($fp, "こんにちは、PHP!\n");
    fwrite($fp, "ファイル操作の基本です。");

    /*
    * 3. fclose(ファイルポインタ)
    *
    * - 開いているファイルリソース ($fp) を**クローズ(閉じる)**します。
    * - 書き込んだ内容を**確実に保存**し、ファイルへのロックを解除するために、ファイル操作の最後には必ず実行する必要があります。
    * - 成功した場合は **true** を、失敗した場合は **false** を返します。
    */
    fclose($fp);
    echo "書き込みが完了しました。";
} else {
    // ファイルのオープンに失敗した場合(例: フォルダへの書き込み権限がない場合など)の処理です。
    echo "ファイルのオープンに失敗しました。";
}
?>

重要な「モード」の指定

fopenの第2引数で指定する「モード」によって、挙動が変わります。

モード説明特徴
‘r’読み込みのみファイルの先頭から読み込む。書き込み不可。
‘w’書き込みのみファイルの中身を空にしてから書き込む(上書き)。ファイルがなければ作成。
‘a’追記書き込みファイルの末尾に追加で書き込む。元の中身は消えない。

3. 【応用】 便利な一括読み書き関数(file_get_contentsとfile_put_contents)

初心者の方におすすめなのが、fopenやfcloseといった手順を省略し、1行で読み書きができる便利な関数です。実務でも、単純なファイルのやり取りにはこちらがよく使われます。

データを一括で書き込む file_put_contents

ファイルを開く、書く、閉じるをすべて自動で行ってくれます。

<?php
// 書き込み先のファイルパスを指定します。
$file = 'log.txt';
// ファイルに書き込む内容を指定します。
// "\n" は改行コードです。
$text = "エラーが発生しました。\n";

/*
* file_put_contents(ファイル名, データ, フラグ)
*
* 1. $file (ファイル名): 書き込み対象のファイルパスを指定します。
* - ファイルが存在しない場合は、新しく作成されます。
* - 書き込み権限がない場合は失敗します。
* 2. $text (データ): ファイルに書き込む文字列またはデータを指定します。
* 3. FILE_APPEND (フラグ): オプションのフラグです。
* - **FILE_APPEND** を指定すると、既存のファイルの内容の**末尾に追記**します。
* - これを指定しない場合(デフォルト)は、既存の内容は**上書き**され、新しい内容に置き換わります。
*
* この関数は、ファイルへの書き込みが成功した場合に書き込まれたバイト数を返し、失敗した場合は **false** を返します。
*/
file_put_contents($file, $text, FILE_APPEND);
?>

データを一括で読み込む file_get_contents

ファイルの中身をすべて文字列として取得します。

<?php
// 読み込みたいファイルパスを指定します。
$file = 'log.txt';

/*
* 🔔 ファイル読み込みの定石(ベストプラクティス)
*
* 1. file_exists(ファイルパス):
* - 指定された $file が**存在するかどうか**を論理値 (true/false) で確認します。
* - ファイルが存在しない状態で file_get_contents を実行すると、PHPは警告 (Warning) を出し、処理が失敗する可能性があります。
* - この確認を行うことで、処理の安定性が向上します。
*/
if (file_exists($file)) {
    /*
    * file_get_contents(ファイル名):
    *
    * - ファイル全体を**文字列として**メモリに読み込み、その内容を返します。
    * - ファイルをオープンし、読み込み、クローズする一連の操作を自動で行います。
    * - 成功した場合はファイルの内容(文字列)を、失敗した場合は **false** を返します。
    *
    * ⚠️ 注意点: 非常に大きなファイルを読み込むとメモリを大量に消費する可能性があるため、その場合は stream_get_contents や fopen/fread を使用する方が適切です。
    */
    $content = file_get_contents($file);

    /*
    * nl2br(文字列):
    *
    * - 文字列中の**改行コード (\n, \r\n など) を HTML の <br> タグに変換**します。
    * - これにより、ファイルの内容をウェブブラウザで表示した際に、ファイル内の改行がそのままブラウザ上でも改行として表示されます。
    */
    echo nl2br($content);
} else {
    // ファイルが存在しない場合の処理(エラーメッセージ表示など)をここに記述できます。
    echo 'ファイルが見つかりませんでした: ' . $file;
}
?>

ポイント:
非常に大きなファイル(数GB単位など)をfile_get_contentsで読み込むと、メモリ不足でエラーになることがあります。その場合は、基本のfopenを使って少しずつ読み込む方法(ストリーム処理)を使います。

4. 画像ファイルをアップロードする手順とセキュリティ対策

Webサイトのお問い合わせフォームやSNS機能などで、ユーザーに画像をアップロードしてもらうケースです。ここではHTMLのフォームとPHPの連携が必要になります。

1. HTML側の準備(enctype属性が必須)

フォームタグに enctype="multipart/form-data" を必ず指定します。これがないとファイルデータが送信されません。

<form action="upload.php" method="post" enctype="multipart/form-data">
    <input type="file" name="upload_image">
    <input type="submit" value="アップロード">
</form>

2. PHP側での保存処理 move_uploaded_file

送信されたファイルは一時的な場所に保存されるため、それを本来保存したい場所へ移動させます。

<?php
// アップロードされたファイルの情報は $_FILES に入る
if (!empty($_FILES['upload_image']['name'])) {
    
    // セキュリティ対策1:ファイル名の衝突や日本語ファイル名を避けるためリネーム
    // uniqid() で一意なIDを生成し、元の拡張子をつけるなどが一般的
    $filename = date('YmdHis') . '_' . $_FILES['upload_image']['name'];
    $save_path = 'uploads/' . $filename;

    // セキュリティ対策2:画像ファイルかどうか簡易チェック(MIMEタイプ確認)
    $allow_types = ['image/jpeg', 'image/png', 'image/gif'];
    if (in_array($_FILES['upload_image']['type'], $allow_types)) {
        
        // 一時フォルダから指定の場所へ移動
        if (move_uploaded_file($_FILES['upload_image']['tmp_name'], $save_path)) {
            echo "アップロード成功!";
        } else {
            echo "保存に失敗しました。";
        }
    } else {
        echo "許可されていないファイル形式です。";
    }
}
?>

絶対にやるべきセキュリティ対策

ユーザーがアップロードするファイルは、ウイルスや不正なプログラム(Webシェルなど)である可能性があります。

  • 拡張子のチェック: .php などの実行ファイルをアップロードさせない。
  • ファイル名のリネーム: ユーザーが付けたファイル名をそのまま使わず、システム側でランダムな名前に変更して保存する。
  • 保存場所の権限: アップロードフォルダ内でプログラムが実行できないようにサーバー設定(.htaccessなど)を行う。

5. ファイルのメタ情報取得と削除の方法

ファイルの中身を見るだけでなく、「ファイルがあるか?」「サイズはどれくらいか?」といった情報(メタ情報)を扱う関数も重要です。

よく使う関数一覧

  • file_exists($path): ファイルやディレクトリが存在するか確認する(最重要)。
  • unlink($path): ファイルを削除する。
  • filesize($path): ファイルの容量(バイト数)を取得する。
  • filemtime($path): ファイルの最終更新日時を取得する。

実践コード:古いファイルの削除

「存在確認」をしてから「削除」を行うのが安全な書き方です。

<?php
$file = 'old_data.txt';

if (file_exists($file)) {
    // 削除実行
    if (unlink($file)) {
        echo "ファイルを削除しました。";
    } else {
        echo "削除に失敗しました。";
    }
} else {
    echo "削除するファイルが見つかりません。";
}
?>

6. ディレクトリ(フォルダ)の作成・削除・一覧表示

ファイルだけでなく、フォルダ(ディレクトリ)自体の操作もPHPで行えます。

ディレクトリ操作の基本関数

  • mkdir($path, $mode): ディレクトリを作成する(Make Directory)。
  • rmdir($path): ディレクトリを削除する(Remove Directory)。
    ※注意:中にファイルが残っていると削除できません。
  • scandir($path): ディレクトリ内のファイル一覧を取得する。

実践コード:年別の保存フォルダを作る

「2025」のようなフォルダがなければ作成し、その中身一覧を表示する例です。

<?php
$dir = '2025_data';

// 1. フォルダがなければ作成(権限は0777など環境に合わせて指定)
if (!file_exists($dir)) {
    mkdir($dir, 0777); 
    echo "フォルダを作成しました。<br>";
}

// 2. フォルダ内の一覧を取得
$files = scandir($dir);

// scandirは「.」と「..」も取得するため、それらを除外して表示
foreach ($files as $file) {
    if ($file !== '.' && $file !== '..') {
        echo "ファイル名: " . $file . "<br>";
    }
}
?>

7. CSVやテキストファイルを扱う具体的な実践例

ここまでの知識を組み合わせて、実務でよくあるシーンでのコード例を紹介します。

例1:CSVファイルを読み込んで表にする

Excelなどで作成した会員リスト(CSV)を読み込み、Web画面に表示するプログラムです。

<?php
$csv_file = 'member_list.csv';

if (($handle = fopen($csv_file, "r")) !== FALSE) {
    echo "<table border='1'>";
    
    // fgetcsv でCSVの1行を配列として取得
    while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) {
        echo "<tr>";
        // 配列の中身を列として表示
        foreach ($data as $cell) {
            echo "<td>" . htmlspecialchars($cell) . "</td>";
        }
        echo "</tr>";
    }
    echo "</table>";
    fclose($handle);
}
?>

例2:簡易アクセスログの作成

ページが見られるたびに、日時とIPアドレスを記録します。

<?php
$log_file = 'access_log.txt';

// 日時とIPアドレスを取得
$log_data = date("Y-m-d H:i:s") . " - " . $_SERVER['REMOTE_ADDR'] . "\n";

// 追記モードで保存
file_put_contents($log_file, $log_data, FILE_APPEND | LOCK_EX);
?>

LOCK_EX を付けると、書き込み中に他の人が書き込めないようにロック(排他制御)をかけられるため、データが壊れるのを防げます。

8. ファイル操作時の注意点とエラー対処法

最後に、ファイル操作で初心者がつまずきやすいポイントをまとめます。

1. パーミッション(権限)のエラー

最も多いのが Permission denied(アクセス拒否)エラーです。
PHP(Webサーバー)が、指定したディレクトリやファイルに対して「書き込み権限」を持っていない場合に発生します。

対処法: FTPソフトやSSHコマンド(chmod)を使って、保存先フォルダの権限を「755」や「777」などに変更して、書き込みを許可する必要があります。

2. パスの指定ミス

ファイルが見つからない(No such file or directory)場合、パス(ファイルの場所)の指定が間違っています。

  • 相対パス: ../images/photo.jpg (現在の場所から見た位置)
  • 絶対パス: /var/www/html/images/photo.jpg (サーバーのルートからの位置)

PHPでは、__DIR__ (現在のファイルのディレクトリ)を使って絶対パスで指定するとミスが減ります。
例:include __DIR__ . '/config.php';

3. 同時アクセスの競合

Webサイトは複数の人が同時にアクセスします。同じファイルに同時に書き込もうとすると、データが消えたり壊れたりします。

対処法: file_put_contentsLOCK_EX フラグや、flock() 関数を使って、書き込み中は他の処理を待たせる「排他制御」を行いましょう。

ファイル操作は、Webアプリケーションの幅を大きく広げる重要な技術です。まずは簡単なテキストファイルの読み書きから試して、徐々に画像投稿やCSV連携などに挑戦してみてください。