Kodokon kodokon.com

Files: read, write, upload, JSON

Read and write files, log events, accept uploads safely, and store your data as JSON.

10 min · 3 questions

Open this lesson in Kodokon

Reading a file takes a single function: file_get_contents() returns the entire contents as a string, file() splits it into an array of lines. Before reading, check access with is_readable(): a broken path would return false along with a warning.

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)";
Reading the same file in full, then line by line.

To write, file_put_contents() replaces the entire contents by default. Two flags change things: FILE_APPEND adds to the end instead of overwriting, LOCK_EX sets an exclusive lock so that two simultaneous requests don't mix their writes. That's the recipe for a minimal application log.

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.";
A reliable application log in a few lines.

File uploads deserve particular care. The form must declare enctype="multipart/form-data", then PHP describes the received file in $_FILES. Validate everything: the error code, the size, and the real type detected by finfo - never the extension or the type announced by the browser. Finally, only move_uploaded_file() should move the temporary 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: size, real type, and file name under control.

The JSON format is the natural bridge between PHP and the rest of the world: json_encode() serializes an array, json_decode() does the reverse. Pass true as the second argument to get associative arrays, and the JSON_THROW_ON_ERROR flag to turn malformed JSON into a clear exception rather than a silent null.

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";
A mini data store: read, modify, rewrite.

Knowledge check

Make sure you remember the key points of this lesson.

  1. What does the FILE_APPEND flag passed to file_put_contents() do?
    • It adds the content to the end of the file instead of overwriting it
    • It creates the parent folder if it doesn't exist
    • It locks the file during the write
  2. Why shouldn't you rely on $_FILES['avatar']['type'] to check an upload?
    • This key only exists on some servers
    • It only works for images
    • This value is sent by the browser and can be forged; you must detect the real type with finfo
  3. What does the second argument true change in json_decode($json, true)?
    • Decoding throws an exception on error
    • JSON objects become PHP associative arrays instead of stdClass objects
    • Keys are converted to uppercase