Reading Files into Strings in PHP: Performance and Security
PHP provides multiple approaches to read file contents into a string, ranging from simple one-liners to streaming methods for large files. Your choice depends on file size, memory constraints, and whether you need line-by-line processing.
file_get_contents() — The Standard Approach
The simplest and most common method is file_get_contents():
$content = file_get_contents('path/to/file.txt');
echo $content;
This reads the entire file into a string in a single operation. It’s efficient for files under a few megabytes and handles the majority of real-world use cases. The function returns false on failure, which you should always check.
file() — Processing Lines as an Array
When you need to work with individual lines while keeping them in memory:
$lines = file('path/to/file.txt');
foreach ($lines as $line) {
echo $line;
}
The file() function returns an array where each element is a line from the file, including the newline character (\n). Use the FILE_SKIP_EMPTY_LINES flag to exclude blank lines, or FILE_IGNORE_NEW_LINES to strip line endings:
$lines = file('path/to/file.txt', FILE_SKIP_EMPTY_LINES | FILE_IGNORE_NEW_LINES);
foreach ($lines as $line) {
// $line has no trailing newline
echo trim($line);
}
Streaming Large Files with fopen() and fgets()
For files larger than available memory, stream the data in chunks:
$file = fopen('path/to/largefile.txt', 'r');
if (!$file) {
throw new RuntimeException('Unable to open file');
}
while (($line = fgets($file, 4096)) !== false) {
// Process $line here
echo $line;
}
fclose($file);
Reading in 4096-byte chunks avoids memory exhaustion on large files. The key is checking !== false rather than just !feof() — an empty line is still valid data. Never use feof() in the loop condition before calling fgets(), as it’s unreliable.
SplFileObject — Object-Oriented Approach
For modern PHP (7.4+), use SPL classes for cleaner syntax:
$file = new SplFileObject('path/to/file.txt');
$content = '';
foreach ($file as $line) {
$content .= $line;
}
SplFileObject implements Iterator, making it convenient for foreach loops. It automatically handles file closing via the destructor, reducing boilerplate.
Error Handling and Validation
Always validate file existence and readability before attempting to read:
$filepath = 'path/to/file.txt';
if (!file_exists($filepath)) {
throw new RuntimeException("File not found: $filepath");
}
if (!is_readable($filepath)) {
throw new RuntimeException("File is not readable: $filepath");
}
$content = file_get_contents($filepath);
if ($content === false) {
throw new RuntimeException("Failed to read file: $filepath");
}
echo $content;
Throwing exceptions is preferable to trigger_error() in modern PHP — it’s clearer and easier to handle at the application level.
Security: User-Supplied Filenames
When reading files based on user input, validate and sanitize strictly:
$filename = $_GET['file'] ?? null;
if (!$filename) {
throw new InvalidArgumentException('No filename provided');
}
// Reject path traversal attempts
if (strpos($filename, '..') !== false || strpos($filename, '/') !== false) {
throw new InvalidArgumentException('Invalid filename');
}
$base_dir = '/var/www/uploads/';
$safe_path = realpath($base_dir . $filename);
// Verify the resolved path is within the allowed directory
if ($safe_path === false || strpos($safe_path, $base_dir) !== 0) {
throw new InvalidArgumentException('Access denied');
}
$content = file_get_contents($safe_path);
Use realpath() to resolve symlinks and relative paths, then verify the result stays within your intended directory. This prevents directory traversal attacks.
Reading Remote Files
file_get_contents() can read HTTP(S) URLs if stream wrappers are enabled (the default):
$content = file_get_contents('https://example.com/api/data');
However, for production systems, use cURL instead for better control:
$ch = curl_init('https://example.com/api/data');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
$content = curl_exec($ch);
if ($content === false) {
throw new RuntimeException('cURL error: ' . curl_error($ch));
}
curl_close($ch);
cURL gives you explicit control over timeouts, SSL verification, redirects, and headers — essential for reliability in production.
Performance Summary
- file_get_contents(): Best for files under a few MB. Fastest for typical cases.
- file(): Use when you need individual lines and the entire file fits in memory.
- fopen() + fgets(): Required for files larger than available memory or streaming processing.
- SplFileObject: Cleaner syntax for line-by-line iteration; slightly more overhead than raw fopen().
Choose based on your file size and processing model rather than premature optimization.
