PHP File Open/Read

Su Subhalakshmi Chandran Updated 05 Nov 2026
7 min read ·Lesson 244 of 248

Reading files is one of the most common file operations: loading configuration, processing logs, importing data, or displaying stored text. PHP offers several ways to read, from one-line helpers that load an entire file to streams that read a few bytes at a time. This lesson explains when to use each and how to read safely and efficiently.

All examples read this sample log file, app.log:

2026-10-15 10:00:01 INFO User login
2026-10-15 10:02:17 ERROR Payment failed
2026-10-15 10:05:42 INFO Order created
2026-10-15 10:07:03 ERROR Email not sent

It can be created for testing with:

<?php
file_put_contents(__DIR__ . '/app.log', implode("\n", [
    '2026-10-15 10:00:01 INFO User login',
    '2026-10-15 10:02:17 ERROR Payment failed',
    '2026-10-15 10:05:42 INFO Order created',
    '2026-10-15 10:07:03 ERROR Email not sent',
]) . "\n");

Reading a Whole File

file_get_contents() — Into One String

<?php
$content = file_get_contents(__DIR__ . '/app.log');

echo strlen($content) . " bytes\n";
echo substr_count($content, 'ERROR') . " errors\n";

Output

157 bytes
2 errors

file() — Into an Array of Lines

<?php
$lines = file(__DIR__ . '/app.log', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);

echo count($lines) . " lines\n";
echo "Last: " . end($lines) . "\n";

Output

4 lines
Last: 2026-10-15 10:07:03 ERROR Email not sent
FlagEffect
FILE_IGNORE_NEW_LINESRemoves the line ending from each element
FILE_SKIP_EMPTY_LINESSkips empty lines (works together with FILE_IGNORE_NEW_LINES)

Reading Part of a File

<?php
echo file_get_contents(__DIR__ . '/app.log', false, null, 20, 4) . "\n";

Output

INFO

The fourth and fifth arguments are the offset (start at byte 20) and the length (read 4 bytes).

Whole-file functions are simple and fast for small files. They load everything into memory, so they are not suitable for very large files.


Opening a File: fopen()

Syntax

fopen(string $filename, string $mode, ...): resource|false

fopen() opens a file and returns a file handle (a resource) used by the other stream functions.

Modes for Reading

ModeMeaningFile Must Exist?Position
rRead onlyYesStart
r+Read and writeYesStart

(Modes for writing are covered in PHP File Create/Write.) Adding b (rb) means "binary mode"; on Linux it has no effect, but it prevents line-ending conversion on Windows, so it is a good habit for binary files.

Handling a Missing File

<?php
$handle = fopen(__DIR__ . '/missing.log', 'r');

if ($handle === false) {
    echo "Could not open the file.\n";
}

Output

PHP Warning:  fopen(/path/missing.log): Failed to open stream: No such file or directory in /path/script.php on line 2
Could not open the file.

fopen() returns false and emits a warning. Check the file with is_readable() first when its absence is expected, and always check the result.


Reading Line by Line: fgets()

<?php
$handle = fopen(__DIR__ . '/app.log', 'r');

if ($handle === false) {
    throw new RuntimeException('Cannot open log file.');
}

$counts = [];

while (($line = fgets($handle)) !== false) {
    $parts = explode(' ', rtrim($line), 4);
    $level = $parts[2] ?? 'UNKNOWN';
    $counts[$level] = ($counts[$level] ?? 0) + 1;
}

fclose($handle);

print_r($counts);

Output

Array
(
    [INFO] => 2
    [ERROR] => 2
)

How It Works

  1. fgets() reads one line including its newline character and moves the file position forward. At the end of the file, it returns false.
  2. ($line = fgets($handle)) !== false assigns and checks in one step. The strict comparison is required: a line containing only "0" would otherwise end the loop.
  3. rtrim() removes the newline; explode(' ', ..., 4) splits the line into date, time, level, and message (the limit keeps spaces inside the message).
  4. fclose() releases the handle. PHP closes handles automatically at the end of the script, but closing them explicitly frees resources in long-running scripts.

Only one line is in memory at a time, so this approach works for files of any size.

The feof() Pitfall

A common older pattern is:

while (!feof($handle)) {
    $line = fgets($handle);
    // ...
}

feof() becomes true only after a read attempt has hit the end. The last iteration therefore calls fgets() once more, which returns false, and code that uses $line without checking processes a non-existent line. Testing the result of fgets() directly, as above, avoids this.


Reading Bytes and Moving the Position

<?php
$handle = fopen(__DIR__ . '/app.log', 'r');

echo fread($handle, 10) . "\n";      // read 10 bytes
echo ftell($handle) . "\n";          // current position

