Lesson 18 / 25

Files, Dates and Times

Read and write files safely and handle dates with DateTimeImmutable and time zones.

Two everyday sources of bugs

For files, file_get_contents and file_put_contents handle small files; for large ones, stream line by line with SplFileObject or fopen/fgets, often wrapped in a generator (yield) so memory stays constant. Write important files atomically (write to a temporary file in the same directory, then rename), use LOCK_EX when appending from several processes, and never build paths from user input without validating them, to prevent directory traversal (../../etc/passwd). For uploads, check $_FILES['x']['error'], verify size and real content type (with finfo), generate your own file names and move them with move_uploaded_file to storage outside the web root or to object storage such as S3. For dates, prefer DateTimeImmutable, which returns new objects instead of mutating, avoiding a classic class of bugs. Always be explicit about time zones: store timestamps in UTC and convert to Asia/Kolkata (or the user's zone) for display. Use DateInterval and DatePeriod for arithmetic and ranges; the Carbon library adds convenient helpers on top.

Streaming a CSV with a generator and handling dates

Constant memory for big files; UTC storage, local display.

<?php
declare(strict_types=1);

/** @return Generator<int, array<string, string>> */
function readCsv(string $path): Generator
{
    $file = new SplFileObject($path);
    $file->setFlags(SplFileObject::READ_CSV | SplFileObject::SKIP_EMPTY | SplFileObject::READ_AHEAD);
    $headers = null;
    foreach ($file as $row) {
        if ($headers === null) { $headers = $row; continue; }
        yield array_combine($headers, $row);
    }
}

foreach (readCsv('/data/imports/orders.csv') as $line) {
    $importer->import($line);                 // one row in memory at a time
}

$placedAtUtc = new DateTimeImmutable('2026-10-03 06:45:00', new DateTimeZone('UTC'));
$local = $placedAtUtc->setTimezone(new DateTimeZone('Asia/Kolkata'));
echo $local->format('d M Y, h:i A'), PHP_EOL;     // 03 Oct 2026, 12:15 PM

$dueDate = $placedAtUtc->add(new DateInterval('P30D'));   // $placedAtUtc is unchanged

DateTime mutates, DateTimeImmutable does not

$due = $date->modify('+30 days'); with DateTime also changes $date, silently breaking other code that holds it. DateTimeImmutable returns a new object, so the original stays correct.

Quick check: Why is DateTimeImmutable generally preferred over DateTime?

  • Its methods return new objects instead of modifying the original, avoiding shared-state bugs
  • It supports more time zones
  • It is required for UTC
  • It formats dates faster
Answer

Its methods return new objects instead of modifying the original, avoiding shared-state bugs — Immutable date objects cannot be changed accidentally by other code.