File permissions, stat(), is_readable, is_writable
Concept
PHP provides several functions for writing to files. The right choice depends on whether you're writing a whole file at once, appending incrementally, or writing in parallel with other processes.
file_put_contents(string $file, mixed $data, int $flags = 0): One-liner for writing a string/array to a file. Returns bytes written or false on failure. Flags:
FILE_APPEND: Append to existing file instead of overwriting.LOCK_EX: Acquire exclusive lock before writing. Prevents race conditions when multiple processes write to the same file.
fopen() modes: 'w' — truncate and write. 'a' — append. 'x' — exclusive create (fails if file exists). 'r+' — read+write (pointer at start). 'a+' — read+append. 'w+' — read+write (truncate). 'c' — open for write, don't truncate (pointer at start). 'c+' — read+write, don't truncate.
fwrite(resource $handle, string $string): Write to an open file handle. Returns bytes written or false. For large data, write in chunks.
fputs(): Alias for fwrite().
Atomic writes: Writing directly to the target file risks partial writes visible to readers. The atomic pattern: write to a temp file → rename() the temp to the target. rename() is atomic on POSIX systems — readers either see the old file or the new one, never a partial write.
Directory creation: file_put_contents fails if the directory doesn't exist. Use mkdir($dir, 0755, true) to create directories recursively before writing.
Code Example
<?php
declare(strict_types=1);
// Simple write — overwrite
$bytes = file_put_contents('/tmp/output.txt', "Hello, World!\n");
if ($bytes === false) {
throw new \RuntimeException("Write failed");
}
// Append to log file with exclusive lock
file_put_contents(
'/var/log/app.log',
date('[Y-m-d H:i:s]') . " User login\n",
FILE_APPEND | LOCK_EX
);
// fopen for sequential writes
$handle = fopen('/tmp/report.csv', 'w');
try {
fwrite($handle, "name,email,score\n");
foreach ($users as $user) {
fwrite($handle, "{$user['name']},{$user['email']},{$user['score']}\n");
}
} finally {
fclose($handle);
}
// Atomic write — safe for concurrent readers
function atomicWrite(string $path, string $content): void
{
$dir = dirname($path);
if (!is_dir($dir)) {
mkdir($dir, 0755, recursive: true);
}
$tmp = $path . '.' . uniqid('', true) . '.tmp';
try {
if (file_put_contents($tmp, $content, LOCK_EX) === false) {
throw new \RuntimeException("Failed to write temp file");
}
if (!rename($tmp, $path)) {
throw new \RuntimeException("Failed to rename to $path");
}
} catch (\Throwable $e) {
if (file_exists($tmp)) {
unlink($tmp);
}
throw $e;
}
}
atomicWrite('/var/cache/config.json', json_encode($config, JSON_PRETTY_PRINT));
// fputcsv — write CSV row with proper quoting
$handle = fopen('/tmp/data.csv', 'w');
fputcsv($handle, ['Name', 'Email', 'Score']); // header
fputcsv($handle, ['Alice O\'Brien', 'alice@example.com', '95.5']); // handles quoting
fclose($handle);