fseek($handle, 20);                  // jump to byte 20
echo fgets($handle);                 // rest of the first line

rewind($handle);                     // back to the start
echo fgets($handle);

fclose($handle);

Output

2026-10-15
10
INFO User login
2026-10-15 10:00:01 INFO User login
FunctionPurpose
fread($handle, $length)Read up to $length bytes
fgetc($handle)Read a single character (byte)
ftell($handle)Current position in bytes
fseek($handle, $offset)Move to a position (SEEK_SET, SEEK_CUR, SEEK_END)
rewind($handle)Move back to the start
feof($handle)true after reading past the end

fread() is used for binary data and for processing large files in fixed-size chunks (for example, 8192 bytes at a time when calculating a checksum or streaming a download).


A Reusable Line Reader With a Generator

A generator function reads lines lazily and handles opening and closing in one place:

<?php
function readLines(string $path): Generator
{
    $handle = fopen($path, 'r');

    if ($handle === false) {
        throw new RuntimeException("Cannot open $path");
    }

    try {
        $lineNumber = 0;
        while (($line = fgets($handle)) !== false) {
            $lineNumber++;
            yield $lineNumber => rtrim($line, "\r\n");
        }
    } finally {
        fclose($handle);
    }
}

foreach (readLines(__DIR__ . '/app.log') as $number => $line) {
    if (str_contains($line, ' ERROR ')) {
        echo "Line $number: " . substr($line, 26) . "\n";
    }
}

Output

Line 2: Payment failed
Line 4: Email not sent

Why This Design Works Well

  • yield returns one line at a time to the foreach loop, so memory use stays constant even for gigabyte-sized files.
  • The line number becomes the key of each yielded value.
  • try ... finally guarantees that the file is closed, even if the loop stops early with break or an exception occurs.
  • rtrim($line, "\r\n") removes both Unix (\n) and Windows (\r\n) line endings, but keeps other trailing spaces.

substr($line, 26) skips the 20-character timestamp and the 6-character ERROR label. Generators are covered in PHP Generators.


SplFileObject: Object-Oriented Reading

<?php
$file = new SplFileObject(__DIR__ . '/app.log');
$file->setFlags(SplFileObject::DROP_NEW_LINE | SplFileObject::SKIP_EMPTY | SplFileObject::READ_AHEAD);

foreach ($file as $index => $line) {
    echo ($index + 1) . ': ' . $line . "\n";
}

Output

1: 2026-10-15 10:00:01 INFO User login
2: 2026-10-15 10:02:17 ERROR Payment failed
3: 2026-10-15 10:05:42 INFO Order created
4: 2026-10-15 10:07:03 ERROR Email not sent

SplFileObject is iterable line by line, closes the file automatically when the object is destroyed, and throws a RuntimeException (instead of returning false) if the file cannot be opened. The flags remove line endings and skip empty lines, including the empty "line" after the final newline.


Choosing a Reading Method

NeedMethod
Small file as one stringfile_get_contents()
Small file as an array of linesfile()
Large file, line by linefgets() loop, generator, or SplFileObject
Binary data or chunksfread()
Random accessfseek() + fread()
CSV rowsfgetcsv() (see PHP CSV Files)

Common Mistakes

Loading Large Files Completely

Effect: "Allowed memory size exhausted" errors.

Fix: Stream line by line.

Loose Checks on fgets() Results

Effect: Loops stop early at a line containing "0".

Fix: while (($line = fgets($h)) !== false).

Looping With while (!feof($h))

Effect: An extra iteration with false as the "line".

Fix: Test the read result instead.

Forgetting Line Endings

Effect: Comparisons like $line === 'INFO' fail because of the trailing \n.

Fix: rtrim() or FILE_IGNORE_NEW_LINES.

Not Handling Open Failures

Effect: Warnings followed by TypeErrors when false is passed to fgets().

Fix: Check fopen() results, or use SplFileObject, which throws an exception.


Best Practices

  • Use file_get_contents()/file() only for files that comfortably fit in memory.
  • Read large files incrementally with fgets(), generators, or SplFileObject.
  • Always check for false with strict comparisons, and close handles (preferably in finally).
  • Remove line endings explicitly and handle both \n and \r\n.
  • Build paths from __DIR__ and never open paths taken directly from user input.

Official reference: https://www.php.net/manual/en/function.fopen.php

0 Comments

Reviewed before they appear

No comments yet.

