PHP file handling uses streams whose paths, wrappers, modes, byte counts, metadata, locks, and lifetimes have observable failure and concurrency behavior. Convenience functions are safe only within explicit size and trust limits.
Reliable file work anchors trusted roots, prevents traversal and link escapes, streams bounded data, handles partial writes, replaces complete temporary files, uses advisory locks deliberately, and verifies cleanup and platform behavior.
<?php
$path = __DIR__ . '/data/message.txt';
$content = file_get_contents($path);
if ($content === false) {
throw new RuntimeException("Could not read {$path}");
}
echo trim($content);
file_get_contents() returns false on failure, so strict comparison is required.
<?php
$path = __DIR__ . '/data/audit.log';
$line = date(DATE_ATOM) . " lesson_viewed\n";
$bytes = file_put_contents($path, $line, FILE_APPEND | LOCK_EX);
if ($bytes === false) {
throw new RuntimeException('Audit log write failed.');
}
Use fopen() when data should be processed incrementally or when you need explicit stream control. Close handles in finally so an exception does not leak the resource.
<?php
$handle = fopen(__DIR__ . '/data/users.csv', 'rb');
if ($handle === false) {
throw new RuntimeException('CSV could not be opened.');
}
try {
while (($row = fgetcsv($handle)) !== false) {
echo implode(' | ', $row) . PHP_EOL;
}
} finally {
fclose($handle);
}
For an application-owned update, write the complete new content to a temporary file in the same directory, flush and close it, then replace the target according to the platform’s supported semantics. Use locking when cooperating processes follow the same protocol, but do not treat flock as a universal distributed lock.
Never combine an upload’s original name with a writable public path. Generate a server-owned name, validate size and content with trusted APIs, store outside the executable web root when possible, and serve it through a controlled download response.
File handling begins with a trusted root and a declared purpose: configuration, user upload, export, cache, log, or application asset. Do not let one general helper read and write arbitrary process paths.
Anchor application-owned paths with `__DIR__` or injected absolute roots. The current working directory can differ among web, CLI, tests, and workers, while URL paths do not follow filesystem rules.
Map external identifiers to generated or allowlisted names. Normalizing dot-dot text alone does not prevent traversal, alternate separators, stream wrappers, links, or encoding tricks. Verify the resolved target remains under the intended root.
Keep writable data outside executable source and the public document root when possible. Apply least-privilege filesystem permissions and separate tenants or data classes when disclosure impact differs.
`fopen` returns a stream resource or false and interprets a mode that controls reading, writing, truncation, creation, append position, and exclusivity. Select the mode from the data-loss policy before opening the file.
A write mode can truncate immediately, before an advisory lock is acquired. When existing content must remain until ownership is established, open without premature truncation, obtain the lock, then truncate deliberately or use a temporary-file replacement strategy.
Binary mode matters for portable byte behavior on systems that translate text line endings. Treat uploaded media, archives, hashes, and protocol payloads as binary and do not assume string length equals visible characters.
Stream wrappers can make a filename refer to URLs, memory, compression, or other resources. Permit only expected schemes at trust boundaries and configure network timeouts and TLS separately when remote access is an intentional feature.
Convenience functions can load an entire file into memory. Use them only after checking an acceptable maximum and considering races between metadata checks and reads. Stream large or untrusted files incrementally under a hard byte limit.
`fread` may return fewer bytes than requested, and line functions include delimiter and end-of-file behavior that must be checked. Loop on the actual result, distinguish false from an empty string where the API requires it, and stop on errors.
CSV, JSON Lines, and other record formats need parser-aware reading rather than manual delimiter splitting. Enforce maximum record length, field count, encoding, and total records before constructing domain objects.
A file can change while being read. Decide whether the feature needs a snapshot, lock, immutable generated name, content hash, or tolerance for change. Never use a pre-read check as proof that later content is safe.
`fwrite` can write fewer bytes than supplied, so critical stream code should advance by the returned count until complete or failed. Check every result and never report success only because opening the file worked.
For replaceable configuration or cache data, write a complete temporary file in the same filesystem, flush as required, set appropriate permissions, and rename it over the target. Readers then see the old or new complete file rather than a partial body.
Rename atomicity and overwrite behavior depend on filesystem and platform boundaries. Test the deployment filesystem, keep temporary and target files on the same volume, and define recovery for a leftover temporary file.
Durability is stronger than PHP returning from a write. Applications that promise crash-resistant persistence need explicit flush and filesystem guarantees and should usually prefer a transactional database for critical records.
`flock` provides advisory locking on supported local filesystems. Every cooperating writer must follow the same protocol; the lock does not automatically stop code that ignores it or guarantee equivalent behavior on every network filesystem.
Acquire a shared lock for a consistent read or exclusive lock for mutation where the feature requires it, check acquisition failure, hold it for the smallest complete critical section, and release it through cleanup paths.
Do not hold a file lock during remote calls or user interaction. Compute data first, lock, verify any version assumption, write or replace, then unlock. Add a timeout or non-blocking policy when indefinite waiting would exhaust workers.
File locking does not solve multi-record transactions, indexing, querying, or distributed coordination. Move those workloads to a database or service designed for concurrent state instead of layering increasingly complex lock files.
Filesystem checks such as existence, type, permissions, size, and modification time are snapshots that can become stale immediately. Open the target safely and validate the resulting handle when security or correctness depends on it.
Symbolic links can escape an apparently safe directory or redirect between check and use. For sensitive operations, control the storage tree, avoid following untrusted links, and verify canonical containment under the trusted root.
File extensions and names do not prove content type. Inspect data with an appropriate parser or detector, maintain an allowlist for the feature, and serve downloads with safe content type and disposition headers.
Permissions are affected by process identity, directory traversal rights, masks, access-control lists, and platform behavior. Deploy a dedicated runtime account and test the exact create, read, replace, and delete operations it needs.
Close handles in a finally block or equivalent ownership boundary, release locks, remove abandoned temporary files, and preserve the original exception when cleanup also fails. Do not rely solely on process shutdown for scarce handles in long-running workers.
Test missing files, empty files, maximum bytes, short reads and writes through test doubles where feasible, permission denial, disk-full behavior, invalid encoding, traversal attempts, links, lock contention, and interruption before rename.
Run platform tests on each supported operating system and the production-like filesystem. Case sensitivity, separators, rename replacement, permissions, and advisory locks can differ despite identical PHP source.
Log operation names, safe logical identifiers, byte counts, elapsed time, and failure categories without exposing private paths or file contents. Monitor temporary-file growth and repeated lock timeouts as early signs of incomplete cleanup or overload.
0 of 2 checked
Try this next
0 of 2 completed
Explore 500+ free tutorials across 20+ languages and frameworks.