PHP $_GET e $_POST
Na aula anterior, você montou formulários e viu que o navegador envia pares nome=valor. Agora é a vez do PHP: onde esses valores aparecem, como ler cada um, como saber se o formulário foi mesmo enviado e o que fazer quando um campo não veio. São dois arrays, $_GET e $_POST, que você vai usar em praticamente toda página PHP que escrever.
- O que são
$_GETe$_POSTe de onde vem o conteúdo deles. - Como ler um endereço como
?nome=Ana&idade=29. - Por que tudo chega como texto.
- Como saber se o formulário foi enviado:
$_SERVER['REQUEST_METHOD'] === 'POST'. - O aviso
Undefined array keye como evitá-lo com??eisset. - Como receber várias checkboxes de uma vez com
name="frutas[]". - O que é
$_REQUESTe por que é melhor não usar. - Como montar links com dados usando
http_build_query.
A ideia: duas caixas de correio
Imagine que a casa do PHP tem duas caixas de correio na porta. Tudo o que chega escrito no envelope (no endereço) vai para a caixa $_GET. Tudo o que chega dentro do envelope (no corpo de um POST) vai para a caixa $_POST. Antes de a primeira linha do seu programa rodar, o PHP já abriu a correspondência, separou cada par nome=valor e arrumou tudo nas caixas.
Essas "caixas" são arrays associativos (vistos em Arrays Associativos): o name do campo é a chave, e o que o visitante digitou é o valor.
| Array | Recebe os dados de | Exemplo de pedido |
|---|---|---|
$_GET | O endereço, depois do ? (a query string, "texto de consulta") | GET /busca.php?q=bolo |
$_POST | O corpo de um pedido POST (formulário com method="post") | POST /cadastro.php + corpo nome=Ana |
Elas são superglobais: existem sozinhas, em qualquer lugar do código, inclusive dentro de funções, sem precisar de global (veja Escopo). O nome com $_ e letras maiúsculas é a marca das superglobais; a lista completa está em $_SERVER e Outras Superglobais.
$_GET: os dados do endereço
Você nem precisa de formulário para usar $_GET: basta escrever o endereço. Este programa mostra o conteúdo das duas caixas com var_dump:
<?php echo "GET: "; var_dump($_GET); echo "POST: "; var_dump($_POST);
A Ana pediu dump.php?nome=Ana&idade=29:
$ curl -s "localhost:8000/dump.php?nome=Ana&idade=29"
GET: array(2) {
["nome"]=>
string(3) "Ana"
["idade"]=>
string(2) "29" « "29" com aspas: é texto, não número
}
POST: array(0) { « nada veio no corpo
}
Anatomia do endereço
- localhost:8000
- O servidor (a máquina e a porta).
- /dump.php
- O programa que vai rodar.
- ?
- Separa o caminho dos dados. Tudo depois dele é a query string.
- nome=Ana
- Primeiro par: vira
$_GET['nome']com o valor"Ana". - &
- Separa um par do outro.
- idade=29
- Segundo par:
$_GET['idade']vale"29".
Para usar um valor, leia a chave como em qualquer array:
<?php $nome = $_GET['nome']; echo "Olá, $nome!\n";
$ curl -s "localhost:8000/ola.php?nome=Ana" Olá, Ana!
string(2) "29". O protocolo da web só transporta texto, então o PHP entrega strings, mesmo que o campo seja type="number". Se precisar fazer conta, converta ((int) $_GET['idade'], de Conversão de Tipos) e, melhor ainda, confira se é mesmo um número (Validar Dados).$_POST: os dados do corpo
O mesmo programa, agora recebendo os dados por POST (o -d do curl faz um POST, como um formulário com method="post"):
$ curl -s localhost:8000/dump.php -d "nome=Ana&idade=29"
GET: array(0) {
}
POST: array(2) {
["nome"]=>
string(3) "Ana"
["idade"]=>
string(2) "29"
}
As mesmas chaves, os mesmos valores, só que na outra caixa. As duas caixas podem ter coisas ao mesmo tempo: um formulário POST cujo action tem um ?, como <form method="post" action="editar.php?id=5">:
$ curl -s "localhost:8000/dump.php?id=5" -d "nome=Ana"
GET: array(1) {
["id"]=>
string(1) "5" « do endereço
}
POST: array(1) {
["nome"]=>
string(3) "Ana" « do corpo
}
Isso é comum em páginas de edição: o id do que está sendo editado vai no endereço, e os campos alterados, no corpo.
A página foi aberta com ?nome=Ana&idade=29 no endereço. Rode e veja o var_dump na aba Resultado. Depois use os dois formulários: o primeiro envia por GET (o endereço muda), o segundo por POST.
<form method="get"> GET: <input name="nome" value="Bia"> <button>Enviar por GET</button> </form> <form method="post"> POST: <input name="nome" value="Caio"> <button>Enviar por POST</button> </form> <h3>$_GET</h3> <pre><?php var_dump($_GET); ?></pre> <h3>$_POST</h3> <pre><?php var_dump($_POST); ?></pre>
Foi enviado? REQUEST_METHOD
Quando uma página mostra e processa o próprio formulário (como na aula Formulários HTML), o PHP precisa saber em qual das duas situações está:
- Primeira visita: a pessoa só abriu a página. Mostre o formulário vazio.
- Envio: a pessoa clicou em Enviar. Processe os dados.
Para formulários POST, a forma mais segura de perguntar é olhar o método do pedido. Abrir uma página (digitar o endereço, clicar num link) é sempre GET; só o envio do formulário é POST. O PHP guarda o método em $_SERVER['REQUEST_METHOD']:
<?php
echo "Método: ", $_SERVER['REQUEST_METHOD'], "\n";
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
echo "O formulário foi enviado.\n";
} else {
echo "Primeira visita: mostrar o formulário.\n";
}
$ curl -s localhost:8000/metodo.php Método: GET Primeira visita: mostrar o formulário. $ curl -s -d "x=1" localhost:8000/metodo.php Método: POST O formulário foi enviado.
- $_SERVER
- Outra superglobal: informações sobre o pedido e o servidor (veja $_SERVER e Outras Superglobais).
- ['REQUEST_METHOD']
- "Método do pedido":
'GET'ou'POST'(sempre em maiúsculas). - === 'POST'
- Comparação estrita (de Comparação): exatamente o texto
POST.
Por que não if ($_POST) ou isset($_POST['enviar'])?
Você vai encontrar essas formas na internet. Elas funcionam quase sempre, mas têm furos:
| Forma | Problema |
|---|---|
if ($_POST) | Um POST sem nenhum campo preenchível (só checkboxes desmarcadas, por exemplo) chega com $_POST vazio, e o envio passa despercebido. |
if (isset($_POST['enviar'])) | Depende de o botão ter name="enviar". Se alguém renomear o botão, ou se o formulário for enviado por JavaScript (que não inclui botão nenhum), o envio passa despercebido. |
if ($_SERVER['REQUEST_METHOD'] === 'POST') | Nenhum: responde exatamente "este pedido é um envio POST?". Use esta. |
Para formulários GET (uma busca, por exemplo), o método é GET nas duas situações, então a pergunta é sobre o campo: "chegou um q?" (isset($_GET['q'])), como você vê a seguir.
Campo ausente: Undefined array key
O que acontece se a Ana abrir ola.php sem o ?nome=… no endereço? A chave 'nome' não existe no $_GET:
$ curl -s localhost:8000/ola.php <br /> <b>Warning</b>: Undefined array key "nome" in <b>/home/ana/site/ola.php</b> on line <b>2</b><br /> Olá, !
No navegador, isso aparece assim: uma linha em negrito Warning: Undefined array key "nome" in /home/ana/site/ola.php on line 2, e depois "Olá, !". O PHP não parou (um Warning é um aviso, não um erro fatal; veja Lendo Erros do PHP): a variável ficou com null, que vira texto vazio no echo.
Rodando o mesmo arquivo direto no terminal (onde $_GET está sempre vazio), o aviso sai sem as tags HTML:
$ php ola.php Warning: Undefined array key "nome" in /home/ana/site/ola.php on line 2 Olá, !
| Parte da mensagem | Significa |
|---|---|
Warning | Aviso: algo está errado, mas o programa continua. |
Undefined array key "nome" | "Chave de array não definida": você leu ['nome'], mas essa chave não existe. |
in /home/ana/site/ola.php on line 2 | Arquivo e linha da leitura. |
Num formulário, campo ausente é normal: primeira visita, checkbox desmarcada, alguém abrindo o endereço sem os dados. O seu código precisa esperar por isso. Há três ferramentas:
1. ??: "use isto, ou então aquilo"
O operador ?? (de ?? e ?->) devolve o valor da esquerda se ele existir e não for null; senão, devolve o da direita. Sem aviso nenhum:
<?php $nome = $_GET['nome'] ?? ''; echo "Olá, $nome!\n";
$ curl -s localhost:8000/ola2.php Olá, ! « sem o Warning: nome ficou com '' (texto vazio)
O valor da direita pode ser qualquer coisa útil: $_GET['nome'] ?? 'visitante', $_POST['frutas'] ?? [] (um array vazio), $_GET['pagina'] ?? 1. Este é o jeito mais comum de ler um campo: sempre com ?? valor_padrão.
2. isset: "chegou?"
isset($_GET['nome']) responde true se a chave existe (e não é null). Útil quando a pergunta é "veio ou não veio?", como numa checkbox:
if (isset($_POST['entrega'])) {
echo "Vamos entregar no seu endereço.";
}
3. empty: "chegou vazio?"
empty($_GET['nome']) é true se a chave não existe ou se o valor é "vazio" ('', '0', 0, null, []). Também não gera aviso. Cuidado com o '0': veja a tabela.
<?php $nome = $_GET['nome'] ?? 'visitante'; echo "Olá, $nome!\n"; var_dump(isset($_GET['nome']), empty($_GET['nome']));
| Endereço | $_GET['nome'] ?? 'visitante' | isset | empty |
|---|---|---|---|
vazio.php | visitante | false | true |
vazio.php?nome= | (texto vazio) | true | true |
vazio.php?nome=0 | 0 | true | true (!) |
vazio.php?nome=Ana | Ana | true | false |
Saídas reais das quatro chamadas:
$ curl -s localhost:8000/vazio.php Olá, visitante! bool(false) bool(true) $ curl -s "localhost:8000/vazio.php?nome=" Olá, ! bool(true) bool(true) $ curl -s "localhost:8000/vazio.php?nome=0" Olá, 0! bool(true) bool(true) $ curl -s "localhost:8000/vazio.php?nome=Ana" Olá, Ana! bool(true) bool(false)
Duas lições: o ?? só usa o padrão quando o campo não veio; um campo que veio vazio (?nome=, um <input> deixado em branco) continua vazio. E o empty acha que "0" é vazio, o que é errado para "quantidade: 0" ou "nota: 0". Para conferir campos de verdade, a aula Validar Dados usa trim e comparações explícitas.
Missão: a página foi aberta sem nada no endereço, e o PHP reclama de Undefined array key. Use ?? para que, sem nome, apareça Olá, visitante!, sem aviso nenhum. Depois envie o formulário com o seu nome.
<?php $nome = $_GET['nome']; ?> <p>Olá, <?= htmlspecialchars($nome) ?>!</p> <form> <input name="nome"> <button>Enviar</button> </form>
<?php $nome = $_GET['nome'] ?? 'visitante'; ?> <p>Olá, <?= htmlspecialchars($nome) ?>!</p> <form> <input name="nome"> <button>Enviar</button> </form>
Várias checkboxes: name="frutas[]"
A Ana quer um formulário de salada de frutas, onde a pessoa marca quantas frutas quiser. Se todas as checkboxes tiverem name="fruta", só a última marcada sobrevive (cada chave existe uma vez só, como você viu em Formulários HTML). A solução é colocar [] no fim do nome:
<form method="post" action="frutas.php"> <label><input type="checkbox" name="frutas[]" value="uva"> Uva</label> <label><input type="checkbox" name="frutas[]" value="pera"> Pera</label> <label><input type="checkbox" name="frutas[]" value="manga"> Manga</label> <button>Montar salada</button> </form>
Os colchetes dizem ao PHP: "junte todos os valores com este nome num array". Marcando uva e pera, o navegador envia frutas[]=uva&frutas[]=pera (com os colchetes codificados como %5B%5D), e o PHP monta uma lista:
<?php
$frutas = $_POST['frutas'] ?? [];
var_dump($frutas);
foreach ($frutas as $fruta) {
echo "- $fruta\n";
}
$ curl -s localhost:8000/frutas.php -d "frutas%5B%5D=uva&frutas%5B%5D=pera"
array(2) {
[0]=>
string(3) "uva"
[1]=>
string(4) "pera"
}
- uva
- pera
$ curl -s localhost:8000/frutas.php -d ""
array(0) { « nenhuma marcada: "frutas" não veio, e o ?? [] deu um array vazio
}
- name="frutas[]"
- Os colchetes vazios fazem cada valor marcado entrar no fim de uma lista, como
$frutas[] = 'uva'(de Arrays Indexados). - ?? []
- Nenhuma marcada = nada enviado. Com o array vazio como padrão, o
foreachsimplesmente não repete, sem aviso.
Funciona com qualquer campo, não só checkbox: <select name="cores[]" multiple> (lista com várias escolhas) ou vários <input name="telefones[]">. Também dá para dar nomes às chaves: name="endereco[rua]" e name="endereco[cidade]" chegam como $_POST['endereco']['rua'] e $_POST['endereco']['cidade'].
$_REQUEST: a caixa misturada (evite)
Existe uma terceira superglobal, $_REQUEST, que junta $_GET, $_POST e, conforme a configuração, os cookies (Cookies). Parece prático: "não importa de onde veio". O problema é justamente esse. Veja o que acontece quando o mesmo nome chega pelos três caminhos:
<?php var_dump($_REQUEST);
$ curl -s "localhost:8000/req.php?nome=Get" -d "nome=Post" -b "nome=Cookie"
array(1) {
["nome"]=>
string(6) "Cookie" « o cookie venceu os outros dois
}
$ curl -s "localhost:8000/req.php?nome=Get" -d "nome=Post"
array(1) {
["nome"]=>
string(4) "Post" « sem cookie, o POST venceu o GET
}
Quem "vence" depende de duas configurações do php.ini (request_order e variables_order), que mudam de servidor para servidor. No laboratório da Ana, o PHP sem php.ini põe até o cookie na mistura; muitos servidores deixam só GET e POST. Ou seja: o mesmo código pode se comportar diferente em outra máquina, e um cookie esquecido pode "trocar" um campo do formulário sem você perceber.
$_POST. Endereço e formulário GET → $_GET. Cookie → $_COOKIE. Assim o código diz claramente de onde espera cada dado.Montar links com dados: http_build_query
O caminho inverso também aparece muito: o PHP precisa escrever um endereço com dados, como o link "Próxima página" de uma busca. Concatenar à mão ("?busca=" . $busca . "&pagina=2") quebra quando o texto tem espaço, acento ou &. A função http_build_query ("construir a consulta HTTP") recebe um array e devolve a query string já codificada:
<?php $filtros = ['busca' => 'bolo de cenoura', 'pagina' => 2]; echo http_build_query($filtros), "\n"; echo '<a href="?' . http_build_query($filtros) . '">Próxima página</a>', "\n"; echo http_build_query(['frutas' => ['uva', 'pera']]), "\n";
$ curl -s localhost:8000/link.php busca=bolo+de+cenoura&pagina=2 <a href="?busca=bolo+de+cenoura&pagina=2">Próxima página</a> frutas%5B0%5D=uva&frutas%5B1%5D=pera
A última linha mostra que até arrays viram o formato com colchetes (frutas[0]=uva, codificado), que o PHP do outro lado transforma de volta em array. Para um valor sozinho, existe a irmã urlencode, vista em Escapar a Saída.
Quando dá errado
Warning: Undefined array key "nome"
$ curl -s localhost:8000/ola.php <br /> <b>Warning</b>: Undefined array key "nome" in <b>/home/ana/site/ola.php</b> on line <b>2</b><br /> Olá, !
O que aconteceu: o código leu $_GET['nome'], mas o campo não veio. As causas mais comuns: primeira visita (nada foi enviado ainda); o formulário usa POST e o código lê $_GET (ou o contrário); o name no HTML é diferente da chave no PHP (name="Nome" × $_GET['nome']: maiúsculas contam); checkbox desmarcada.
Como resolver: confira método e nome; e leia sempre com um padrão: $_GET['nome'] ?? ''.
foreach() argument must be of type array|object, string given
$ curl -s localhost:8000/frutas.php -d "frutas=uva" string(3) "uva" <br /> <b>Warning</b>: foreach() argument must be of type array|object, string given in <b>/home/ana/site/frutas.php</b> on line <b>4</b><br />
O que aconteceu: o foreach esperava um array (as frutas marcadas), mas chegou uma string. Ou o HTML está com name="frutas" (sem os colchetes), ou alguém mandou o pedido "na mão", sem o seu formulário. Lembre: qualquer pessoa pode enviar qualquer coisa para o seu PHP.
Como resolver: no HTML, name="frutas[]". No PHP, confira o tipo antes de usar: $frutas = $_POST['frutas'] ?? []; seguido de if (!is_array($frutas)) { $frutas = []; }.
Unsupported operand types ou A non-numeric value
<?php $a = "29" + "1"; var_dump($a); $b = "vinte" + 1; var_dump($b);
$ php conta.php
int(30)
Fatal error: Uncaught TypeError: Unsupported operand types: string + int in /home/ana/site/conta.php:4
Stack trace:
#0 {main}
thrown in /home/ana/site/conta.php on line 4
E com $b = "29 anos" + 1;:
$ php conta2.php Warning: A non-numeric value encountered in /home/ana/site/conta2.php on line 2 int(30)
O que aconteceu: os valores do formulário chegam como texto. Numa conta, o PHP converte sozinho um texto que é número ("29"). Um texto que não é número para o programa (TypeError, um erro fatal); um texto que só começa com número gera um aviso e usa o começo (veja Conversão de Tipos). Num formulário, basta alguém digitar "vinte" no campo de idade.
Como resolver: confira antes de usar, com filter_var($_POST['idade'], FILTER_VALIDATE_INT), explicado em Validar Dados.
O que aconteceu: o valor de $_GET/$_POST foi escrito com echo direto, e alguém digitou HTML no campo. Isso é a porta para um ataque chamado XSS.
Como resolver: todo dado do visitante que vai para a página passa por htmlspecialchars. A aula inteira sobre isso é Escapar a Saída (XSS).
Referência
| Código | O que faz |
|---|---|
$_GET['x'] | Valor de x vindo do endereço (?x=…) |
$_POST['x'] | Valor de x vindo do corpo de um POST |
$_GET['x'] ?? 'padrão' | O valor, ou 'padrão' se não veio (sem aviso) |
isset($_POST['x']) | true se x veio |
empty($_POST['x']) | true se não veio ou veio vazio (inclusive "0") |
$_SERVER['REQUEST_METHOD'] === 'POST' | O pedido é um envio POST? |
name="x[]" no HTML | Vários valores viram um array em $_POST['x'] |
name="x[chave]" no HTML | Vira $_POST['x']['chave'] |
http_build_query($array) | Monta a=1&b=2 codificado |
$_REQUEST | Mistura GET, POST (e talvez cookies). Evite. |
Exercícios
1. Ler sem aviso
Complete para ler o campo cidade do POST, com 'Recife' como padrão:
2. Foi enviado?
Complete a pergunta "o formulário POST foi enviado?":
3. Várias cores
Complete o name para que várias checkboxes de cor cheguem como array em $_POST['cores']:
4. O endereço é produto.php?id=7&cor=azul. O que var_dump($_GET['id']) mostra?
5. A pessoa deixou o campo <input name="apelido"> em branco e enviou. Quanto vale $_POST['apelido'] ?? 'sem apelido'?
6. Por que evitar $_REQUEST?
Missão: a salada de frutas da Ana. A página já foi aberta com um envio POST (uva e manga marcadas). Complete o PHP: só quando o método for POST, leia $_POST['frutas'] (com ?? []), mostre Você escolheu N frutas (use count) e a lista com foreach. Depois marque outras frutas na aba Resultado e envie.
<form method="post"> <label><input type="checkbox" name="frutas[]" value="uva"> Uva</label> <label><input type="checkbox" name="frutas[]" value="pera"> Pera</label> <label><input type="checkbox" name="frutas[]" value="manga"> Manga</label> <button>Montar salada</button> </form> <?php // seu código aqui
<form method="post">
<label><input type="checkbox" name="frutas[]" value="uva"> Uva</label>
<label><input type="checkbox" name="frutas[]" value="pera"> Pera</label>
<label><input type="checkbox" name="frutas[]" value="manga"> Manga</label>
<button>Montar salada</button>
</form>
<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$frutas = $_POST['frutas'] ?? [];
echo "<p>Você escolheu " . count($frutas) . " frutas:</p>";
echo "<ul>";
foreach ($frutas as $fruta) {
echo "<li>" . htmlspecialchars($fruta) . "</li>";
}
echo "</ul>";
}
Resumo
$_GETguarda os dados do endereço (?nome=Ana&idade=29);$_POST, os do corpo de um POST. São arrays associativos superglobais.- Tudo chega como string, mesmo números.
- Para saber se um formulário POST foi enviado:
$_SERVER['REQUEST_METHOD'] === 'POST'. - Campo que não veio gera
Warning: Undefined array key. Leia sempre com?? padrão; useissetpara "veio?". ??só troca o que não veio; campo vazio continua vazio.emptyconsidera"0"vazio.name="frutas[]"junta vários valores num array.- Evite
$_REQUEST: leia da caixa certa. http_build_querymonta links com dados, já codificados.