Webエンジニア向けプログラミング解説動画をYouTubeで配信中!
▶ チャンネル登録はこちら

【PHP8.x】move_uploaded_file()関数の使い方

move_uploaded_file関数の使い方について、初心者にもわかりやすく解説します。

作成日: 更新日:

基本的な使い方

move_uploaded_file関数は、HTTP POSTによるファイルアップロードでアップロードされたファイルを、指定された新しい場所に移動する関数です。これは、アップロードされたファイルを安全に処理し、ウェブサーバー上の適切な場所に保存するために不可欠な関数です。第一引数には、アップロードされたファイルの一時的なファイル名(通常は$_FILES配列の'tmp_name'キーで指定されます)を指定します。第二引数には、ファイルを移動させたい新しいパスとファイル名を指定します。

この関数は、アップロードされたファイルが実際にHTTP POSTによってアップロードされたものであることを確認し、セキュリティ上のリスクを軽減します。move_uploaded_file関数を使用する前に、ファイルサイズやファイルの種類などのバリデーションを行うことが推奨されます。

移動が成功した場合、この関数はtrueを返します。失敗した場合、falseを返します。失敗の原因としては、指定された移動先のディレクトリが存在しない、書き込み権限がない、ファイルサイズが大きすぎるなどが考えられます。関数の実行結果を適切に確認し、エラーが発生した場合は適切なエラー処理を行うことが重要です。

move_uploaded_file関数は、アップロードされたファイルを確実に移動させることを保証するものではありません。移動が成功したかどうかを確認するために、返り値を必ず確認してください。また、ファイル名にユーザーが入力した値を使用する場合は、セキュリティ上の問題を防ぐために、ファイル名を適切にサニタイズする必要があります。これにより、悪意のあるスクリプトの実行を防ぐことができます。

構文(syntax)

1move_uploaded_file ( string $filename , string $destination ) : bool

引数(parameters)

string $from, string $to

  • string $from: アップロードされたファイルの元のファイルパス
  • string $to: ファイルを移動する先の新しいファイルパス

戻り値(return)

bool

指定されたファイルからアップロードされたファイルを、指定された宛先に移動できたかどうかを真偽値(trueまたはfalse)で返します。移動に成功した場合はtrue、失敗した場合はfalseを返します。

サンプルコード

PHP ファイルアップロード: move_uploaded_file サーバーエラー対処法

