# Files, Dates and Times — PHP

Source: https://www.skillbyai.com/en/php/d-files

> 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
<?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.

**Quiz:** Why is DateTimeImmutable generally preferred over DateTime?

- [x] 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.