PHP
PHP Home PHP Switch PHP Match Alternative Syntax PHP Loops While Loop Do While Loop For Loop Foreach Loop Nested Loops Break Statement PHP Intro PHP Install PHP Built-in Web Server PHP Syntax PHP Tags PHP and HTML PHP Comments PHP Multiline Comments PHP Variables Variables Scope PHP Casting Logical Operators Null Coalescing Operator Spaceship Operator Operator Precedence Continue Statement Modify Strings PHP If...Else...Elseif PHP If Modify Strings PHP If Operators PHP Functions Concatenate Strings PHP If...Else User-defined Functions Slicing Strings Function Arguments PHP Shorthand if Escape Characters Default Argument Values PHP Nested if Pass by Reference String Interpolation Named Arguments PHP File Upload Variadic Functions Return Values Search in Strings Multibyte Strings Type Declarations in Functions PHP Numbers PHP Math PHP Floats PHP Random Numbers PHP Constants PHP Properties PHP Methods PHP Constructor Constructor Property Promotion PHP Cookies PHP Destructor PHP Access Modifiers Readonly Properties PHP Sessions PHP Inheritance Method Overriding PHP Filters The final Keyword CSRF Protection PHP Filters Advanced Session Security Secure Cookies PHP Magic Constants File Upload Security PHP Callback Functions PHP Operators Filesystem Security PHP JSON Arithmetic Operators Error Reporting in Production Hiding PHP Assignment Operators PHP Configuration (php.ini) Comparison Operators PHP Command Line (CLI) PHP Exceptions Increment / Decrement Operators Migrating to PHP 8 PHP Form Validation PHP Form Required PHP Form URL/E-mail Error Handling Form Input Sanitizing Escaping Output (htmlspecialchars) Custom Error Handler Checkbox, Radio and Select Inputs Sticky Forms PHP Form Complete PHP Date and Time Date Formatting Error Logging HTTP Headers and Redirects Output Buffering Sending Email PHP cURL Consuming REST APIs PHP Generators AJAX PHP AJAX Database AJAX XML AJAX Live Search AJAX Poll AJAX with JSON Security Introduction Password Hashing SQL Injection Prevention XSS Prevention Garbage Collection Project: Simple Blog PHP What is OOP PHP Classes/Objects The $this Keyword Access Array Items Update Array Items Add Array Items Remove Array Items Sorting Arrays Multidimensional Arrays Array Destructuring Array Spread Operator Search in Arrays Merge Arrays PHP Abstract Classes PHP Interfaces Polymorphism PHP Traits PHP Static Methods PHP Static Properties Late Static Binding Magic Methods Object Cloning MySQL Database MySQL Connect MySQL Create DB MySQL Create Table MySQL Insert Data MySQL Get Last ID MySQL Insert Multiple MySQL Prepared MySQL Select Data MySQL Where Debugging PHP Project: Image Upload Gallery $_SESSION $_ENV PHP RegEx RegEx Patterns Heredoc and Nowdoc RegEx Modifiers preg_match preg_replace preg_split PHP RegEx Functions Nullable Types Mixed and Never Types Nullsafe Operator PHP Attributes PHP Fibers Readonly Classes Property Hooks (PHP 8.4) Asymmetric Visibility (PHP 8.4) Reflection API SPL Data Structures Composer PHP Form Handling PDO Transactions PDO Error Handling PHP XML Parsers PHP SimpleXML Parser PHP SimpleXML - Get PHP XML Expat Parser PHP DOM Parser PHP XMLReader PHP XMLWriter AJAX Intro PHP Class Constants Encryption with Sodium Recursive Functions Variable Functions Anonymous Functions Closures Arrow Functions First-class Callable Syntax PHP Arrays Indexed Arrays Associative Arrays Create Arrays Variables Scope Static Variables Variable Variables PHP Echo / Print PHP printf / sprintf PHP Data Types PHP Booleans PHP NULL PHP Strings String Functions array_map / array_filter / array_reduce Array Functions PHP Superglobals $GLOBALS What Is `$_SERVER` ? $_REQUEST $_POST $_GET $_FILES $_COOKIE PHP Type Juggling MySQL Order By MySQL Delete Data MySQL Update Data MySQL Limit Data MySQL Joins with PHP Pagination with PHP PDO Introduction PDO Connect PDO Prepared Statements PDO Fetch Modes PSR-4 Autoloading Installing Extensions (PECL / PIE) OPcache & Performance PHP-FPM Project: Contact Form with Email Project: Login & Registration System Project: CRUD App with MySQL Project: Shopping Cart with Sessions Project: REST API with PHP DateTime Class Timezones PHP Include Include vs Require PHP File Handling PHP File Open/Read PHP File Create/Write File Delete / Rename / Copy PHP Directories PHP CSV Files
Ask about this post
AI Ask about this post

Ask questions about PHP File Open/Read and get answers drawn from it.

Signed-in readers only.