Series 9 — Part 4 of 5

Audio pipeline processing creates temporary files — WAV from TTS, OGG from conversion. If those temp files are not cleaned up on every code path (including error paths), /tmp fills up silently and the next TTS call fails for a completely unrelated reason. This article covers the correct temp file lifecycle pattern.

The Create-Use-Unlink Pattern

// WRONG: cleanup only on success
$tmpFile = tempnam(sys_get_temp_dir(), 'tts_') . '.wav';
file_put_contents($tmpFile, $wavBytes);
$oggBytes = convert($tmpFile);
unlink($tmpFile);  // Only reached if convert() doesn't throw

// CORRECT: cleanup in finally — always runs
$tmpFile = null;
try {
    $tmpFile = sys_get_temp_dir() . '/' . bin2hex(random_bytes(8)) . '.wav';
    file_put_contents($tmpFile, $wavBytes);
    $oggBytes = convert($tmpFile);
} finally {
    if ($tmpFile !== null && file_exists($tmpFile)) {
        unlink($tmpFile);
    }
}

Naming Temp Files Safely

// WRONG: time() is predictable — path traversal risk
$tmp = sys_get_temp_dir() . '/audio_' . time() . '.wav';

// WRONG: uniqid() uses microtime — predictable with system access
$tmp = tempnam(sys_get_temp_dir(), 'tts_');  // Extension not set — harder to type-check

// CORRECT: random_bytes gives cryptographically unpredictable names
function make_temp(string $extension): string
{
    return sys_get_temp_dir() . '/' . bin2hex(random_bytes(8)) . '.' . ltrim($extension, '.');
}

$tmpWav = make_temp('wav');  // e.g. /tmp/a3f8b2c7d1e04f9a.wav

PHP register_shutdown_function for Emergency Cleanup

// Register at request start — fires even on fatal errors
$tempFiles = [];

register_shutdown_function(function () use (&$tempFiles) {
    foreach ($tempFiles as $path) {
        if (file_exists($path)) {
            unlink($path);
        }
    }
});

// Track all temp files
function create_temp(string $ext, array &$registry): string
{
    $path = make_temp($ext);
    $registry[] = $path;
    return $path;
}

$tmpWav = create_temp('wav', $tempFiles);
$tmpOgg = create_temp('ogg', $tempFiles);
// Both will be cleaned up at shutdown regardless of what happens

Monitoring /tmp Growth

# Check /tmp usage
du -sh /tmp

# Find orphaned audio files (older than 10 minutes)
find /tmp -name '*.wav' -o -name '*.ogg' -mmin +10 | wc -l

# Add to cron: clean up any orphaned audio files older than 30 minutes
0 * * * * find /tmp -name '*.wav' -o -name '*.ogg' -mmin +30 -delete

What to Watch For

  • Multiple temp files in one pipeline — The WAV → OGG conversion creates two temp files. Track both in the registry from the start, even though the OGG file doesn't exist yet when you register. The shutdown function checks file_exists() before unlinking.
  • /tmp as a tmpfs partition — On Raspberry Pi OS, /tmp may be a tmpfs (RAM-backed). Files there don't survive reboots, but they do consume RAM. Monitor /tmp usage on memory-constrained systems.