1<?php
2/**
3 * PHPファイルアップロード処理サンプル
4 * システムエンジニアを目指す初心者向けに、move_uploaded_file の使い方と
5 * 「サーバーで動作しない」場合の一般的な原因を考慮したコードです。
6 */
7
8// 開発環境ではエラー表示を有効にすることをおすすめします。
9// 本番環境ではセキュリティのため無効にするか、ログに記録するように設定してください。
10ini_set('display_errors', 1);
11ini_set('display_startup_errors', 1);
12error_reporting(E_ALL);
13
14$message = ''; // ユーザーに表示するメッセージを格納する変数
15
16// HTTPリクエストがPOSTメソッドで、かつファイルがアップロードされた場合のみ処理を実行
17if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['uploadedFile'])) {
18    $file = $_FILES['uploadedFile'];
19
20    // 1. アップロードエラーのチェック
21    // $_FILES['uploadedFile']['error'] にはエラーコードが格納されます。
22    if ($file['error'] !== UPLOAD_ERR_OK) {
23        switch ($file['error']) {
24            case UPLOAD_ERR_INI_SIZE:
25            case UPLOAD_ERR_FORM_SIZE:
26                $message = 'エラー: アップロードされたファイルが大きすぎます。php.ini の設定を確認してください。';
27                break;
28            case UPLOAD_ERR_PARTIAL:
29                $message = 'エラー: ファイルが部分的にしかアップロードされませんでした。';
30                break;
31            case UPLOAD_ERR_NO_FILE:
32                $message = 'エラー: ファイルが選択されていません。';
33                break;
34            case UPLOAD_ERR_NO_TMP_DIR:
35                $message = 'エラー: 一時フォルダが見つかりません。サーバー設定を確認してください。';
36                break;
37            case UPLOAD_ERR_CANT_WRITE:
38                $message = 'エラー: ディスクへの書き込みに失敗しました。サーバーのパーミッションを確認してください。';
39                break;
40            case UPLOAD_ERR_EXTENSION:
41                $message = 'エラー: PHPの拡張機能によってファイルアップロードが停止されました。';
42                break;
43            default:
44                $message = '不明なアップロードエラーが発生しました。';
45                break;
46        }
47    } else {
48        // 2. アップロードされたファイルがHTTP POST経由でアップロードされたものであることを確認
49        // is_uploaded_file() はセキュリティ上非常に重要です。
50        // これにより、悪意のあるユーザーがシステム上の一時ファイルではないファイルの名前を指定して
51        // move_uploaded_file を実行するのを防ぎます。
52        if (is_uploaded_file($file['tmp_name'])) {
53            $uploadDir = 'uploads/'; // ファイルの保存先ディレクトリ
54
55            // 3. 保存先ディレクトリが存在しない場合は作成する
56            // mkdir() の第3引数 `true` で、ネストされたディレクトリも再帰的に作成します。
57            // 0755 は一般的なディレクトリのパーミッション設定です。
58            // Webサーバーのユーザーがこのディレクトリに書き込める必要があります。
59            // 失敗する場合は、手動でディレクトリを作成し、パーミッションを設定してください(例: `chmod 755 uploads/`)。
60            if (!is_dir($uploadDir)) {
61                if (!mkdir($uploadDir, 0755, true)) {
62                    $message = 'エラー: アップロードディレクトリ "' . htmlspecialchars($uploadDir) . '" を作成できませんでした。サーバーのパーミッションを確認してください。';
63                }
64            }
65
66            // ディレクトリが正常に存在または作成された場合のみ処理を続行
67            if (empty($message)) {
68                // 4. ファイル名を安全に処理する
69                // basename() を使用して、ファイル名からパス成分を取り除きます。
70                // これにより、ディレクトリトラバーサル攻撃(例: ../../../evil.php)を防ぎます。
71                $targetFileName = basename($file['name']);
72                $targetPath = $uploadDir . $targetFileName;
73
74                // 5. アップロードされた一時ファイルを最終的な保存先に移動
75                // move_uploaded_file($from, $to) は、一時ファイル ($from) を指定された場所 ($to) へ移動します。
76                // この関数が失敗する主な原因は以下の通りです:
77                //   - $file['tmp_name'] が実際の一時ファイルではない(is_uploaded_file() でチェック済み)。
78                //   - $targetPath のディレクトリにWebサーバーが書き込み権限を持っていない(パーミッションの問題)。
79                //   - ターゲットパスが既に存在し、何らかの理由で上書きできない(稀なケース)。
80                if (move_uploaded_file($file['tmp_name'], $targetPath)) {
81                    $message = 'ファイルを ' . htmlspecialchars($targetPath) . ' にアップロードしました。';
82                } else {
83                    $message = 'エラー: ファイルの移動に失敗しました。保存先ディレクトリ(' . htmlspecialchars($uploadDir) . ')のパーミッション、またはファイルパスを確認してください。';
84                }
85            }
86        } else {
87            $message = 'エラー: アップロードされたファイルが不正なものでした。';
88        }
89    }
90}
91?>
92<!DOCTYPE html>
93<html lang="ja">
94<head>
95    <meta charset="UTF-8">
96    <meta name="viewport" content="width=device-width, initial-scale=1.0">
97    <title>PHP ファイルアップロード</title>
98    <style>
99        body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; margin: 20px; background-color: #f4f7f6; color: #333; }
100        h1 { color: #2c3e50; }
101        .message { margin-top: 20px; padding: 12px; border-radius: 6px; font-weight: bold; }
102        .success { background-color: #d4edda; color: #155724; border: 1px solid #c3e6cb; }
103        .error { background-color: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }
104        form { margin-top: 25px; padding: 25px; border: 1px solid #e0e0e0; border-radius: 8px; max-width: 500px; background-color: #ffffff; box-shadow: 0 4px 8px rgba(0,0,0,0.05); }
105        label { display: block; margin-bottom: 10px; font-weight: bold; color: #555; }
106        input[type="file"] { margin-bottom: 20px; padding: 8px; border: 1px solid #ccc; border-radius: 4px; width: calc(100% - 16px); box-sizing: border-box; }
107        input[type="submit"] { padding: 10px 25px; background-color: #007bff; color: white; border: none; border-radius: 5px; cursor: pointer; font-size: 16px; transition: background-color 0.3s ease; }
108        input[type="submit"]:hover { background-color: #0056b3; }
109        .tip-section { margin-top: 30px; padding: 20px; border: 1px solid #e0e0e0; border-radius: 8px; background-color: #ffffff; max-width: 500px; box-shadow: 0 4px 8px rgba(0,0,0,0.05); }
110        .tip-section h2 { color: #2c3e50; font-size: 1.2em; margin-top: 0; }
111        .tip-section ul { list-style-type: disc; margin-left: 20px; }
112        .tip-section li { margin-bottom: 8px; line-height: 1.5; }
113        .tip-section code { background-color: #e9ecef; padding: 2px 4px; border-radius: 3px; font-family: Consolas, monospace; }
114    </style>
115</head>
116<body>
117    <h1>ファイルをアップロードする</h1>
118
119    <?php if (!empty($message)): ?>
120        <div class="message <?php echo strpos($message, 'エラー') !== false ? 'error' : 'success'; ?>">
121            <?php echo htmlspecialchars($message); ?>
122        </div>
123    <?php endif; ?>
124
125    <form action="" method="post" enctype="multipart/form-data">
126        <label for="uploadedFile">アップロードするファイルを選択してください:</label>
127        <input type="file" name="uploadedFile" id="uploadedFile">
128        <input type="submit" value="アップロード">
129    </form>
130
131    <div class="tip-section">
132        <h2>「not working on server」で困った際の確認ポイント</h2>
133        <ul>
134            <li><strong>パーミッション不足:</strong> PHPがファイルを保存するディレクトリ(このコードでは <code>uploads/</code>)に書き込み権限がありません。FTPソフトやSSHでディレクトリのパーミッションを適切に設定(例: <code>chmod 755 uploads/</code>)してください。<code>777</code> は一時的なテスト以外ではセキュリティリスクが高いため非推奨です。</li>
135            <li><strong>PHP設定の制限:</strong> <code>php.ini</code><code>upload_max_filesize</code><code>post_max_size</code> の設定値が、アップロードしようとしているファイルサイズよりも小さい可能性があります。</li>
136            <li><strong>一時ディレクトリの問題:</strong> <code>php.ini</code><code>upload_tmp_dir</code> で指定された一時ディレクトリが存在しないか、PHPがそのディレクトリにファイルを書き込めない可能性があります。</li>
137            <li><strong>ファイルパスの誤り:</strong> <code>move_uploaded_file</code> の第2引数に指定するターゲットパスが、意図しない場所を指しているか、存在しないパスである可能性があります。<code>basename()</code> を使うことでファイル名の安全性を高めています。</li>
138            <li><strong>エラー表示:</strong> 開発中は <code>display_errors = On</code><code>error_reporting = E_ALL</code> を設定し、発生しているエラーメッセージを詳細に確認することが問題解決の第一歩です。</li>
139        </ul>
140        これらの点を確認することで、問題が解決する可能性が高いです。
141    </div>
142</body>
143</html>

PHPのmove_uploaded_file関数は、ウェブブラウザからHTTP POSTメソッドでアップロードされたファイルを、サーバー上の一時保存場所から指定された最終的な保存場所へ安全に移動させるために使用されます。この関数は、第一引数string $fromにアップロードされた一時ファイルのパス(通常は$_FILES配列のtmp_name要素)、第二引数string $toにファイルを移動させたい最終的な保存先のパスを指定します。移動処理が成功した場合はtrueを、失敗した場合はfalseをブール値として返します。

システムエンジニアを目指す初心者の方が「サーバーで動作しない」という問題に遭遇する主な原因はいくつかあります。最も重要なのはセキュリティ面で、move_uploaded_fileを実行する前に必ずis_uploaded_file()関数を使って、指定された$fromパスが本当にHTTP POSTでアップロードされたファイルの一時パスであるか検証する必要があります。これにより、悪意のあるユーザーがシステム内の任意のファイルを移動させることを防ぎます。

次に一般的なのは、ファイルの保存先ディレクトリのパーミッション不足です。ウェブサーバーが指定された$toパスのディレクトリにファイルを書き込む権限を持っていない場合、関数は失敗します。サンプルコードではuploads/ディレクトリのパーミッションが0755であることを前提としていますが、環境によっては手動で設定が必要です。また、basename()関数を用いてファイル名からパス情報を除去し、セキュリティを確保しながら安全な保存パスを構築することも重要です。ファイルサイズ超過や一時ディレクトリの問題など、アップロード自体のエラーも$_FILES配列のエラーコードで確認できます。開発時はエラー表示を有効にすることで、問題の特定と解決に役立ちます。

PHPのmove_uploaded_file関数を使用する際は、セキュリティのため、アップロードされたファイルが正規のものかis_uploaded_file()で必ず確認してください。ファイルを保存するディレクトリには、Webサーバーが書き込み可能なパーミッション(例えばchmod 755)が設定されていることを確認することが重要です。もし動作しない場合、php.iniupload_max_filesizepost_max_sizeが、アップロードしようとしているファイルサイズに対して適切か確認してください。ファイル名を扱う際には、パス成分を除去するbasename()を使用することで、セキュリティを高めることができます。開発中はエラー表示を有効にし、詳細なエラーメッセージを確認することで、問題の原因特定が容易になります。

PHP move_uploaded_file でファイルを安全に移動・上書きする

1<?php
2
3/**
4 * アップロードされたファイルを指定されたディレクトリへ安全に移動するサンプル関数。
5 *
6 * この関数は、ファイルのアップロード処理の一部をシミュレートします。
7 * 実際には、Webサーバー経由でアップロードされたファイルの一時パスを使用します。
8 *
9 * @param array $fileInfo 通常は `$_FILES['input_name']` の形式で提供されるファイル情報。
10 *                        ['tmp_name' => 一時ファイルパス, 'name' => 元のファイル名] を含むことを想定。
11 * @param string $destinationDirectory ファイルを移動する最終的なディレクトリのパス。
12 * @return string 処理結果のメッセージ。
13 */
14function handleFileUploadAndMove(array $fileInfo, string $destinationDirectory): string
15{
16    // アップロードされたファイルの一時的なパス。
17    // 実際には `$_FILES['input_name']['tmp_name']` から取得されます。
18    $temporaryFilePath = $fileInfo['tmp_name'];
19
20    // 元のファイル名。basename() を使用して、パス情報を除去し、
21    // ディレクトリトラバーサル攻撃(例: ../../../evil.php)を防ぎます。
22    // 実際には `$_FILES['input_name']['name']` から取得されます。
23    $originalFileName = basename($fileInfo['name']);
24
25    // 移動先の完全なファイルパスを構築します。
26    $destinationFilePath = rtrim($destinationDirectory, '/') . '/' . $originalFileName;
27
28    // 移動先ディレクトリが存在しない場合は作成を試みます。
29    if (!is_dir($destinationDirectory)) {
30        // 0755 は一般的なディレクトリ権限です。true は再帰的にディレクトリを作成します。
31        if (!mkdir($destinationDirectory, 0755, true)) {
32            return "エラー: 移動先ディレクトリ '$destinationDirectory' の作成に失敗しました。パーミッションを確認してください。";
33        }
34    }
35
36    // move_uploaded_file() を使用してファイルを一時パスから最終的な保存場所へ移動します。
37    //
38    // キーワード「上書き」について:
39    // もし指定された $destinationFilePath に同名のファイルが既に存在する場合、
40    // move_uploaded_file() は既存のファイルを新しいファイルで上書きします。
41    //
42    // セキュリティ上の注意点:
43    // move_uploaded_file() は、渡されたファイルがHTTP POSTアップロードによって
44    // アップロードされた正規の一時ファイルであるかを内部で厳格にチェックします(`is_uploaded_file()`と同様)。
45    // このチェックにより、悪意のあるユーザーがシステム上の任意のファイルを移動することを防ぎます。
46    //
47    // このサンプルコードはPHPスクリプト単体で実行されるため、HTTP POSTアップロード環境ではありません。
48    // したがって、`$temporaryFilePath` に指定されたパスは有効なアップロード済み一時ファイルではないため、
49    // move_uploaded_file() は通常、ここで失敗します。
50    // 実際のWebアプリケーションでは、`$_FILES`経由で提供される有効な一時ファイルパスでこの関数は成功します。
51    if (move_uploaded_file($temporaryFilePath, $destinationFilePath)) {
52        return "ファイル '$originalFileName' が '$destinationFilePath' に正常に移動されました。";
53    } else {
54        return "エラー: ファイル '$originalFileName' の移動に失敗しました。詳細: この環境では一時ファイルがHTTPアップロードによって提供されていない可能性があります。";
55    }
56}
57
58// --- 使用例 ---
59// 実際のWebアプリケーションでは、HTMLフォームからファイルをアップロードした際に、
60// PHPが自動で生成する一時ファイルパスとファイル名が `$_FILES['フォームのname属性']` に格納されます。
61// この例では、`move_uploaded_file` 関数の引数構造を示すためにダミーのファイル情報を使用しています。
62$dummyUploadedFileInfo = [
63    'tmp_name' => '/tmp/php_dummy_temp_file_'.uniqid(), // 実際にはPHPが自動生成する一時ファイルパス
64    'name' => 'report_2023.pdf', // 元のファイル名
65    'type' => 'application/pdf',
66    'size' => 512000,
67    'error' => UPLOAD_ERR_OK, // アップロードエラーがないことを示す(実際のエラーは UPLOAD_ERR_XXX 定数)
68];
69
70// ファイルを保存する最終的なディレクトリのパス。
71// __DIR__ は現在のスクリプトが置かれているディレクトリを指します。
72$uploadDirectory = __DIR__ . '/uploaded_files';
73
74// アップロード処理を実行し、結果を表示
75echo handleFileUploadAndMove($dummyUploadedFileInfo, $uploadDirectory);
76
77?>

PHPのmove_uploaded_file関数は、WebサーバーにHTTP POSTリクエストでアップロードされたファイルを、一時的な場所から指定した最終保存先へ安全に移動するために使用されます。

この関数は二つの文字列型引数を取ります。一つ目の$from引数にはアップロードされたファイルの一時的なパスを、二つ目の$to引数にはファイルを移動させたい最終的な保存先のパスを指定します。処理が成功するとtrueを、失敗するとfalseを返します。

move_uploaded_file関数は、ファイルが本当にHTTP POSTアップロードによってアップロードされたものであるかを厳しくチェックします。これにより、悪意のあるユーザーがシステム上の他のファイルを移動させるのを防ぐ、重要なセキュリティ機能が組み込まれています。また、もし移動先のパスに同名のファイルが既に存在する場合、その既存のファイルは新しいファイルで上書きされます。

提示されたサンプルコードでは、$_FILESスーパーグローバル変数で通常提供されるファイル情報(一時パスや元のファイル名)を模倣し、アップロードされたファイルを安全に移動する一連の流れを示しています。特にbasename()関数を使って元のファイル名からパス情報を除去し、セキュリティを考慮しています。また、移動先ディレクトリが存在しない場合には自動的に作成する処理も含まれています。ただし、このサンプルコードはHTTPアップロード環境ではないため、move_uploaded_file関数は通常、指定された一時ファイルパスが有効なアップロード済み一時ファイルではないと判断し、失敗する点にご注意ください。実際のWebアプリケーションでは、HTMLフォームからのファイルアップロード時にPHPが生成する一時ファイルパスを$fromに指定することで、この関数が正常に機能します。

move_uploaded_file関数は、HTTP POSTでアップロードされた正規の一時ファイルのみを安全に移動するための関数です。サンプルコードはダミー情報を使っていますが、実際のWebアプリケーションでは$_FILESから得られる一時ファイルパスを指定する必要があります。この関数は、移動先に同名のファイルが既に存在する場合、既存のファイルを新しいファイルで上書きしますので、意図しないデータ消失を防ぐために事前のファイル名チェックやリネーム処理を検討してください。また、アップロードされたファイルの元のファイル名を安全に扱うため、basename()関数を使用してパス情報を除去し、ディレクトリトラバーサル攻撃などのセキュリティリスクを防ぐことが非常に重要です。移動先のディレクトリが存在しない場合はmkdir()で作成し、適切なパーミッション(例: 0755)を設定することも忘れないでください。関数が失敗した場合はfalseを返すため、必ず戻り値をチェックし、エラーに応じた適切な処理を行うことが安全なコードにつながります。

PHP move_uploaded_file 失敗時の処理

1<?php
2
3/**
4 * ファイルアップロードを処理し、move_uploaded_file の失敗ケースをハンドリングする関数
5 *
6 * このスクリプトは単体で動作します。
7 * 1. HTMLフォームでファイルを選択し「アップロード」ボタンを押してください。
8 * 2. サーバーサイドでファイルを受け取り、指定ディレクトリに保存します。
9 * 3. move_uploaded_file が成功したか失敗したかに応じてメッセージを表示します。
10 */
11function handleFileUpload(): void
12{
13    // アップロードされたファイルを保存するディレクトリ
14    $uploadDirectory = 'uploads/';
15    $message = '';
16
17    // 保存先ディレクトリが存在しない場合は作成を試みる
18    if (!is_dir($uploadDirectory)) {
19        // mkdirの第三引数を true にすることで、再帰的にディレクトリを作成
20        if (!mkdir($uploadDirectory, 0755, true)) {
21            // ディレクトリ作成に失敗した場合、処理を中断
22            $message = 'エラー: アップロードディレクトリの作成に失敗しました。';
23        }
24    }
25
26    // フォームがPOSTメソッドで送信された場合のみ処理を実行
27    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
28        // ファイルが選択され、アップロードエラーがないことを確認
29        if (isset($_FILES['user_file']) && $_FILES['user_file']['error'] === UPLOAD_ERR_OK) {
30            $tmpFilePath = $_FILES['user_file']['tmp_name'];
31
32            // 安全のため、ファイル名からディレクトリ情報を除去
33            $fileName = basename($_FILES['user_file']['name']);
34            $destinationPath = $uploadDirectory . $fileName;
35
36            // move_uploaded_file() でファイルを一時ディレクトリから指定の場所へ移動
37            // この関数は、成功時に true、失敗時に false を返す
38            if (move_uploaded_file($tmpFilePath, $destinationPath)) {
39                $message = "ファイル '{$fileName}' のアップロードに成功しました。";
40            } else {
41                // ここが move_uploaded_file() が失敗した場合の処理
42                $message = "エラー: ファイルの移動に失敗しました。";
43                $message .= "考えられる原因として、サーバー上の '{$uploadDirectory}' ディレクトリに対する書き込み権限がない可能性があります。";
44            }
45        } else {
46            // ファイルが選択されていない、またはアップロード中にエラーが発生した場合
47            $message = 'エラー: ファイルが選択されていないか、アップロード中にエラーが発生しました。';
48            if (isset($_FILES['user_file']['error'])) {
49                 $message .= ' (エラーコード: ' . $_FILES['user_file']['error'] . ')';
50            }
51        }
52    }
53    // HTML部分と処理結果のメッセージを表示
54    displayForm($message);
55}
56
57/**
58 * HTMLフォームとメッセージを表示する関数
59 * @param string $message 表示するメッセージ
60 */
61function displayForm(string $message): void
62{
63    echo '<!DOCTYPE html>';
64    echo '<html lang="ja">';
65    echo '<head>';
66    echo '    <meta charset="UTF-8">';
67    echo '    <title>move_uploaded_file 失敗サンプル</title>';
68    echo '</head>';
69    echo '<body>';
70    echo '    <h1>ファイルアップロード</h1>';
71
72    if ($message) {
73        // XSS対策として、メッセージをエスケープして表示
74        echo '    <p><strong>' . htmlspecialchars($message, ENT_QUOTES, 'UTF-8') . '</strong></p>';
75    }
76
77    // ファイルアップロード用のフォーム
78    // enctype="multipart/form-data" はファイル送信に必須
79    echo '    <form action="" method="post" enctype="multipart/form-data">';
80    echo '        <p>';
81    echo '            <label for="file_input">ファイルを選択:</label>';
82    echo '            <input type="file" name="user_file" id="file_input">';
83    echo '        </p>';
84    echo '        <p>';
85    echo '            <input type="submit" value="アップロード">';
86    echo '        </p>';
87    echo '    </form>';
88    echo '</body>';
89    echo '</html>';
90}
91
92// メイン処理の実行
93handleFileUpload();
94

PHPのmove_uploaded_file関数は、ユーザーがウェブフォームを通じてアップロードしたファイルを、一時的な保存場所からサーバー上の指定したディレクトリへ安全に移動させるための重要な関数です。この関数は、アップロードされたファイルが本当にアップロード処理を経てサーバーに到着したものであることを保証し、セキュリティ上のリスクを軽減します。

引数$fromには、アップロードされた一時ファイルが現在置かれているパス(通常は$_FILES['入力フィールド名']['tmp_name']で取得)を指定します。一方、引数$toには、ファイルを最終的に保存したいサーバー上のディレクトリとファイル名を指定します。

関数が正常にファイルの移動を完了した場合、戻り値としてtrueが返されます。しかし、ファイルの移動に失敗した場合はfalseが返されます。サンプルコードでは、このfalseが返されたケースを「失敗」としてハンドリングし、ユーザーにエラーメッセージを表示しています。失敗の一般的な原因としては、指定された保存先ディレクトリが存在しない、またはそのディレクトリに対するウェブサーバーの書き込み権限がないことなどが挙げられます。ファイルをアップロードする際は、保存先ディレクトリの作成と適切な権限設定が不可欠です。

move_uploaded_file関数が失敗する最も一般的な原因は、移動先のディレクトリに対する書き込み権限がないことです。Webサーバーを実行しているユーザー(例: www-data)に、指定したディレクトリへの書き込みを許可する必要があります。この関数は、PHPによってアップロードされた正規のファイルであることも検証するため、rename関数などを使うより安全です。また、サンプルコードのようにbasename関数でファイル名からパス情報を除去するのは、意図しない場所にファイルを保存される攻撃を防ぐための重要なセキュリティ対策です。アップロード自体が失敗している可能性もあるため、$_FILES['error']の値を確認することも問題解決の助けになります。

PHP move_uploaded_file で安全にファイルを移動する

1<?php
2
3/**
4 * アップロードされたファイルを指定されたディレクトリに安全に移動します。
5 *
6 * この関数は、HTTP POST リクエストを通じてアップロードされた一時ファイルを
7 * 永続的な保存場所へ移動するプロセスを処理します。
8 * ファイルのセキュリティと権限に関する一般的な問題にも対応しています。
9 *
10 * @param array $file_info $_FILES スーパーグローバル変数から取得した、アップロードされたファイル情報。
11 *                          例: $_FILES['uploaded_file'](HTMLフォームのname属性と一致)
12 * @param string $destination_dir ファイルの移動先となるディレクトリの絶対パス。
13 *                                 このディレクトリは、Webサーバーの実行ユーザーが書き込み可能である必要があります。
14 * @return string ファイル移動の成否を示すメッセージ。成功時には新しいファイル名を含みます。
15 */
16function handleUploadedFile(array $file_info, string $destination_dir): string
17{
18    // 1. アップロード初期エラーのチェック
19    // ファイルが選択されていない、またはアップロードに失敗した場合の初期チェックを行います。
20    if (!isset($file_info['tmp_name']) || $file_info['error'] === UPLOAD_ERR_NO_FILE) {
21        return 'エラー: ファイルが選択されていないか、アップロードされていません。';
22    }
23
24    // PHPが報告するアップロードエラーコードに基づいた詳細なチェック
25    if ($file_info['error'] !== UPLOAD_ERR_OK) {
26        switch ($file_info['error']) {
27            case UPLOAD_ERR_INI_SIZE:
28            case UPLOAD_ERR_FORM_SIZE:
29                return 'エラー: ファイルサイズが大きすぎます。PHPの設定 (upload_max_filesize, post_max_size) またはHTMLフォームの制限を確認してください。';
30            case UPLOAD_ERR_PARTIAL:
31                return 'エラー: ファイルの一部しかアップロードされませんでした。';
32            case UPLOAD_ERR_NO_TMP_DIR:
33                return 'エラー: 一時フォルダがありません。サーバー管理者にお問い合わせください。';
34            case UPLOAD_ERR_CANT_WRITE:
35                return 'エラー: ディスクへの書き込みに失敗しました。サーバー管理者にお問い合わせください。';
36            case UPLOAD_ERR_EXTENSION:
37                return 'エラー: PHP拡張機能がファイルのアップロードを停止しました。';
38            default:
39                return 'エラー: 不明なファイルアップロードエラーが発生しました。コード: ' . $file_info['error'];
40        }
41    }
42
43    // 2. 宛先ディレクトリの存在と書き込み権限の確認
44    // ファイルを移動する前に、宛先ディレクトリが存在し、Webサーバーの実行ユーザーが
45    // そのディレクトリにファイルを書き込む権限を持っているかを確認することが重要です。
46    if (!is_dir($destination_dir)) {
47        // ディレクトリが存在しない場合、作成を試みます。
48        // 0755 は一般的なディレクトリ権限(所有者:読み書き実行, グループ・その他:読み取り実行)ですが、
49        // 実際にはWebサーバーの実行ユーザーが書き込み可能である必要があります。
50        if (!mkdir($destination_dir, 0755, true)) {
51            return "エラー: アップロード先ディレクトリ '{$destination_dir}' の作成に失敗しました。手動で作成し、適切な権限を設定してください。";
52        }
53    }
54
55    if (!is_writable($destination_dir)) {
56        // 権限の問題は move_uploaded_file が失敗する最も一般的な原因の一つです。
57        // 例: Webサーバーの実行ユーザー (例: www-data) に書き込み権限がない場合。
58        return "エラー: アップロード先ディレクトリ '{$destination_dir}' に書き込み権限がありません。ディレクトリのパーミッション (例: chmod 775 /path/to/uploads) を確認してください。";
59    }
60
61    // 3. セキュリティ対策: ファイル名の生成
62    // アップロードされたファイル名をそのまま使用するとセキュリティリスクがあるため、
63    // ユニークで安全なファイル名を生成することを強く推奨します。
64    // ここでは、ユニークIDと元のファイル拡張子を組み合わせています。
65    $file_extension = pathinfo($file_info['name'], PATHINFO_EXTENSION);
66    $unique_filename = uniqid('uploaded_', true) . '.' . $file_extension; // 例: uploaded_60c7b4f2a3d0f5.12345.jpg
67    $destination_path = rtrim($destination_dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $unique_filename;
68
69    // 4. move_uploaded_file でファイルを移動
70    // move_uploaded_file は、アップロードされたファイルがHTTP POSTアップロードによって
71    // アップロードされたものであることを保証する重要なセキュリティチェックを行います。
72    // これにより、悪意のあるユーザーがシステム上の任意のファイルを移動するのを防ぎます。
73    if (move_uploaded_file($file_info['tmp_name'], $destination_path)) {
74        return '成功: ファイルが正常にアップロードされ、' . htmlspecialchars($unique_filename) . ' として保存されました。';
75    } else {
76        // move_uploaded_file が false を返した場合、権限問題(is_writableで事前にチェック済)、
77        // ディスク容量不足、ファイル破損など、他の原因が考えられます。
78        return 'エラー: ファイルの移動に失敗しました。サーバーのログを確認するか、ディスク容量や設定をチェックしてください。';
79    }
80}
81
82// このPHPスクリプトが直接実行された場合の処理。
83// HTTP POST リクエストを検出した場合にファイル処理を実行し、それ以外の場合はフォームを表示します。
84if ($_SERVER['REQUEST_METHOD'] === 'POST') {
85    // アップロードファイルを保存するディレクトリ。
86    // このスクリプトと同じディレクトリに 'uploads' という名前で作成されます。
87    $upload_directory = __DIR__ . DIRECTORY_SEPARATOR . 'uploads';
88
89    // handleUploadedFile 関数を呼び出し、結果メッセージを出力します。
90    // $_FILES['uploaded_file'] はHTMLフォームの <input type="file" name="uploaded_file"> に対応します。
91    $result_message = handleUploadedFile($_FILES['uploaded_file'] ?? [], $upload_directory);
92
93    // アップロード結果をユーザーに表示するHTMLを生成
94    echo '<!DOCTYPE html><html lang="ja"><head><meta charset="UTF-8"><title>アップロード結果</title><style>body { font-family: sans-serif; margin: 2em; line-height: 1.6; background-color: #f4f4f4; color: #333; text-align: center;} h1 { color: #007bff; } p { background-color: #fff; padding: 1em; border-radius: 5px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); display: inline-block; } a { color: #007bff; text-decoration: none; } a:hover { text-decoration: underline; }</style></head><body>';
95    echo '<h1>ファイルのアップロード結果</h1>';
96    echo '<p>' . $result_message . '</p>';
97    echo '<p><a href="/">アップロードフォームに戻る</a></p>'; // トップページへのリンク
98    echo '</body></html>';
99} else {
100    // GET リクエストの場合、ファイルアップロード用のHTMLフォームを表示します。
101    echo '
102<!DOCTYPE html>
103<html lang="ja">
104<head>
105    <meta charset="UTF-8">
106    <title>ファイルアップロード</title>
107    <style>
108        body { font-family: sans-serif; margin: 2em; line-height: 1.6; background-color: #f4f4f4; color: #333; }
109        form { background: #fff; padding: 1.5em; border-radius: 8px; border: 1px solid #ddd; max-width: 500px; margin: 2em auto; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
110        label { display: block; margin-bottom: 0.5em; font-weight: bold; color: #555; }
111        input[type="file"] { margin-bottom: 1em; padding: 0.5em; border: 1px solid #ccc; border-radius: 4px; width: calc(100% - 1em); box-sizing: border-box; }
112        input[type="submit"] { background-color: #007bff; color: white; padding: 0.7em 1.5em; border: none; border-radius: 4px; cursor: pointer; font-size: 1em; width: 100%; box-sizing: border-box; }
113        input[type="submit"]:hover { background-color: #0056b3; }
114        h1 { text-align: center; color: #007bff; margin-bottom: 1.5em; }
115    </style>
116</head>
117<body>
118    <h1>ファイルをアップロード</h1>
119    <form action="" method="post" enctype="multipart/form-data">
120        <label for="uploaded_file">アップロードするファイルを選択してください:</label>
121        <input type="file" name="uploaded_file" id="uploaded_file">
122        <input type="submit" value="ファイルをアップロード">
123    </form>
124</body>
125</html>';
126}
127

PHPのmove_uploaded_file関数は、HTTP POSTリクエストを通じてサーバーにアップロードされた一時ファイルを、指定された永続的な保存場所へ安全に移動するために使用されます。

第一引数$fromには、$_FILESスーパーグローバル変数から取得する一時ファイルパスを指定します。第二引数$toには、ファイルを保存したい最終的な絶対パス(ディレクトリと新しいファイル名)を指定します。戻り値はbool型で、移動が成功すればtrue、失敗すればfalseを返します。

この関数を使う上で最も注意すべき点は、移動先ディレクトリにWebサーバーの書き込み権限があるかです。権限がないとファイルの移動は失敗するため、サンプルコードのようにis_writable関数で事前に確認し、必要に応じてmkdir関数でディレクトリを作成する必要があります。

また、セキュリティ対策として、悪意のあるファイル名による攻撃を防ぐため、アップロードされたファイル名をそのまま使用せず、uniqidなどでユニークかつ安全なファイル名を生成することを強く推奨します。move_uploaded_file関数自体も、移動対象がHTTPアップロードによって生成された一時ファイルであるかを確認する重要なセキュリティ機能も備わっています。これらの権限管理とセキュリティ対策を適切に行うことで、安全で堅牢なファイルアップロード機能を実現できます。

move_uploaded_file関数は、アップロードされた一時ファイルを安全に移動するためのPHPの重要な機能です。この関数を利用する際は、まず移動先のディレクトリがWebサーバーの実行ユーザーにとって書き込み可能であるかを必ず確認してください。適切なファイル権限(パーミッション)設定が不足していると、ファイルの移動に失敗する主な原因となります。また、セキュリティのため、アップロードされたファイル名をそのまま使用せず、uniqidなどを用いてユニークで安全なファイル名を生成することを強く推奨します。これにより、予期せぬ上書きや悪意のあるスクリプト実行を防ぎます。最後に、$_FILESスーパーグローバル変数に含まれるエラーコードを詳細にチェックし、適切なエラーメッセージをユーザーに返すことで、より堅牢なアップロード処理を実装できます。

関連コンテンツ

関連プログラミング言語