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
| Mode | Read | Write | If the file exists | If it does not | Starts at |
|---|---|---|---|---|---|
r | yes | no | opens it | fails | start |
r+ | yes | yes | opens it, keeps content | fails | start |
w | no | yes | empties it | creates it | start |
w+ | yes | yes | empties it | creates it | start |
a | no | yes | keeps content | creates it | end, every write appends |
a+ | yes | yes | keeps content | creates it | end, every write appends |
x | no | yes | fails | creates it | start |
x+ | yes | yes | fails | creates it | start |
c | no | yes | keeps content | creates it | start |
c+ | yes | yes | keeps content | creates it | start |
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.