PHP Consumir APIs
Na aula anterior, a Ana criou uma API JSON e a testou com o curl do terminal. Agora é a vez do outro lado: o seu programa PHP fazendo o papel do cliente, pedindo dados a uma API e usando a resposta. É assim que um site consulta um CEP, calcula o frete nos Correios, confere um pagamento ou mostra a previsão do tempo. "Consumir" uma API é só isto: mandar uma requisição HTTP e ler o JSON que volta.
- Buscar uma URL com
file_get_contentse transformar o JSON em array comjson_decode. - Configurar a requisição com
stream_context_create: método, cabeçalhos, corpo, tempo limite. - Fazer o mesmo com as funções
curl_*, o jeito mais completo. - Ler o código de status (200, 201, 404…) e tratar cada caso.
- Lidar com o que dá errado: API desligada, resposta lenta, JSON inválido.
file_get_contents("http://…") e curl_exec não funcionam nelas. Todas as saídas de requisições desta aula vieram do computador da Ana, com a API rodando de verdade no servidor embutido. Nas caixas, você vai treinar a outra metade do trabalho: ler e tratar a resposta.A ideia: pedir por telefone
Pense na Ana ligando para uma papelaria para saber o preço de uma caneta. Ela precisa saber o número (a URL), dizer o que quer (o método: "quero consultar" = GET, "quero fazer um pedido" = POST), às vezes dar detalhes (o corpo, em JSON), e então ouvir a resposta. Às vezes ninguém atende (a API está fora do ar). Às vezes atendem e dizem "esse produto não existe" (404). Às vezes a pessoa demora tanto que a Ana desliga (tempo limite). Um bom cliente de API prevê os três casos.
Preparando: a API da lojinha ligada
Para ter algo de verdade para consumir, a Ana deixou a API da aula anterior rodando num terminal, na pasta ~/api:
$ cd ~/api $ php -S localhost:8000 index.php [Fri Oct 16 20:54:58 2026] PHP 8.5.8 Development Server (http://localhost:8000) started
Esse terminal fica ocupado com o servidor. Os programas clientes rodam num segundo terminal, na pasta ~/cliente. Lembrete do que a API sabe fazer: GET /produtos (lista), GET /produtos/2 (um produto), POST /produtos (cria um produto a partir de um JSON com nome e preco).
1. O jeito mais simples: file_get_contents com uma URL
Você conhece o file_get_contents da aula Ler e Escrever Arquivos: ele lê um arquivo inteiro e devolve o texto. O detalhe é que ele também aceita uma URL. Nesse caso, o PHP faz uma requisição GET e devolve o corpo da resposta.
<?php
declare(strict_types=1);
$resposta = file_get_contents('http://localhost:8000/produtos');
$produtos = json_decode($resposta, true);
foreach ($produtos as $produto) {
echo "{$produto['id']}. {$produto['nome']}: R$ ", number_format($produto['preco'], 2, ',', '.'), "\n";
}
- file_get_contents('http://localhost:8000/produtos')
- Faz
GET /produtose devolve o corpo da resposta como texto (umastring). - json_decode($resposta, true)
- Transforma o texto JSON em dados do PHP. O
truepede arrays associativos (sem ele, viriam objetosstdClass). Veja JSON. - foreach
- A partir daqui é PHP comum: uma lista de arrays.
$ php listar.php 1. Caderno: R$ 25,50 2. Caneta azul: R$ 3,90
Repare que são dois passos: buscar o texto e decodificar. Para ver a diferença, este programa mostra o que chega em cada etapa:
<?php
$resposta = file_get_contents('http://localhost:8000/produtos/2');
var_dump($resposta);
var_dump(json_decode($resposta, true));
$ php cru.php
string(61) "{
"id": 2,
"nome": "Caneta azul",
"preco": 3.9
}
"
array(3) {
["id"]=>
int(2)
["nome"]=>
string(11) "Caneta azul"
["preco"]=>
float(3.9)
}
A resposta é uma string de 61 caracteres, igual ao que o curl mostrava. Só depois do json_decode ela vira um array, com id como int e preco como float: o JSON guarda os tipos dos números.
Experimente: a variável $resposta tem exatamente o texto que a API devolveu para GET /produtos. Rode, depois acrescente um terceiro produto no texto JSON (por exemplo, {"id":3,"nome":"Borracha","preco":1.5}, sem esquecer a vírgula) e rode de novo. Tente também apagar uma aspa do JSON para ver o que acontece quando a resposta vem quebrada.
<?php
// O texto abaixo é exatamente o que a API devolveu para GET /produtos
// (o editor do guia não acessa a rede, então copiamos a resposta).
$resposta = '[{"id":1,"nome":"Caderno","preco":25.5},{"id":2,"nome":"Caneta azul","preco":3.9}]';
$produtos = json_decode($resposta, true);
foreach ($produtos as $produto) {
echo "{$produto['id']}. {$produto['nome']}: R$ ", number_format($produto['preco'], 2, ',', '.'), "\n";
}
Quando a API responde com erro
E se o produto não existir? A API responde 404 com um JSON explicando. Mas veja o que o file_get_contents faz:
<?php
$resposta = file_get_contents('http://localhost:8000/produtos/9');
var_dump($resposta);
$ php nao-existe.php Warning: file_get_contents(http://localhost:8000/produtos/9): Failed to open stream: HTTP request failed! HTTP/1.1 404 Not Found in /home/ana/cliente/nao-existe.php on line 2 bool(false)
Por padrão, qualquer status de erro (4xx ou 5xx) vira um Warning ("falhou ao abrir o fluxo: a requisição HTTP falhou! 404 Não Encontrado") e a função devolve false. O JSON {"erro": "Produto 9 não encontrado"} que a API mandou foi jogado fora. (A quebra de linha antes de in /home/… é real: a mensagem inclui o fim de linha da primeira linha da resposta HTTP.)
Configurando a requisição: stream_context_create
Para mudar o comportamento, você passa um contexto: um array de opções para o "fluxo" HTTP. Ele é o 3º argumento do file_get_contents (o 2º é sempre false nesse uso).
<?php
declare(strict_types=1);
$contexto = stream_context_create([
'http' => [
'timeout' => 5,
'ignore_errors' => true,
],
]);
$resposta = file_get_contents('http://localhost:8000/produtos/9', false, $contexto);
$cabecalhos = http_get_last_response_headers();
echo $cabecalhos[0], "\n";
var_dump(json_decode($resposta, true));
- stream_context_create([...])
- Cria o contexto. A chave
'http'guarda as opções para URLshttp://ehttps://. - 'timeout' => 5
- Desiste se a API ficar 5 segundos sem mandar dados. Sempre defina: o padrão (
default_socket_timeout) é 60 segundos, tempo demais para um visitante esperando a página. - 'ignore_errors' => true
- "Ignore os erros": devolve o corpo mesmo com status 4xx/5xx, sem
Warning. Agora quem decide o que fazer é o seu código. - http_get_last_response_headers()
- PHP 8.4+ Devolve os cabeçalhos da última resposta, um por posição. A posição
0é a linha de status. Em versões antigas, usava-se a variável mágica$http_response_header, que no PHP 8.5 está obsoleta (veja os erros no fim da aula).
$ php contexto.php
HTTP/1.1 404 Not Found
array(1) {
["erro"]=>
string(25) "Produto 9 não encontrado"
}
Agora o programa sabe o status (404) e tem a mensagem de erro da API para mostrar ao usuário.
Enviando dados: POST com JSON
O mesmo contexto define o método, os cabeçalhos e o corpo da requisição:
<?php
declare(strict_types=1);
$novo = ['nome' => 'Régua 30 cm', 'preco' => 4.5];
$contexto = stream_context_create([
'http' => [
'method' => 'POST',
'header' => "Content-Type: application/json\r\nAccept: application/json\r\n",
'content' => json_encode($novo),
'timeout' => 5,
'ignore_errors' => true,
],
]);
$resposta = file_get_contents('http://localhost:8000/produtos', false, $contexto);
echo http_get_last_response_headers()[0], "\n";
echo $resposta;
- 'method' => 'POST'
- O método HTTP (padrão:
GET). - 'header' => "…\r\n…\r\n"
- Os cabeçalhos num texto só, cada um terminado em
\r\n(por isso aspas duplas).Content-Typediz que o corpo é JSON;Acceptdiz que queremos JSON de volta. - 'content' => json_encode($novo)
- O corpo da requisição: o array convertido em texto JSON. É o que a API lê com
php://input.
$ php post-fgc.php
HTTP/1.1 201 Created
{
"id": 3,
"nome": "Régua 30 cm",
"preco": 4.5
}
Status 201 Created ("criado") e o produto novo, já com o id que a API escolheu.
2. O jeito completo: as funções curl_*
A extensão cURL ("client URL") é a mesma biblioteca do comando curl que você usou no terminal, agora dentro do PHP. Ela tem mais opções que o file_get_contents (tempo limite de conexão separado, mensagens de erro detalhadas, o status num número inteiro) e é o que quase todo projeto usa, diretamente ou por baixo de bibliotecas.
O roteiro é sempre o mesmo: iniciar, configurar, executar, ler as informações.
<?php
declare(strict_types=1);
$ch = curl_init('http://localhost:8000/produtos/2');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 5);
$resposta = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
echo "Status: $status\n";
$produto = json_decode($resposta, true);
echo "{$produto['nome']} custa R$ {$produto['preco']}\n";
| Linha | O que faz |
|---|---|
$ch = curl_init(URL) | Cria uma requisição (um objeto CurlHandle; $ch é o nome tradicional, de "curl handle", "alça"). |
CURLOPT_RETURNTRANSFER, true | "Devolva a resposta" em vez de imprimi-la na tela. Você quase sempre quer isto. |
CURLOPT_TIMEOUT, 5 | Tempo máximo da requisição inteira, em segundos. |
curl_exec($ch) | Executa. Devolve o corpo (texto) ou false se não conseguiu falar com o servidor. |
curl_getinfo($ch, CURLINFO_RESPONSE_CODE) | O código de status, como int (200, 404…). |
$ php curl-get.php Status: 200 Caneta azul custa R$ 3.9
(O preço saiu 3.9 porque foi interpolado direto; para 3,90, use number_format como no listar.php.)
POST com cURL e curl_setopt_array
Com muitas opções, curl_setopt_array configura tudo de uma vez. Este cliente também trata os três casos: sem conexão, criado e recusado.
<?php
declare(strict_types=1);
$novo = ['nome' => 'Apontador', 'preco' => 2.75];
$ch = curl_init('http://localhost:8000/produtos');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($novo),
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Accept: application/json'],
CURLOPT_CONNECTTIMEOUT => 3,
CURLOPT_TIMEOUT => 5,
]);
$resposta = curl_exec($ch);
if ($resposta === false) {
echo 'Falha na conexão: ', curl_error($ch), ' (código ', curl_errno($ch), ")\n";
exit(1);
}
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
$dados = json_decode($resposta, true);
if ($status === 201) {
echo "Criado: produto {$dados['id']} ({$dados['nome']})\n";
} else {
echo "A API respondeu $status: {$dados['erro']}\n";
}
- CURLOPT_POST => true
- Usa o método
POST. (Para outros, comoPUTeDELETE:CURLOPT_CUSTOMREQUEST => 'DELETE'.) - CURLOPT_POSTFIELDS
- O corpo. Com um texto (o JSON), ele vai exatamente assim. Com um array, o cURL enviaria como formulário, que a nossa API não aceita.
- CURLOPT_HTTPHEADER
- Os cabeçalhos, agora como uma lista, um por item (sem
\r\n). - CURLOPT_CONNECTTIMEOUT => 3
- Tempo máximo só para conectar. Se o servidor nem atender em 3 segundos, desiste.
- $resposta === false
- Não houve resposta nenhuma (servidor fora do ar, nome errado, tempo esgotado).
curl_errorexplica em texto;curl_errnodá o número do erro. - $status === 201
- Houve resposta: agora o status diz se deu certo. Um 404 ou 400 não faz o
curl_execdevolverfalse.
$ php curl-post.php Criado: produto 4 (Apontador)
E se a Ana esquecer o preço ($novo = ['nome' => 'Apontador'];)? A API recusa com 400, e o cliente mostra a mensagem dela:
$ php curl-post-400.php A API respondeu 400: Envie um JSON com "nome" e "preco"
3. Quando ninguém atende: conexão e tempo limite
A API está desligada
A Ana desligou o servidor (Ctrl+C no primeiro terminal) e rodou os dois clientes:
$ php curl-post.php
Falha na conexão: Failed to connect to localhost port 8000 after 0 ms: Could not connect to server (código 7)
$ php listar.php
Warning: file_get_contents(http://localhost:8000/produtos): Failed to open stream: Connection refused in /home/ana/cliente/listar.php on line 5
Fatal error: Uncaught TypeError: json_decode(): Argument #1 ($json) must be of type string, false given in /home/ana/cliente/listar.php:6
Stack trace:
#0 /home/ana/cliente/listar.php(6): json_decode(false, true)
#1 {main}
thrown in /home/ana/cliente/listar.php on line 6
O curl-post.php conferiu o false e mostrou uma mensagem clara ("falhou ao conectar em localhost porta 8000"). O listar.php não conferiu: o file_get_contents avisou "Connection refused" (conexão recusada) e devolveu false, o false foi parar no json_decode, e os tipos estritos derrubaram o programa. Regra: toda requisição pode falhar; confira o resultado antes de usar.
A API está lenta
Para testar, a Ana acrescentou à API uma rota que demora de propósito:
// rota de teste: demora 3 segundos para responder
if ($caminho === '/lenta') {
sleep(3);
responder(200, ['ok' => true]);
}
E um cliente que só espera 1 segundo:
<?php
declare(strict_types=1);
$ch = curl_init('http://localhost:8000/lenta');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 1);
$resposta = curl_exec($ch);
if ($resposta === false) {
echo 'Erro ', curl_errno($ch), ': ', curl_error($ch), "\n";
exit(1);
}
echo $resposta;
$ php lenta-curl.php Erro 28: Operation timed out after 1001 milliseconds with 0 bytes received
"A operação esgotou o tempo depois de 1001 milissegundos, com 0 bytes recebidos." Sem o CURLOPT_TIMEOUT, o cURL esperaria para sempre, e a página do visitante ficaria carregando até o PHP desistir. Com o file_get_contents, a opção é o 'timeout' do contexto, e a mensagem é menos detalhada:
<?php
$contexto = stream_context_create(['http' => ['timeout' => 1]]);
$resposta = file_get_contents('http://localhost:8000/lenta', false, $contexto);
var_dump($resposta);
$ php lenta-fgc.php Warning: file_get_contents(http://localhost:8000/lenta): Failed to open stream: HTTP request failed! in /home/ana/cliente/lenta-fgc.php on line 3 bool(false)
Os erros de cURL mais comuns
curl_errno | Constante | Significa |
|---|---|---|
| 6 | CURLE_COULDNT_RESOLVE_HOST | O nome do site não existe (erro de digitação, sem internet). |
| 7 | CURLE_COULDNT_CONNECT | Ninguém atendeu: servidor desligado ou porta errada. |
| 28 | CURLE_OPERATION_TIMEDOUT | Tempo limite esgotado. |
| 35 | CURLE_SSL_CONNECT_ERROR | Falha ao negociar o HTTPS. |
| 60 | CURLE_PEER_FAILED_VERIFICATION | O certificado HTTPS do site não é confiável. Não "resolva" desligando a verificação (CURLOPT_SSL_VERIFYPEER => false): isso abre a porta para alguém se passar pelo site. |
4. O JSON da resposta: confie, mas confira
Mesmo com status 200, a resposta pode não ser JSON: uma API com defeito pode mandar um Warning em HTML antes do JSON (você viu isso acontecer na aula anterior), ou uma página de manutenção. O json_decode devolve null nesses casos, em silêncio. Com a opção JSON_THROW_ON_ERROR, ele lança uma JsonException, que você trata com try/catch:
<?php
declare(strict_types=1);
$resposta = '<br /><b>Warning</b>: ... {"id":null}';
try {
$dados = json_decode($resposta, true, flags: JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
echo 'A resposta não é JSON válido: ', $e->getMessage(), "\n";
}
var_dump(json_decode($resposta, true));
echo json_last_error_msg(), "\n";
$ php json-throw.php A resposta não é JSON válido: Syntax error NULL Syntax error
A primeira linha veio do catch. As duas últimas mostram o jeito antigo: json_decode devolve NULL e você precisa lembrar de perguntar a json_last_error_msg() o que houve ("erro de sintaxe"). O flags: é um argumento nomeado, para não ter que escrever o 3º argumento (a profundidade, 512).
Juntando tudo: uma função para falar com a API
Em vez de repetir o roteiro do cURL em todo lugar, a Ana criou uma função que faz a requisição, confere a conexão, decodifica o JSON com segurança e devolve o status junto com os dados:
<?php
declare(strict_types=1);
const API = 'http://localhost:8000';
/**
* Faz uma requisição e devolve [código de status, dados decodificados].
* Lança RuntimeException se não conseguir falar com a API.
*/
function pedirJson(string $metodo, string $caminho, ?array $corpo = null): array
{
$ch = curl_init(API . $caminho);
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => $metodo,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Accept: application/json', 'Content-Type: application/json'],
CURLOPT_CONNECTTIMEOUT => 3,
CURLOPT_TIMEOUT => 10,
]);
if ($corpo !== null) {
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($corpo));
}
$resposta = curl_exec($ch);
if ($resposta === false) {
throw new RuntimeException('Sem resposta da API: ' . curl_error($ch));
}
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
$dados = json_decode($resposta, true, flags: JSON_THROW_ON_ERROR);
return [$status, $dados];
}
try {
[$status, $produto] = pedirJson('GET', '/produtos/2');
echo "$status → {$produto['nome']}\n";
[$status, $erro] = pedirJson('GET', '/produtos/99');
echo "$status → {$erro['erro']}\n";
[$status, $novo] = pedirJson('POST', '/produtos', ['nome' => 'Cola', 'preco' => 6.2]);
echo "$status → criado com id {$novo['id']}\n";
} catch (RuntimeException | JsonException $e) {
echo $e->getMessage(), "\n";
exit(1);
}
Com a API ligada (e a lista de volta aos dois produtos iniciais):
$ php loja-cliente.php 200 → Caneta azul 404 → Produto 99 não encontrado 201 → criado com id 3
Com a API desligada:
$ php loja-cliente.php Sem resposta da API: Failed to connect to localhost port 8000 after 0 ms: Could not connect to server
- CURLOPT_CUSTOMREQUEST => $metodo
- Um só lugar para qualquer método:
GET,POST,PUT,DELETE. - ?array $corpo = null
- O corpo é opcional:
GETnão tem corpo. - throw new RuntimeException(...)
- Falha de conexão vira uma exceção: quem chama decide o que mostrar.
- [$status, $produto] = …
- Desestruturação: pega as duas posições do array devolvido.
- catch (RuntimeException | JsonException $e)
- Pega os dois tipos de problema: sem conexão e resposta que não é JSON.
Qual usar?
| Ferramenta | Bom para | Cuidado |
|---|---|---|
file_get_contents + contexto | Um GET rápido, um script pequeno, sem depender de extensões. | Precisa de allow_url_fopen=On. Erros viram Warning se você esquecer ignore_errors. Mensagens de erro pouco detalhadas. |
curl_* | O dia a dia: qualquer método, tempo de conexão separado, status em int, erros detalhados. | Precisa da extensão curl (vem em quase toda instalação). Mais linhas para escrever. |
| Bibliotecas (Guzzle, Symfony HttpClient) | Projetos grandes: várias requisições ao mesmo tempo, novas tentativas automáticas, testes. | Instaladas com o Composer. Por baixo, usam cURL ou fluxos do PHP: o que você aprendeu aqui continua valendo. |
Para uma API pública de verdade, nada muda além da URL (com https://) e, em geral, de uma chave de acesso enviada num cabeçalho, como Authorization: Bearer SUA-CHAVE. Guarde a chave fora do código (num arquivo de configuração fora da pasta pública ou numa variável de ambiente lida com getenv), nunca no repositório.
Quando dá errado
Você viu esse acima. O que significa: o servidor respondeu, mas com status de erro, e o file_get_contents sem ignore_errors trata isso como falha e devolve false.
Como consertar: use um contexto com 'ignore_errors' => true e leia o status em http_get_last_response_headers()[0], ou troque por cURL.
O que significa: a requisição falhou (conexão recusada, tempo esgotado, status de erro) e devolveu false, mas o código passou esse false adiante como se fosse o texto da resposta.
Como consertar: confira if ($resposta === false) logo depois do file_get_contents ou do curl_exec, como no curl-post.php.
$ php -d allow_url_fopen=0 listar.php
Warning: file_get_contents(): http:// wrapper is disabled in the server configuration by allow_url_fopen=0 in /home/ana/cliente/listar.php on line 5
Warning: file_get_contents(http://localhost:8000/produtos): Failed to open stream: no suitable wrapper could be found in /home/ana/cliente/listar.php on line 5
Fatal error: Uncaught TypeError: json_decode(): Argument #1 ($json) must be of type string, false given in /home/ana/cliente/listar.php:6
Stack trace:
#0 /home/ana/cliente/listar.php(6): json_decode(false, true)
#1 {main}
thrown in /home/ana/cliente/listar.php on line 6
O que significa: alguns servidores de hospedagem desligam a opção allow_url_fopen ("permitir abrir URLs como arquivos"), e então o file_get_contents só aceita arquivos locais. (Aqui a Ana simulou isso com -d.)
Como consertar: use cURL, que não depende dessa opção. Ou, se o servidor for seu, ligue-a no php.ini.
$ php fechar.php
{
"id": 1,
"nome": "Caderno",
"preco": 25.5
}
Deprecated: Function curl_close() is deprecated since 8.5, as it has no effect since PHP 8.0 in /home/ana/cliente/fechar.php on line 5
O que significa: tutoriais antigos terminam com curl_close($ch);. Desde o PHP 8.0 a requisição é um objeto que se fecha sozinho quando não é mais usado, então a função não faz nada, e no 8.5 ela ganhou um aviso de obsoleta ("será removida no futuro").
Como consertar: apague a linha do curl_close.
$ php velho.php Deprecated: The predefined locally scoped $http_response_header variable is deprecated, call http_get_last_response_headers() instead in /home/ana/cliente/velho.php on line 3 HTTP/1.1 200 OK
O que significa: o código usa a variável $http_response_header, que o PHP criava "magicamente" depois de um file_get_contents com URL. Ela ainda funciona (a linha HTTP/1.1 200 OK saiu), mas está obsoleta no PHP 8.5.
Como consertar: troque por http_get_last_response_headers(), como a própria mensagem sugere.
O que significa: a API que o seu PHP chama está lenta ou travada, e a requisição não tem tempo limite (ou tem um muito alto). O visitante espera junto.
Como consertar: defina sempre CURLOPT_CONNECTTIMEOUT e CURLOPT_TIMEOUT (ou 'timeout' no contexto) com poucos segundos e trate o erro 28 mostrando uma mensagem amigável ("serviço indisponível, tente de novo").
Referência rápida
| Função / opção | Para quê |
|---|---|
file_get_contents($url, false, $ctx) | Faz a requisição e devolve o corpo (ou false) |
stream_context_create(['http' => [...]]) | Opções: method, header, content, timeout, ignore_errors |
http_get_last_response_headers() | Cabeçalhos da última resposta ([0] = status) PHP 8.4+ |
curl_init($url) | Cria a requisição |
curl_setopt($ch, OPÇÃO, valor) / curl_setopt_array | Configura uma / várias opções |
CURLOPT_RETURNTRANSFER | Devolver a resposta em vez de imprimir |
CURLOPT_POST / CURLOPT_CUSTOMREQUEST | Método POST / qualquer método |
CURLOPT_POSTFIELDS | Corpo da requisição |
CURLOPT_HTTPHEADER | Lista de cabeçalhos |
CURLOPT_CONNECTTIMEOUT / CURLOPT_TIMEOUT | Tempo máximo para conectar / para tudo (segundos) |
curl_exec($ch) | Executa; corpo ou false |
curl_getinfo($ch, CURLINFO_RESPONSE_CODE) | Código de status (int) |
curl_error($ch) / curl_errno($ch) | Mensagem / número do erro de conexão |
json_decode($txt, true, flags: JSON_THROW_ON_ERROR) | JSON → array, com exceção se for inválido |
Exercícios
1. Devolver em vez de imprimir
Complete a opção que faz o curl_exec devolver a resposta como texto:
2. O status da resposta
Complete para obter o código de status da requisição cURL:
3. Não jogar fora o corpo do erro
Complete a opção do contexto que faz o file_get_contents devolver o corpo mesmo com status 404:
4. A API respondeu 404 Not Found. O que o curl_exec devolve?
5. Por que definir um tempo limite em toda requisição?
6. O que json_decode('<br /><b>Warning</b>', true) devolve?
Missão final: a função mensagem recebe o status e o corpo de uma resposta da API (como viriam do curl_getinfo e do curl_exec) e deve devolver: Produto: NOME quando o status for 200; Erro STATUS: MENSAGEM (usando a chave erro do JSON) para outros status; e Resposta inválida se o corpo não for JSON. Rode primeiro para ver os avisos que a versão ingênua produz. Dica: JSON_THROW_ON_ERROR e catch (JsonException).
<?php
declare(strict_types=1);
function mensagem(int $status, string $corpo): string
{
$dados = json_decode($corpo, true);
return 'Produto: ' . $dados['nome'];
}
echo mensagem(200, '{"id":2,"nome":"Caneta azul","preco":3.9}'), "\n";
echo mensagem(404, '{"erro":"Produto 9 não encontrado"}'), "\n";
echo mensagem(200, '<br /><b>Warning</b>: Undefined array key'), "\n";
<?php
declare(strict_types=1);
function mensagem(int $status, string $corpo): string
{
try {
$dados = json_decode($corpo, true, flags: JSON_THROW_ON_ERROR);
} catch (JsonException) {
return 'Resposta inválida';
}
if ($status !== 200) {
return "Erro $status: {$dados['erro']}";
}
return 'Produto: ' . $dados['nome'];
}
echo mensagem(200, '{"id":2,"nome":"Caneta azul","preco":3.9}'), "\n";
echo mensagem(404, '{"erro":"Produto 9 não encontrado"}'), "\n";
echo mensagem(200, '<br /><b>Warning</b>: Undefined array key'), "\n";
Rodando a versão ingênua no computador da Ana, os avisos são estes (no editor, o nome do arquivo muda):
$ php mensagem.php Produto: Caneta azul Warning: Undefined array key "nome" in /home/ana/cliente/mensagem.php on line 8 Produto: Warning: Trying to access array offset on null in /home/ana/cliente/mensagem.php on line 8 Produto:
Resumo
- Consumir uma API = mandar uma requisição HTTP e decodificar o JSON da resposta com
json_decode($texto, true). file_get_contents($url)faz um GET simples. Comstream_context_createvocê define método, cabeçalhos, corpo,timeouteignore_errors.- cURL:
curl_init→curl_setopt/curl_setopt_array→curl_exec→curl_getinfo. Não precisa (e não deve, no 8.5) chamarcurl_close. false= não houve resposta (conexão, nome, tempo). Status 4xx/5xx = houve resposta, com erro. Trate os dois.- Sempre um tempo limite. Sempre confira o resultado antes de usar. Use
JSON_THROW_ON_ERRORpara não engolir JSON inválido. - O editor do guia não acessa a rede: treine o tratamento das respostas nele e as requisições no seu computador, com a sua própria API no
php -S.