Menu

PHP fopen(): Open, Read and Write Files (Modes Explained)

fopen('data.txt', 'r') opens a file and returns a handle, or false on failure. Read from the handle with fgets() or fread(), write with fwrite(), close it with fclose(). The mode ('r', 'w', 'a', 'x', 'c' and their + forms) decides what you can do.

This page includes runnable editors - edit, run, and see output instantly.

fopen('data.txt', 'r') opens a file and returns a handle, a stream resource that the other file functions take as their first argument. Read with fgets() (one line) or fread() (a number of bytes), write with fwrite(), and close with fclose(). The second argument is the mode: 'r' to read, 'w' to write, 'a' to append.

Output:

Got: apple
Got: banana
Got: cherry

fopen() returns false when it cannot open the file (it does not exist in r mode, a folder is missing, no permission), and PHP prints a warning such as:

Warning: fopen(missing.txt): Failed to open stream: No such file or directory in /home/index.php on line 2

So check for false before using the handle. For reading or writing a whole small file in one call, file_get_contents() and file_put_contents() are shorter; fopen() is for files you read piece by piece or keep open.

fopen modes

ModeReadWriteIf the file existsIf it does notStarts at
ryesnoopens itfailsstart
r+yesyesopens it, keeps contentfailsstart
wnoyesempties itcreates itstart
w+yesyesempties itcreates itstart
anoyeskeeps contentcreates itend, every write appends
a+yesyeskeeps contentcreates itend, every write appends
xnoyesfailscreates itstart
x+yesyesfailscreates itstart
cnoyeskeeps contentcreates itstart
c+yesyeskeeps contentcreates itstart

Two rows surprise people. r+ writes over the existing bytes in place, without inserting, and a+ always writes at the end, even after you move the pointer with fseek() (moving it only affects reads):

Output:

Xbc
Xbc
Xbc
end

On Windows you may see 'rb' or 'wb': the b flag means binary and stops Windows from translating line endings. It does nothing on Linux and macOS, so adding it is harmless and portable.

Read a file line by line with fgets()

fgets() returns the next line including its \n, and false at the end of the file. Because only one line is in memory at a time, this loop handles a log file of any size, and you can stop early with break:

Output:

2 errors in 5 lines
line 2: database timeout
line 4: disk full

Compare with false using !==, not with a plain while ($line = fgets($fh)): a line containing only "0" with no newline (the last line of a file can be one) is falsy and would end the loop early.

Write and append with fwrite()

fwrite() writes a string at the current position and returns the number of bytes written, or false on failure. fputs() is the same function under another name. Open with 'a' to add to the end of a file without touching what is there:

Output:

wrote 12 bytes
wrote 13 bytes
wrote 15 bytes
visit /home
visit /about
visit /contact
visit /home

PHP_EOL is the line ending of the system PHP runs on (\n on Linux and macOS, \r\n on Windows). For files other programs read, pick one explicitly, usually "\n".

Read and write CSV files with fgetcsv() and fputcsv()

fputcsv() writes an array as one CSV row and quotes fields that contain commas, quotes or newlines. fgetcsv() reads one row back as an array. Pass the separator, the enclosure and an empty escape character explicitly: that gives standard CSV, and PHP 8.4 prints a deprecation notice when the escape argument is left out.

Output:

name,city,note
Ada,London,"likes ""math"", a lot"
Linus,Helsinki,kernel

Ada lives in London: likes "math", a lot
Linus lives in Helsinki: kernel

Splitting lines with explode(',', $line) breaks as soon as a field contains a comma; fgetcsv() handles the quoting for you. For a semicolon-separated file (common in Excel exports in Europe), pass ';' as the separator.

Move around a file with fseek(), ftell() and rewind()

An open file has a position. Reads and writes start there and move it forward. ftell() tells you where it is, fseek() moves it to a byte offset, and rewind() goes back to the start. With w+ you can write a file and read it back through the same handle:

Output:

11
world
hello world

Lock a file with flock()

Two requests that read a counter, add one and write it back at the same time can both read 41 and both write 42. flock() with LOCK_EX makes the second one wait until the first releases the lock. Mode c+ is the right one here: it creates the file if needed and, unlike w+, does not empty it before you hold the lock.

Output:

Visitor number 1
Visitor number 2
Visitor number 3

Locks are advisory: they only stop other code that also calls flock(). For appending one line, file_put_contents($path, $line, FILE_APPEND | LOCK_EX) does the same in one call.

Common mistake: while (!feof($fh))

Many old tutorials loop with feof(). It returns true only after a read has already hit the end, so the loop runs once more than there are lines and the last fgets() returns false:

Output:

string(2) "a
"
string(2) "b
"
string(2) "c
"
bool(false)
4 rounds for 3 lines

That extra false becomes an empty row in a report or an empty element in an array. Loop on the read itself, while (($line = fgets($fh)) !== false), as every other example on this page does.

Frequently Asked Questions

What are the fopen modes in PHP?

r reads, w writes after emptying the file, a writes at the end, x creates a new file and fails if it exists, and c writes without emptying it. Adding + (r+, w+, a+, x+, c+) allows both reading and writing. Every mode except r and r+ creates the file when it is missing.

What is the difference between w and a in fopen?

w empties the file as soon as it is opened and writes from the start. a keeps the content and every fwrite() goes to the end of the file, even after fseek(), which makes it the mode for log files.

How do I read a CSV file in PHP?

Open it with fopen($path, 'r') and call fgetcsv() in a loop: while (($row = fgetcsv($fh, null, ',', '"', '')) !== false) { ... }. Each call returns one row as an array of fields, with quotes and embedded commas handled for you.

Do I need to call fclose in PHP?

PHP closes every open file when the script ends, so a short script works without it. Call fclose() anyway as soon as you are done: it flushes buffered writes, releases locks taken with flock(), and stops a long-running script from running out of file handles.

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED