PHP Ler e Escrever Arquivos

Tudo o que um programa guarda em variáveis some quando ele termina. Para lembrar de alguma coisa até a próxima vez (a lista de compras, os recados dos clientes, o registro das vendas do dia), o programa precisa gravar num arquivo. Nesta aula você vai ler e escrever arquivos de texto, criar pastas, listar e apagar arquivos, e entender as mensagens que o PHP mostra quando algo dá errado.

Você vai aprender
  • O jeito curto: file_put_contents (gravar) e file_get_contents (ler), e como acrescentar com FILE_APPEND.
  • Ler um arquivo como uma lista de linhas com file().
  • O jeito "passo a passo": fopen, fgets, fwrite e fclose, e quando ele vale a pena.
  • Perguntar se algo existe: file_exists, is_file, is_dir.
  • Pastas: mkdir, scandir, rmdir; apagar com unlink.
  • Os erros reais: arquivo inexistente, Permission denied, File exists.

Os exemplos rodam na pasta /home/ana/arq da máquina da Ana. No editor do guia, os arquivos são criados num disco de mentira, na memória do navegador: pode gravar à vontade.


A ideia: um caderno na gaveta

Um arquivo é como um caderno guardado numa gaveta. Para usar, você precisa saber o nome do caderno e em que gaveta (pasta) ele está. Aí pode ler o que está escrito, escrever por cima (apagando o que havia) ou continuar escrevendo na próxima linha em branco. O PHP tem uma função para cada uma dessas ações.


Gravar e ler de uma vez

As duas funções mais usadas fazem tudo numa linha só:

  • file_put_contents ("pôr o conteúdo no arquivo"): grava um texto num arquivo. Se o arquivo não existe, ele é criado.
  • file_get_contents ("pegar o conteúdo do arquivo"): devolve todo o texto do arquivo, como uma string.
<?php
$bytes = file_put_contents('compras.txt', "arroz\nfeijão\nleite\n");
var_dump($bytes);
echo file_get_contents('compras.txt');
file_put_contents(
A função que grava.
'compras.txt',
1º argumento: o nome do arquivo. Sem pasta na frente, ele fica na pasta atual.
"arroz\nfeijão\nleite\n"
2º argumento: o texto. Cada \n é uma quebra de linha (por isso aspas duplas; veja Strings).
$bytes =
A função devolve quantos bytes gravou (ou false se falhou).
file_get_contents('compras.txt')
Lê o arquivo inteiro e devolve o texto, que o echo mostra.
$ cd arq
$ php escrever.php
int(20)            « 20 bytes gravados
arroz
feijão
leite
$ cat compras.txt  « o arquivo ficou no disco, mesmo depois de o PHP terminar
arroz
feijão
leite
Por que 20, se são 19 caracteres?

Conte: arroz (5) + feijão (6) + leite (5) + 3 quebras de linha = 19 caracteres. Mas o ã ocupa 2 bytes em UTF-8, então o arquivo tem 20 bytes. A mesma história do strlen com acentos.

Escrever por cima × acrescentar no fim

Cuidado: por padrão, file_put_contents apaga o que havia no arquivo e grava o texto novo. Para acrescentar no fim, passe a "bandeira" (flag) FILE_APPEND ("anexar ao arquivo") como terceiro argumento:

<?php
file_put_contents('compras.txt', "café\n", FILE_APPEND);
file_put_contents('compras.txt', "açúcar\n", FILE_APPEND);
echo file_get_contents('compras.txt');
$ php acrescentar.php
arroz
feijão
leite
café              « acrescentado
açúcar            « acrescentado

E sem o FILE_APPEND:

<?php
file_put_contents('compras.txt', "pão\n");
echo file_get_contents('compras.txt');
$ php sobrescrever.php
pão               « o resto da lista sumiu: o arquivo foi reescrito do zero
Não existe "desfazer"

Quando o PHP grava por cima, o conteúdo antigo é perdido para sempre: não vai para a lixeira. Antes de testar algo com arquivos importantes, faça uma cópia (copy('dados.txt', 'dados-copia.txt');).

Missão: o programa grava a lista e depois tenta acrescentar café e açúcar, mas a lista começa do zero a cada linha. Conserte as duas últimas gravações para que a lista final tenha os cinco itens.

<?php
file_put_contents('compras.txt', "arroz\nfeijão\nleite\n");
file_put_contents('compras.txt', "café\n");
file_put_contents('compras.txt', "açúcar\n");
echo file_get_contents('compras.txt');
<?php
file_put_contents('compras.txt', "arroz\nfeijão\nleite\n");
file_put_contents('compras.txt', "café\n", FILE_APPEND);
file_put_contents('compras.txt', "açúcar\n", FILE_APPEND);
echo file_get_contents('compras.txt');

Ler linha por linha com file()

Muitas vezes você não quer o texto inteiro, e sim cada linha separada, para contar, filtrar ou mostrar numa lista. A função file() lê o arquivo e devolve um array, com uma linha em cada posição:

<?php
file_put_contents('compras.txt', "arroz\nfeijão\n\nleite\n");
$linhas = file('compras.txt');
var_dump($linhas);
$ php linhas.php
array(4) {
  [0]=>
  string(6) "arroz
"                       « a quebra de linha veio junto!
  [1]=>
  string(8) "feijão
"
  [2]=>
  string(1) "
"                       « a linha em branco virou um item
  [3]=>
  string(6) "leite
"
}

Duas surpresas: cada item termina com a quebra de linha ("arroz\n", por isso o string(6)), e a linha vazia também entrou. Quase sempre você quer o contrário, e para isso existem duas bandeiras, que se combinam com |:

$limpas = file('compras.txt', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
var_dump($limpas);
echo "Itens: " . count($limpas) . "\n";
« (continuação da saída de php linhas.php)
array(3) {
  [0]=>
  string(5) "arroz"
  [1]=>
  string(7) "feijão"
  [2]=>
  string(5) "leite"
}
Itens: 3
BandeiraTraduçãoEfeito
FILE_IGNORE_NEW_LINES"ignorar as quebras de linha"Tira o \n do fim de cada item.
FILE_SKIP_EMPTY_LINES"pular as linhas vazias"Não coloca as linhas em branco no array (só funciona junto com a de cima).

O | entre elas é o "ou" de bits: um jeito de passar várias bandeiras num argumento só. Você não precisa entender a matemática, só a receita: BANDEIRA_1 | BANDEIRA_2.


O jeito passo a passo: fopen, fgets, fwrite, fclose

file_get_contents e file() carregam o arquivo inteiro na memória. Para arquivos pequenos, perfeito. Para um arquivo de registro com 2 GB, o programa estouraria a memória. O jeito "clássico", herdado da linguagem C, abre o arquivo e lê ou escreve um pedaço por vez:

  1. fopen (file open, "abrir arquivo"): abre e devolve um identificador (um "marcador de página" do caderno).
  2. fgets (file get string, "pegar uma linha"): lê a próxima linha. Ou fwrite (file write, "escrever"): escreve um texto.
  3. fclose (file close, "fechar"): fecha o arquivo e libera o identificador.
<?php
$arquivo = fopen('diario.txt', 'w');
fwrite($arquivo, "16/10: vendi 3 bolos\n");
fwrite($arquivo, "17/10: vendi 5 bolos\n");
fclose($arquivo);

$arquivo = fopen('diario.txt', 'r');
$numero = 1;
while (($linha = fgets($arquivo)) !== false) {
    echo $numero . ": " . $linha;
    $numero++;
}
fclose($arquivo);
LinhaO que faz
$arquivo = fopen('diario.txt', 'w');Abre para escrever ('w' = write). Cria o arquivo ou apaga o conteúdo antigo. $arquivo guarda o identificador.
fwrite($arquivo, "…\n");Escreve o texto no ponto onde o "marcador" está, e o marcador anda para depois dele.
fclose($arquivo);Fecha. Garante que tudo foi gravado no disco.
fopen('diario.txt', 'r')Abre de novo, agora para ler ('r' = read), com o marcador no começo.
while (($linha = fgets($arquivo)) !== false)Lê uma linha e guarda em $linha; quando acabar o arquivo, fgets devolve false e o while para. Os parênteses extras fazem a atribuição acontecer antes da comparação.
echo $numero . ": " . $linha;Mostra a linha com o número. Sem \n no fim, porque a linha já traz a sua quebra.
$ php fopen.php
1: 16/10: vendi 3 bolos
2: 17/10: vendi 5 bolos
$ cat diario.txt
16/10: vendi 3 bolos
17/10: vendi 5 bolos
Por que !== false e não só while ($linha = fgets($arquivo))?

Uma linha com o texto "0" (sem quebra, a última do arquivo) é considerada "falsa" pelo PHP (veja Booleanos), e o laço pararia antes da hora. Comparar com !== false só para quando o arquivo realmente acabou.

Os modos do fopen

O segundo argumento do fopen diz o que você pretende fazer com o arquivo:

ModoVem deLê?Escreve?Se não existeSe já existe
'r'readsimnãoWarning, devolve falsecomeça do início
'w'writenãosimcriaapaga tudo
'a'appendnãosim, no fimcriamantém e escreve no fim
'x'exclusivenãosimcriaWarning File exists, devolve false
'r+', 'w+', 'a+', 'x+'O mesmo do modo sem +, mas permitindo ler e escrever.

O modo 'x' é útil para "crie só se ainda não existir", sem risco de apagar nada:

$ php modos.php      « $f = fopen('compras.txt', 'x'); var_dump($f);

Warning: fopen(compras.txt): Failed to open stream: File exists in /home/ana/arq/modos.php on line 2
bool(false)

Qual usar?

SituaçãoUse
Ler um arquivo pequeno inteiro (um texto, um JSON, um modelo de e-mail)file_get_contents
Ler um arquivo pequeno como lista de linhasfile(…, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES)
Gravar ou acrescentar um textofile_put_contents (com FILE_APPEND para acrescentar)
Arquivo grande (registros, exportações), ler ou escrever em partesfopen + fgets/fwrite + fclose
CSV (planilhas)fopen + fgetcsv/fputcsv (aula CSV)

Missão: o diario.txt (na segunda aba) tem as vendas de três dias. Complete o programa para mostrar cada linha numerada (1: 16/10: …) e, no fim, a linha Total: 3 dias.

<?php
$arquivo = fopen('diario.txt', 'r');
$numero = 0;
// leia as linhas aqui

fclose($arquivo);
16/10: vendi 3 bolos
17/10: vendi 5 bolos
18/10: vendi 2 tortas
<?php
$arquivo = fopen('diario.txt', 'r');
$numero = 0;
while (($linha = fgets($arquivo)) !== false) {
    $numero++;
    echo $numero . ": " . $linha;
}
fclose($arquivo);
echo "Total: $numero dias\n";

Perguntar antes: existe? é pasta?

Ler um arquivo que não existe dá aviso. Antes, você pode perguntar:

<?php
var_dump(file_exists('compras.txt'));
var_dump(file_exists('receitas.txt'));
var_dump(is_file('compras.txt'));
var_dump(is_dir('compras.txt'));
var_dump(is_dir('/home/ana'));
echo filesize('compras.txt'), " bytes\n";
$ php existe.php
bool(true)       « compras.txt existe
bool(false)      « receitas.txt não existe
bool(true)       « compras.txt é um arquivo comum
bool(false)      « …e não é uma pasta
bool(true)       « /home/ana é uma pasta
21 bytes
FunçãoPergunta
file_exists($c)"Existe algo com esse nome?" (arquivo ou pasta)
is_file($c)"É um arquivo comum?"
is_dir($c)"É uma pasta (directory)?"
is_readable($c) / is_writable($c)"Posso ler?" / "Posso escrever?"
filesize($c)Tamanho em bytes.
filemtime($c)Quando foi modificado (um timestamp; veja Datas e Horas).

Pastas: criar, listar, apagar

A Ana quer guardar cada pedido num arquivo, dentro de uma pasta pedidos:

<?php
if (!is_dir('pedidos')) {
    mkdir('pedidos');
    echo "Pasta criada.\n";
}
file_put_contents('pedidos/001.txt', "2 bolos de cenoura\n");
file_put_contents('pedidos/002.txt', "1 torta de limão\n");
print_r(scandir('pedidos'));
$ php pastas.php
Pasta criada.
Array
(
    [0] => .
    [1] => ..
    [2] => 001.txt
    [3] => 002.txt
)
mkdir('pedidos')
make directory, "criar pasta" (o mesmo nome do comando do terminal).
scandir('pedidos')
scan directory, "varrer a pasta": devolve um array com os nomes, em ordem alfabética.
. e ..
Toda pasta tem essas duas entradas especiais: . é a própria pasta e .. é a pasta de cima. Quase sempre você vai querer tirá-las.

Para listar só os arquivos de verdade, tire o . e o .. com array_diff, ou use glob, que aceita curingas como no terminal:

<?php
foreach (array_diff(scandir('pedidos'), ['.', '..']) as $nome) {
    echo "$nome\n";
}
print_r(glob('pedidos/*.txt'));
$ php glob.php
001.txt
002.txt
Array
(
    [0] => pedidos/001.txt         « glob devolve o caminho junto
    [1] => pedidos/002.txt
)

Pastas dentro de pastas

mkdir só cria uma pasta por vez, e reclama se ela já existir:

<?php
mkdir('pedidos');
mkdir('fotos/2026/outubro');
var_dump(mkdir('fotos/2026/outubro', 0755, true));
$ php mkdir-erro.php

Warning: mkdir(): File exists in /home/ana/arq/mkdir-erro.php on line 2

Warning: mkdir(): No such file or directory in /home/ana/arq/mkdir-erro.php on line 3
bool(true)
$ ls -R fotos
fotos:
2026

fotos/2026:
outubro

fotos/2026/outubro:

A linha 2 falhou porque pedidos já existia (File exists). A linha 3 falhou porque fotos e fotos/2026 não existiam (No such file or directory). A linha 4 funcionou graças ao terceiro argumento, true ("recursivo"), que cria todas as pastas do caminho, como o mkdir -p do terminal. O 0755 é a permissão das pastas novas, no mesmo formato do chmod (o 0 na frente é obrigatório: é um número em octal).

Apagar e mover

<?php
unlink('pedidos/001.txt');
print_r(scandir('pedidos'));
unlink('pedidos/001.txt');
$ php apagar.php
Array
(
    [0] => .
    [1] => ..
    [2] => 002.txt          « o 001.txt sumiu
)

Warning: unlink(pedidos/001.txt): No such file or directory in /home/ana/arq/apagar.php on line 4

unlink ("desvincular") é o nome histórico de "apagar arquivo", vindo do Unix. Para apagar uma pasta, use rmdir, que só funciona com a pasta vazia:

$ php rmdir.php      « rmdir('pedidos'); com o 002.txt ainda lá dentro

Warning: rmdir(pedidos): Directory not empty in /home/ana/arq/rmdir.php on line 2

E para copiar, mover e desmontar caminhos:

<?php
copy('diario.txt', 'diario-backup.txt');
rename('diario-backup.txt', 'pedidos/diario-antigo.txt');
print_r(scandir('pedidos'));
$caminho = '/home/ana/arq/pedidos/002.txt';
echo basename($caminho), "\n";
echo dirname($caminho), "\n";
print_r(pathinfo($caminho));
echo realpath('pedidos/../diario.txt'), "\n";
$ php outros.php
Array
(
    [0] => .
    [1] => ..
    [2] => 002.txt
    [3] => diario-antigo.txt       « copiado e depois movido (rename também move)
)
002.txt                            « basename: só o nome
/home/ana/arq/pedidos              « dirname: só a pasta
Array
(
    [dirname] => /home/ana/arq/pedidos
    [basename] => 002.txt
    [extension] => txt
    [filename] => 002
)
/home/ana/arq/diario.txt           « realpath: o caminho completo, sem ".."

Caminhos: relativos a quê?

Um nome como 'compras.txt' ou 'pedidos/001.txt' é um caminho relativo: o PHP o procura a partir da pasta atual do programa, que é a pasta de onde ele foi chamado, e não a pasta onde o arquivo .php está. Veja:

<?php
echo getcwd(), "\n";
echo __DIR__, "\n";
file_put_contents(__DIR__ . '/registro.txt', "ok\n");
var_dump(file_exists(__DIR__ . '/registro.txt'));
$ cd ~
$ php arq/dir.php
/home/ana             « getcwd(): a pasta atual (de onde a Ana chamou)
/home/ana/arq         « __DIR__: a pasta do arquivo dir.php
bool(true)

Se o programa tivesse gravado só 'registro.txt', o arquivo iria parar em /home/ana, e não ao lado do script. Num site, a "pasta atual" depende do servidor. Por isso, a regra é a mesma da aula include e require: para arquivos que acompanham o seu programa, monte o caminho com __DIR__ . '/nome.txt'.


Um mural de recados (modo web)

Juntando tudo: uma página com um formulário onde os clientes deixam recados, guardados num arquivo de texto, um por linha. Cada vez que alguém envia, a página acrescenta uma linha e mostra todas.

<?php
$arquivo = __DIR__ . '/recados.txt';

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $nome = trim($_POST['nome'] ?? '');
    $recado = trim($_POST['recado'] ?? '');
    if ($nome !== '' && $recado !== '') {
        $linha = str_replace("\n", ' ', "$nome: $recado") . "\n";
        file_put_contents($arquivo, $linha, FILE_APPEND | LOCK_EX);
    }
}

$recados = file_exists($arquivo)
    ? file($arquivo, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES)
    : [];
?>
<h1>Mural de recados da Lojinha</h1>
<form method="post">
  <input name="nome" placeholder="Seu nome">
  <input name="recado" placeholder="Seu recado">
  <button>Enviar</button>
</form>
<ul>
<?php foreach ($recados as $r): ?>
  <li><?= htmlspecialchars($r) ?></li>
<?php endforeach; ?>
</ul>
<p><?= count($recados) ?> recado(s).</p>
TrechoPor quê
str_replace("\n", ' ', …)Um recado com várias linhas quebraria a regra "um recado por linha". Trocamos as quebras por espaços.
FILE_APPEND | LOCK_EXAcrescenta no fim e tranca o arquivo (exclusive lock) durante a gravação: se dois clientes enviarem no mesmo instante, um espera o outro, e as linhas não se misturam.
file_exists(…) ? file(…) : []Na primeira visita o arquivo ainda não existe: em vez de um Warning, começamos com uma lista vazia.
htmlspecialchars($r)O recado foi escrito por um visitante. Sem escapar, um <script> no recado rodaria no navegador de todo mundo (veja XSS).

A Ana testou com o servidor embutido e o curl, mandando dois recados (o primeiro com HTML de propósito):

$ curl -s -X POST --data "nome=Bia&recado=Adorei o bolo de <b>cenoura</b>!" localhost:8000/
<h1>Mural de recados da Lojinha</h1>
<form method="post">
  <input name="nome" placeholder="Seu nome">
  <input name="recado" placeholder="Seu recado">
  <button>Enviar</button>
</form>
<ul>
  <li>Bia: Adorei o bolo de &lt;b&gt;cenoura&lt;/b&gt;!</li>       « o HTML virou texto inofensivo
</ul>
<p>1 recado(s).</p>
$ curl -s -X POST --data "nome=Caio&recado=Quero encomendar" localhost:8000/ | tail -6
</form>
<ul>
  <li>Bia: Adorei o bolo de &lt;b&gt;cenoura&lt;/b&gt;!</li>
  <li>Caio: Quero encomendar</li>
</ul>
<p>2 recado(s).</p>
$ cat recados.txt
Bia: Adorei o bolo de <b>cenoura</b>!            « no arquivo, o texto fica original
Caio: Quero encomendar

Missão: o mural já tem um recado da Ana (aba recados.txt), e a caixa abre enviando um recado da Bia. Mas o programa apaga os recados antigos a cada envio e não mostra a lista. Conserte a gravação (acrescentar, não substituir) e complete a parte que lê o arquivo em $recados. Depois, deixe outros recados pela aba Resultado.

<?php
$arquivo = __DIR__ . '/recados.txt';

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $nome = trim($_POST['nome'] ?? '');
    $recado = trim($_POST['recado'] ?? '');
    if ($nome !== '' && $recado !== '') {
        $linha = str_replace("\n", ' ', "$nome: $recado") . "\n";
        file_put_contents($arquivo, $linha);
    }
}

$recados = [];   // leia as linhas do arquivo aqui
?>
<h1>Mural de recados da Lojinha</h1>
<form method="post">
  <input name="nome" placeholder="Seu nome">
  <input name="recado" placeholder="Seu recado">
  <button>Enviar</button>
</form>
<ul>
<?php foreach ($recados as $r): ?>
  <li><?= htmlspecialchars($r) ?></li>
<?php endforeach; ?>
</ul>
<p><?= count($recados) ?> recado(s).</p>
Ana: Bem-vindos ao mural!
<?php
$arquivo = __DIR__ . '/recados.txt';

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $nome = trim($_POST['nome'] ?? '');
    $recado = trim($_POST['recado'] ?? '');
    if ($nome !== '' && $recado !== '') {
        $linha = str_replace("\n", ' ', "$nome: $recado") . "\n";
        file_put_contents($arquivo, $linha, FILE_APPEND | LOCK_EX);
    }
}

$recados = file_exists($arquivo)
    ? file($arquivo, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES)
    : [];
?>
<h1>Mural de recados da Lojinha</h1>
<form method="post">
  <input name="nome" placeholder="Seu nome">
  <input name="recado" placeholder="Seu recado">
  <button>Enviar</button>
</form>
<ul>
<?php foreach ($recados as $r): ?>
  <li><?= htmlspecialchars($r) ?></li>
<?php endforeach; ?>
</ul>
<p><?= count($recados) ?> recado(s).</p>

Quando dá errado

"Failed to open stream: No such file or directory"
$ php nao-existe.php     « $texto = file_get_contents('receitas.txt'); var_dump($texto); …

Warning: file_get_contents(receitas.txt): Failed to open stream: No such file or directory in /home/ana/arq/nao-existe.php on line 2
bool(false)

Warning: fopen(receitas.txt): Failed to open stream: No such file or directory in /home/ana/arq/nao-existe.php on line 4
bool(false)

O que significa: "falhou ao abrir: arquivo ou diretório inexistente". O PHP avisa e devolve false no lugar do texto (ou do identificador).

Como consertar: confira o nome (maiúsculas contam), a pasta atual (getcwd()) e use __DIR__. Se o arquivo pode legitimamente não existir, pergunte antes com file_exists.

"must be of type resource, false given" (o fopen falhou e ninguém conferiu)
$ php fgets-false.php    « $f = fopen('receitas.txt', 'r'); echo fgets($f);

Warning: fopen(receitas.txt): Failed to open stream: No such file or directory in /home/ana/arq/fgets-false.php on line 2

Fatal error: Uncaught TypeError: fgets(): Argument #1 ($stream) must be of type resource, false given in /home/ana/arq/fgets-false.php:3
Stack trace:
#0 /home/ana/arq/fgets-false.php(3): fgets(false)
#1 {main}
  thrown in /home/ana/arq/fgets-false.php on line 3

O que significa: o fopen devolveu false (primeiro aviso), e o programa passou esse false para o fgets, que esperava um arquivo aberto (um resource). O erro de verdade é o da linha 2; o da linha 3 é consequência.

Como consertar: confira o resultado do fopen antes de usar: if ($f === false) { echo "Não consegui abrir.\n"; exit(1); }. Na aula de exceções você vai ver formas mais elegantes.

"Permission denied"

A Ana tirou a permissão de escrita de um arquivo (chmod 444, veja as permissões no Guia Bash) e tentou gravar nele:

$ chmod 444 trancado.txt
$ ls -l trancado.txt
-r--r--r-- 1 ana ana 9 out 16 20:47 trancado.txt
$ php permissao.php      « $ok = file_put_contents('trancado.txt', "novo texto\n"); var_dump($ok);

Warning: file_put_contents(trancado.txt): Failed to open stream: Permission denied in /home/ana/arq/permissao.php on line 2
bool(false)

O que significa: "permissão negada". O arquivo existe, mas o usuário que roda o PHP não pode escrever nele (ou na pasta).

Como consertar: no seu computador, ajuste as permissões (chmod u+w trancado.txt). Num servidor, o PHP roda como outro usuário (no Ubuntu/Debian, www-data), e é esse usuário que precisa poder escrever na pasta de dados. Nunca resolva com chmod 777: isso deixa qualquer um no servidor alterar os arquivos.

"mkdir(): File exists"
Warning: mkdir(): File exists in /home/ana/arq/mkdir-erro.php on line 2

O que significa: a pasta já existe.

Como consertar: pergunte antes: if (!is_dir('pedidos')) { mkdir('pedidos'); }.


Referência

FunçãoO que fazDevolve
file_get_contents($c)Lê o arquivo inteirostring ou false
file_put_contents($c, $texto, $flags)Grava (por cima, ou no fim com FILE_APPEND; LOCK_EX tranca)bytes gravados ou false
file($c, $flags)Lê como array de linhasarray ou false
fopen($c, $modo)Abre (r w a x, com +)identificador ou false
fgets($f)Lê a próxima linhastring ou false no fim
fwrite($f, $texto)Escrevebytes ou false
feof($f)"Chegou ao fim?" (end of file)bool
fclose($f)Fechabool
file_exists, is_file, is_dirPerguntas sobre o caminhobool
filesize, filemtimeTamanho, data de modificaçãoint
mkdir($c, 0755, true)Cria pasta (com true: o caminho todo)bool
scandir($c) / glob('*.txt')Lista uma pasta / por curingaarray
unlink($c) / rmdir($c)Apaga arquivo / pasta vaziabool
copy($de, $para) / rename($de, $para)Copia / renomeia ou movebool
basename, dirname, pathinfo, realpathDesmontam e arrumam caminhosstring / array
getcwd(), __DIR__Pasta atual / pasta do scriptstring

Exercícios

1. Acrescentar

Complete para acrescentar uma linha ao fim do arquivo, sem apagar o que já existe:

file_put_contents('log.txt', "venda ok\n", );

2. Modo de leitura

Complete o modo do fopen para apenas ler:

$f = fopen('diario.txt', '');

3. Pastas aninhadas

Complete para criar fotos/2026/outubro de uma vez, mesmo que fotos não exista:

mkdir('fotos/2026/outubro', 0755, );

4. O que file('lista.txt') devolve, sem bandeiras?

5. O que acontece com fopen('dados.txt', 'w') se dados.txt já tem conteúdo?

6. O site da Ana mostra Permission denied ao gravar recados.txt no servidor. O que fazer?

Missão final: crie a pasta pedidos (só se ela não existir), grave três arquivos (001.txt, 002.txt, 003.txt, cada um com um pedido) e depois liste os nomes, um por linha, sem o . e o ... Termine com a linha 3 pedidos, calculada com count. Rode duas vezes: não pode aparecer nenhum Warning.

<?php
$pasta = __DIR__ . '/pedidos';

<?php
$pasta = __DIR__ . '/pedidos';
if (!is_dir($pasta)) {
    mkdir($pasta);
}
file_put_contents("$pasta/001.txt", "2 bolos de cenoura\n");
file_put_contents("$pasta/002.txt", "1 torta de limão\n");
file_put_contents("$pasta/003.txt", "12 brigadeiros\n");

$nomes = array_diff(scandir($pasta), ['.', '..']);
foreach ($nomes as $nome) {
    echo "$nome\n";
}
echo count($nomes) . " pedidos\n";

Resumo

  • file_put_contents grava (por cima!) e file_get_contents lê o arquivo inteiro. FILE_APPEND acrescenta no fim.
  • file() devolve as linhas num array; use FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES.
  • Para arquivos grandes: fopen → fgets/fwrite → fclose, com while (($linha = fgets($f)) !== false).
  • Pergunte antes com file_exists/is_dir; crie pastas com mkdir (true para o caminho todo), liste com scandir/glob, apague com unlink/rmdir.
  • Falhas viram Warning + false: confira o retorno antes de usar.
  • Caminhos relativos partem da pasta atual; use __DIR__.
  • Texto de visitante gravado em arquivo: escape com htmlspecialchars na hora de mostrar.

Próxima aula: CSV, o formato das planilhas, lido e gravado linha a linha.