Kodokon kodokon.com

ファイル:読み取り、書き込み、アップロード、JSON

ファイルを読み書きし、イベントを記録し、アップロードを安全に受け入れ、データをJSONとして保存しましょう。

10 分 · 3 問

このレッスンを Kodokon で開く

ファイルの読み取りには一つの関数で足ります。file_get_contents()は中身全体を文字列として返し、file()は行の配列に分割します。読み取る前に、is_readable()でアクセスを確認しましょう。壊れたパスは、警告とともにfalseを返してしまいます。

PHP
<?php

declare(strict_types=1);

$path = __DIR__ . '/notes.txt';

if (!is_readable($path)) {
    exit("File not found or unreadable.");
}

$content = (string) file_get_contents($path);
echo nl2br(htmlspecialchars($content, ENT_QUOTES));

$lines = file(
    $path,
    FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES
);
echo count($lines) . " non-empty line(s)";
同じファイルを、まるごと、それから一行ずつ読み取る。

書き込みには、file_put_contents()がデフォルトで中身全体を置き換えます。二つのフラグが状況を変えます。FILE_APPENDは上書きの代わりに末尾へ追加し、LOCK_EXは排他ロックをかけて、同時の二つのリクエストが書き込みを混ぜ合わせないようにします。これが最小限のアプリケーションログのレシピです。

PHP
<?php

declare(strict_types=1);

$logFile = __DIR__ . '/app.log';

$line = sprintf(
    "[%s] New order from %s\n",
    date('Y-m-d H:i:s'),
    'alice@example.com'
);

file_put_contents(
    $logFile,
    $line,
    FILE_APPEND | LOCK_EX
);

echo "Event logged.";
数行で作る、信頼できるアプリケーションログ。

ファイルのアップロードには特別な注意が必要です。フォームはenctype="multipart/form-data"を宣言しなければならず、するとPHPは受け取ったファイルを$_FILESに記述します。すべてを検証しましょう。エラーコード、サイズ、そしてfinfoが検出する本当の型です。拡張子やブラウザが申告する型は決して信じてはいけません。最後に、一時ファイルを移動してよいのはmove_uploaded_file()だけです。

PHP
<?php

declare(strict_types=1);

$maxSize = 2 * 1024 * 1024;
$file = $_FILES['avatar'] ?? null;

if ($file === null || $file['error'] !== UPLOAD_ERR_OK) {
    exit("No valid file received.");
}

if ($file['size'] > $maxSize) {
    exit("File too large (2 MB max).");
}

$info = new finfo(FILEINFO_MIME_TYPE);
$mime = $info->file($file['tmp_name']);
$allowed = ['image/jpeg' => 'jpg', 'image/png' => 'png'];

if (!isset($allowed[$mime])) {
    exit("Format not allowed.");
}

$name = bin2hex(random_bytes(8));
$target = __DIR__ . '/uploads/' . $name
    . '.' . $allowed[$mime];

move_uploaded_file($file['tmp_name'], $target);
echo "Image saved.";
upload.php:サイズ、本当の型、そしてファイル名を管理下に。

JSON形式は、PHPと世界の他の部分とをつなぐ自然な橋です。json_encode()は配列をシリアライズし、json_decode()は逆を行います。連想配列を得るには第二引数にtrueを渡し、不正なJSONを黙ったnullではなく明確な例外に変えるにはJSON_THROW_ON_ERRORフラグを渡しましょう。

PHP
<?php

declare(strict_types=1);

$path = __DIR__ . '/tasks.json';

$json = is_readable($path)
    ? (string) file_get_contents($path)
    : '[]';

$tasks = json_decode(
    $json,
    true,
    512,
    JSON_THROW_ON_ERROR
);

$tasks[] = [
    'title' => 'Review the lesson',
    'done' => false,
];

file_put_contents(
    $path,
    json_encode(
        $tasks,
        JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE
    ),
    LOCK_EX
);

echo count($tasks) . " task(s) saved";
小さなデータストア:読み取り、変更、書き直し。

理解度チェック

このレッスンの要点をしっかり覚えているか確認しましょう。

  1. file_put_contents()に渡すFILE_APPENDフラグは何をしますか?
    • 内容を上書きする代わりにファイルの末尾へ追加する
    • 親フォルダが存在しなければ作成する
    • 書き込みのあいだファイルをロックする
  2. アップロードを確認するのに、なぜ$_FILES['avatar']['type']を当てにしてはいけないのですか?
    • このキーは一部のサーバーにしか存在しないから
    • 画像にしか効かないから
    • この値はブラウザから送られ、偽装できるから。本当の型はfinfoで検出しなければならない
  3. json_decode($json, true)で、第二引数のtrueは何を変えますか?
    • デコードがエラー時に例外を投げる
    • JSONオブジェクトがstdClassオブジェクトの代わりにPHPの連想配列になる
    • キーが大文字に変換される