Read & Write Whole Files
<?php
// Read entire file into a string
$contents = file_get_contents("data.txt");
// Read into an array - one line per element
$lines = file("data.txt", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
// Write entire file (overwrites)
file_put_contents("out.txt", "Hello world\n");
// Append
file_put_contents("log.txt", "Entry\n", FILE_APPEND | LOCK_EX);
// Read from URL (allow_url_fopen must be on)
$json = file_get_contents("https://api.example.com/users");
Stream API (fopen / fread / fwrite)
| Mode | Means |
|---|---|
r | Read only - file must exist |
r+ | Read & write - file must exist |
w | Write - truncate existing or create new |
w+ | Read & write - truncate or create |
a | Append - position at end, create if missing |
a+ | Read & append - create if missing |
x | Create new - fail if exists |
b | Binary mode (append to any above on Windows) |
<?php
$fp = fopen("data.bin", "rb");
if ($fp === false) throw new RuntimeException("Cannot open");
try {
while (!feof($fp)) {
$chunk = fread($fp, 8192); // 8KB chunks
// process $chunk
}
} finally {
fclose($fp);
}
// Write
$fp = fopen("out.log", "a");
fwrite($fp, "Log entry\n");
fclose($fp);
Reading Line by Line
<?php
$fp = fopen("huge.log", "r");
try {
while (($line = fgets($fp)) !== false) {
if (str_contains($line, "ERROR")) {
echo $line;
}
}
} finally {
fclose($fp);
}
// Modern alternative: SplFileObject - memory-efficient iterator
foreach (new SplFileObject("huge.log") as $line) {
if (str_contains($line, "ERROR")) echo $line;
}
Directories
<?php
// Create
mkdir("logs/archive", 0755, recursive: true);
// List entries
foreach (scandir(__DIR__) as $entry) {
if ($entry === "." || $entry === "..") continue;
echo $entry . "\n";
}
// Glob (pattern matching)
foreach (glob("logs/*.log") as $file) {
echo $file . "\n";
}
// Recursive walk
$it = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator(__DIR__, FilesystemIterator::SKIP_DOTS)
);
foreach ($it as $file) {
if ($file->isFile()) echo $file->getPathname() . "\n";
}
// Remove
unlink("temp.txt");
rmdir("empty-dir");
File Metadata
<?php
$path = "data.txt";
if (!file_exists($path)) die("Missing");
echo filesize($path); // bytes
echo filemtime($path); // last modified unix timestamp
echo date("Y-m-d H:i:s", filemtime($path));
echo pathinfo($path, PATHINFO_EXTENSION); // txt
echo dirname($path);
echo basename($path);
echo mime_content_type($path); // text/plain
echo realpath($path); // absolute path with symlinks resolved
echo is_readable($path) ? "yes" : "no";
File Uploads
<?php
if (!isset($_FILES["upload"])
|| $_FILES["upload"]["error"] !== UPLOAD_ERR_OK
) {
die("Upload failed");
}
$file = $_FILES["upload"];
// Size limit
if ($file["size"] > 5 * 1024 * 1024) die("Too large (max 5MB)");
// Type check by content, not extension
$mime = mime_content_type($file["tmp_name"]);
$allowed = [
"image/jpeg" => "jpg",
"image/png" => "png",
"application/pdf" => "pdf",
];
if (!isset($allowed[$mime])) die("Type not allowed");
// Safe filename
$name = bin2hex(random_bytes(16)) . "." . $allowed[$mime];
$dest = __DIR__ . "/uploads/$name";
if (!move_uploaded_file($file["tmp_name"], $dest)) {
die("Save failed");
}
echo "Saved as $name";
An attacker can submit ../../etc/passwd or shell.php.jpg. ALWAYS generate a fresh random filename and ALWAYS check the MIME type with mime_content_type(), not the file extension.
CSV Files
<?php
// Read
$fp = fopen("users.csv", "r");
$headers = fgetcsv($fp);
while (($row = fgetcsv($fp)) !== false) {
$user = array_combine($headers, $row);
echo $user["email"] . "\n";
}
fclose($fp);
// Write
$fp = fopen("export.csv", "w");
fputcsv($fp, ["id", "name", "email"]);
foreach ($users as $u) {
fputcsv($fp, [$u->id, $u->name, $u->email]);
}
fclose($fp);
File Locking
Prevent concurrent writes from corrupting a file:
<?php
$fp = fopen("counter.txt", "c+");
flock($fp, LOCK_EX); // exclusive lock - blocks others
$count = (int) stream_get_contents($fp);
$count++;
rewind($fp);
ftruncate($fp, 0);
fwrite($fp, (string) $count);
fflush($fp);
flock($fp, LOCK_UN);
fclose($fp);