fopen('data.txt', 'r') abre un archivo y devuelve un manejador, un recurso de stream que las demás funciones de archivos reciben como primer argumento. Lee con fgets() (una línea) o fread() (un número de bytes), escribe con fwrite() y cierra con fclose(). El segundo argumento es el modo: 'r' para leer, 'w' para escribir, 'a' para añadir.
Salida:
Got: apple
Got: banana
Got: cherry
fopen() devuelve false cuando no puede abrir el archivo (no existe en modo r, falta una carpeta, no hay permiso), y PHP imprime un warning como:
Warning: fopen(missing.txt): Failed to open stream: No such file or directory in /home/index.php on line 2
Así que comprueba si es false antes de usar el manejador. Para leer o escribir un archivo pequeño entero en una llamada, file_get_contents() y file_put_contents() son más cortas; fopen() es para archivos que lees por partes o mantienes abiertos.
Modos de fopen
| Modo | Lee | Escribe | Si el archivo existe | Si no existe | Empieza en |
|---|---|---|---|---|---|
r | sí | no | lo abre | falla | el principio |
r+ | sí | sí | lo abre, conserva el contenido | falla | el principio |
w | no | sí | lo vacía | lo crea | el principio |
w+ | sí | sí | lo vacía | lo crea | el principio |
a | no | sí | conserva el contenido | lo crea | el final, cada escritura añade |
a+ | sí | sí | conserva el contenido | lo crea | el final, cada escritura añade |
x | no | sí | falla | lo crea | el principio |
x+ | sí | sí | falla | lo crea | el principio |
c | no | sí | conserva el contenido | lo crea | el principio |
c+ | sí | sí | conserva el contenido | lo crea | el principio |
Dos filas sorprenden. r+ escribe sobre los bytes existentes en el sitio, sin insertar, y a+ siempre escribe al final, incluso después de mover el puntero con fseek() (moverlo solo afecta a las lecturas):
Salida:
Xbc
Xbc
Xbc
end
En Windows puedes ver 'rb' o 'wb': la opción b significa binario e impide que Windows traduzca los finales de línea. No hace nada en Linux ni en macOS, así que añadirla es inofensivo y portable.
Leer un archivo línea a línea con fgets()
fgets() devuelve la siguiente línea incluido su \n, y false al final del archivo. Como solo hay una línea en memoria cada vez, este bucle maneja un archivo de log de cualquier tamaño, y puedes parar antes con break:
Salida:
2 errors in 5 lines
line 2: database timeout
line 4: disk full
Compara con false usando !==, no con un simple while ($line = fgets($fh)): una línea que contenga solo "0" sin salto de línea (la última línea de un archivo puede serlo) es falsy y terminaría el bucle antes de tiempo.
Escribir y añadir con fwrite()
fwrite() escribe una cadena en la posición actual y devuelve el número de bytes escritos, o false si falla. fputs() es la misma función con otro nombre. Abre con 'a' para añadir al final de un archivo sin tocar lo que ya hay:
Salida:
wrote 12 bytes
wrote 13 bytes
wrote 15 bytes
visit /home
visit /about
visit /contact
visit /home
PHP_EOL es el final de línea del sistema en el que se ejecuta PHP (\n en Linux y macOS, \r\n en Windows). Para archivos que leen otros programas, elige uno de forma explícita, normalmente "\n".
Leer y escribir archivos CSV con fgetcsv() y fputcsv()
fputcsv() escribe un array como una fila CSV y pone entre comillas los campos que contienen comas, comillas o saltos de línea. fgetcsv() vuelve a leer una fila como array. Pasa de forma explícita el separador, el delimitador y un carácter de escape vacío: eso da un CSV estándar, y PHP 8.4 imprime un aviso de obsolescencia cuando se omite el argumento de escape.
Salida:
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
Dividir las líneas con explode(',', $line) falla en cuanto un campo contiene una coma; fgetcsv() se encarga de las comillas por ti. Para un archivo separado por punto y coma (habitual en las exportaciones de Excel en Europa), pasa ';' como separador.
Moverse por un archivo con fseek(), ftell() y rewind()
Un archivo abierto tiene una posición. Las lecturas y escrituras empiezan ahí y la hacen avanzar. ftell() te dice dónde está, fseek() la mueve a un desplazamiento en bytes y rewind() vuelve al principio. Con w+ puedes escribir un archivo y volver a leerlo con el mismo manejador:
Salida:
11
world
hello world
Bloquear un archivo con flock()
Dos peticiones que leen un contador, le suman uno y lo vuelven a escribir a la vez pueden leer las dos 41 y escribir las dos 42. flock() con LOCK_EX hace que la segunda espere hasta que la primera libere el bloqueo. Aquí el modo adecuado es c+: crea el archivo si hace falta y, a diferencia de w+, no lo vacía antes de que tengas el bloqueo.
Salida:
Visitor number 1
Visitor number 2
Visitor number 3
Los bloqueos son orientativos: solo detienen a otro código que también llame a flock(). Para añadir una línea, file_put_contents($path, $line, FILE_APPEND | LOCK_EX) hace lo mismo en una llamada.
Error frecuente: while (!feof($fh))
Muchos tutoriales antiguos recorren el archivo con feof(). Solo devuelve true después de que una lectura ya haya llegado al final, así que el bucle se ejecuta una vez más que líneas hay y el último fgets() devuelve false:
Salida:
string(2) "a
"
string(2) "b
"
string(2) "c
"
bool(false)
4 rounds for 3 lines
Ese false de más se convierte en una fila vacía en un informe o en un elemento vacío en un array. Recorre el archivo con la propia lectura, while (($line = fgets($fh)) !== false), como hacen todos los demás ejemplos de esta página.
Preguntas frecuentes
¿Cuáles son los modos de fopen en PHP?
r lee, w escribe después de vaciar el archivo, a escribe al final, x crea un archivo nuevo y falla si existe, y c escribe sin vaciarlo. Añadir + (r+, w+, a+, x+, c+) permite leer y escribir. Todos los modos salvo r y r+ crean el archivo cuando no existe.
¿Cuál es la diferencia entre w y a en fopen?
w vacía el archivo en cuanto se abre y escribe desde el principio. a conserva el contenido y cada fwrite() va al final del archivo, incluso después de fseek(), lo que lo convierte en el modo para los archivos de log.
¿Cómo leo un archivo CSV en PHP?
Ábrelo con fopen($path, 'r') y llama a fgetcsv() en un bucle: while (($row = fgetcsv($fh, null, ',', '"', '')) !== false) { ... }. Cada llamada devuelve una fila como array de campos, con las comillas y las comas incluidas resueltas por ti.
¿Tengo que llamar a fclose en PHP?
PHP cierra todos los archivos abiertos cuando termina el script, así que un script corto funciona sin él. Llama a fclose() de todos modos en cuanto termines: vuelca las escrituras en búfer, libera los bloqueos tomados con flock() e impide que un script de larga duración se quede sin manejadores de archivo.