PHP $_SERVER e Outras Superglobais
Você já usou três variáveis "mágicas" que existem sozinhas em qualquer programa PHP: $_GET, $_POST e $_FILES. Elas fazem parte de uma família de nove, as superglobais. Esta aula apresenta a mais curiosa delas, $_SERVER, que conta tudo sobre o pedido que chegou (quem pediu, qual endereço, com qual navegador), e fecha com o mapa da família inteira.
- O que é uma superglobal e por que ela funciona dentro de funções.
- O que tem dentro de
$_SERVER, com a saída real dophp -S. - As chaves mais úteis:
REQUEST_METHOD,REQUEST_URI,QUERY_STRING,SCRIPT_NAME,HTTP_HOST,HTTP_USER_AGENT,REMOTE_ADDR. - Quais delas o visitante pode falsificar (e o caso do
PHP_SELF). - Como
$_SERVERmuda no terminal (php arquivo.php). $_ENV,getenve$GLOBALS.- A tabela com todas as superglobais, e onde cada uma é estudada.
A ideia: o carimbo do correio
Quando uma carta chega, além do conteúdo, o envelope traz informações: o carimbo com a data, a cidade de origem, o endereço de destino, às vezes o remetente. Ninguém escreveu isso na carta; vem "de brinde" com a entrega.
$_GET e $_POST são o conteúdo da carta. $_SERVER é o envelope: o PHP anota nele como o pedido chegou e onde ele está rodando. E, como num envelope, parte dessas anotações foi escrita pelo correio (o servidor, confiável) e parte pelo remetente (o navegador, que pode escrever o que quiser).
O que é uma superglobal
Normalmente, uma variável criada fora de uma função não existe dentro dela (veja Escopo). As superglobais são a exceção: o PHP as cria antes do seu código rodar e elas são visíveis em todo lugar, sem global:
function foiEnviado(): bool
{
return $_SERVER['REQUEST_METHOD'] === 'POST'; // funciona: superglobal
}
Todas começam com $_ e são escritas em maiúsculas (a única exceção é $GLOBALS, sem o sublinhado). Você não pode criar novas superglobais: a família é fixa.
$_SERVER por dentro
A forma mais rápida de conhecer $_SERVER é imprimir tudo. A Ana criou um server.php e abriu no servidor embutido (O Servidor Embutido):
<?php print_r($_SERVER);
$ curl -s localhost:8000/server.php
Array
(
[DOCUMENT_ROOT] => /home/ana/site
[REMOTE_ADDR] => 127.0.0.1
[REMOTE_PORT] => 37966
[SERVER_SOFTWARE] => PHP/8.5.8 (Development Server)
[SERVER_PROTOCOL] => HTTP/1.1
[SERVER_NAME] => localhost
[SERVER_PORT] => 8000
[REQUEST_URI] => /server.php
[REQUEST_METHOD] => GET
[SCRIPT_NAME] => /server.php
[SCRIPT_FILENAME] => /home/ana/site/server.php
[PHP_SELF] => /server.php
[HTTP_HOST] => localhost:8000
[HTTP_USER_AGENT] => curl/8.18.0
[HTTP_ACCEPT] => */*
[REQUEST_TIME_FLOAT] => 1792195356.5285
[REQUEST_TIME] => 1792195356
)
As chaves se dividem em três grupos:
- Sobre o pedido:
REQUEST_METHOD,REQUEST_URI,QUERY_STRING,REQUEST_TIME. - Cabeçalhos que o navegador mandou: todas as que começam com
HTTP_. Cada cabeçalho HTTP vira uma chave:User-Agent→HTTP_USER_AGENT,Accept-Language→HTTP_ACCEPT_LANGUAGE(maiúsculas, hífen vira sublinhado,HTTP_na frente). - Sobre o servidor e o arquivo:
SERVER_NAME,SERVER_PORT,DOCUMENT_ROOT,SCRIPT_FILENAME.
Num Apache ou Nginx, a lista é bem maior (e algumas chaves mudam). O que vale para todos: as chaves abaixo.
As chaves mais úteis
Para ver cada uma com calma, a Ana pediu a página loja/produto.php?id=7&cor=azul como se fosse um Firefox, vindo de um link na página /loja/. O -A do curl define o User-Agent, e o -H acrescenta outros cabeçalhos:
<?php
$chaves = ['REQUEST_METHOD', 'REQUEST_URI', 'QUERY_STRING', 'SCRIPT_NAME', 'PHP_SELF',
'HTTP_HOST', 'HTTP_USER_AGENT', 'HTTP_ACCEPT_LANGUAGE', 'HTTP_REFERER',
'REMOTE_ADDR', 'SERVER_NAME', 'SERVER_PORT', 'SERVER_PROTOCOL', 'DOCUMENT_ROOT', 'SCRIPT_FILENAME', 'HTTPS'];
foreach ($chaves as $c) {
echo str_pad($c, 22), '= ', $_SERVER[$c] ?? '(não existe)', "\n";
}
$ curl -s "localhost:8000/loja/produto.php?id=7&cor=azul" -H "Accept-Language: pt-BR,pt;q=0.9" -H "Referer: http://localhost:8000/loja/" -A "Mozilla/5.0 (X11; Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0" REQUEST_METHOD = GET REQUEST_URI = /loja/produto.php?id=7&cor=azul QUERY_STRING = id=7&cor=azul SCRIPT_NAME = /loja/produto.php PHP_SELF = /loja/produto.php HTTP_HOST = localhost:8000 HTTP_USER_AGENT = Mozilla/5.0 (X11; Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0 HTTP_ACCEPT_LANGUAGE = pt-BR,pt;q=0.9 HTTP_REFERER = http://localhost:8000/loja/ REMOTE_ADDR = 127.0.0.1 SERVER_NAME = localhost SERVER_PORT = 8000 SERVER_PROTOCOL = HTTP/1.1 DOCUMENT_ROOT = /home/ana/site SCRIPT_FILENAME = /home/ana/site/loja/produto.php HTTPS = (não existe)
E a mesma página pedida pelo curl "puro", sem endereço com dados nem cabeçalhos extras. Repare nas chaves que desaparecem:
$ curl -s localhost:8000/loja/produto.php REQUEST_METHOD = GET REQUEST_URI = /loja/produto.php QUERY_STRING = (não existe) … HTTP_USER_AGENT = curl/8.18.0 HTTP_ACCEPT_LANGUAGE = (não existe) HTTP_REFERER = (não existe) …
| Chave | Exemplo | O que é | Quem escreve |
|---|---|---|---|
REQUEST_METHOD | GET | O método do pedido (veja $_GET e $_POST) | Servidor |
REQUEST_URI | /loja/produto.php?id=7&cor=azul | O endereço pedido, a partir da /, com a query string | Visitante (o endereço que ele digitou) |
QUERY_STRING | id=7&cor=azul | Só a parte depois do ?. Não existe quando não há ? | Visitante |
SCRIPT_NAME | /loja/produto.php | O caminho do arquivo PHP no site | Servidor |
PHP_SELF | /loja/produto.php | Parecido com SCRIPT_NAME, mas pode incluir texto extra do endereço (veja o cenário de erro) | Visitante, em parte |
HTTP_HOST | localhost:8000 | O nome do site que o navegador pediu | Visitante |
HTTP_USER_AGENT | Mozilla/5.0 … Firefox/140.0 | "Agente do usuário": o programa que fez o pedido (navegador, robô, curl) | Visitante |
HTTP_ACCEPT_LANGUAGE | pt-BR,pt;q=0.9 | Os idiomas preferidos do navegador | Visitante |
HTTP_REFERER | http://localhost:8000/loja/ | A página de onde a pessoa veio (pelo link). Muitas vezes não vem | Visitante |
REMOTE_ADDR | 127.0.0.1 | O endereço IP de quem fez o pedido (127.0.0.1 = a própria máquina) | Servidor (a conexão) |
SERVER_NAME, SERVER_PORT | localhost, 8000 | Nome e porta do servidor | Servidor |
DOCUMENT_ROOT | /home/ana/site | A pasta raiz do site no disco | Servidor |
SCRIPT_FILENAME | /home/ana/site/loja/produto.php | O caminho completo do arquivo no disco | Servidor |
HTTPS | (não existe) | Existe (com um valor diferente de off) quando a conexão é segura (https://). O php -S não tem HTTPS | Servidor |
REQUEST_TIME | 1792195356 | A hora do pedido, em segundos desde 1970 (veja Datas e Horas) | Servidor |
HTTP_ foi escrito pelo navegador e pode ser qualquer coisa: o curl acima "virou" um Firefox com uma opção. Use esses valores para estatísticas e conveniências (escolher o idioma da página), nunca para segurança ("só deixa entrar se vier do Chrome"). E, ao mostrar qualquer um deles na página, htmlspecialchars (Escapar a Saída). Até o REMOTE_ADDR, que é confiável, pode ser o endereço de um proxy no meio do caminho, e não da pessoa.O $_SERVER do editor do guia. Aqui o "servidor" é o PHP rodando dentro do seu navegador, então alguns valores são diferentes dos do php -S, mas o HTTP_USER_AGENT é o do seu navegador. Rode, envie o formulário (GET e depois POST) e veja REQUEST_METHOD, REQUEST_URI e QUERY_STRING mudarem. Troque a lista para print_r($_SERVER) e veja tudo.
<form method="get"><input name="busca" value="torta"> <button>GET</button></form>
<form method="post"><input name="busca" value="pudim"> <button>POST</button></form>
<pre>
<?php
foreach (['REQUEST_METHOD', 'REQUEST_URI', 'QUERY_STRING', 'SCRIPT_NAME',
'HTTP_HOST', 'HTTP_USER_AGENT', 'REMOTE_ADDR'] as $c) {
echo str_pad($c, 16), '= ', htmlspecialchars($_SERVER[$c] ?? '(não existe)'), "\n";
}
?>
</pre>
Usos comuns
| Para… | Use |
|---|---|
| Saber se um formulário POST foi enviado | $_SERVER['REQUEST_METHOD'] === 'POST' |
| Montar o link da página atual | $_SERVER['REQUEST_URI'] (escapado com htmlspecialchars) |
| Destacar o item atual de um menu | $_SERVER['SCRIPT_NAME'] === '/contato.php' |
| Registrar quem acessou (um log) | REMOTE_ADDR, HTTP_USER_AGENT, REQUEST_TIME |
| Saber se está em HTTPS | ($_SERVER['HTTPS'] ?? 'off') !== 'off' |
| Caminhos de arquivos no disco | Prefira __DIR__ (a pasta do arquivo atual) a DOCUMENT_ROOT |
$_SERVER no terminal
Quando o PHP roda no terminal (php arquivo.php, veja PHP na Linha de Comando), não existe pedido HTTP. Então não há REQUEST_METHOD nem cabeçalhos, mas aparecem os argumentos (argv, argc) e as variáveis de ambiente do sistema:
<?php echo $_SERVER['argv'][1] ?? '-', ' ', $_SERVER['argc'], ' ', $_SERVER['SCRIPT_NAME'], ' ', $_SERVER['REQUEST_METHOD'] ?? '(sem REQUEST_METHOD)', "\n";
$ php cli.php Ana Ana 2 cli.php (sem REQUEST_METHOD)
Por isso, num código que pode rodar dos dois jeitos, leia sempre com ?? ….
$_ENV e getenv: variáveis de ambiente
O sistema operacional guarda variáveis de ambiente: configurações como HOME (a pasta pessoal), LANG (o idioma) e, em servidores, coisas como a senha do banco de dados, para não ficarem escritas no código. O PHP pode lê-las de dois jeitos:
<?php
var_dump(count($_ENV), getenv('HOME'));
$ php env.php int(15) string(9) "/home/ana"
No laboratório da Ana, $_ENV veio cheio porque o PHP está sem php.ini. Nos php.ini que acompanham o PHP (e na maioria dos servidores), a configuração variables_order é "GPCS", sem o E de Environment, e $_ENV fica vazio. Por isso a forma mais segura é getenv('NOME'), que funciona nos dois casos e devolve false se a variável não existir.
$GLOBALS: todas as variáveis globais
$GLOBALS é um array com todas as variáveis criadas fora de funções. Dentro de uma função, ele dá acesso a elas pelo nome:
<?php
$loja = 'Padaria da Ana';
function nomeDaLoja(): string
{
return $GLOBALS['loja'];
}
echo nomeDaLoja(), "\n";
$ php globals.php Padaria da Ana
Funciona, mas cria a mesma bagunça do global (veja Escopo): a função depende de uma variável que pode ser mudada em qualquer lugar. Prefira passar o valor como parâmetro. Desde o PHP 8.1, $GLOBALS não pode mais ser substituído inteiro:
<?php $GLOBALS = [];
$ php globals2.php
Fatal error: $GLOBALS can only be modified using the $GLOBALS[$name] = $value syntax in /home/ana/site/globals2.php on line 2
Stack trace:
#0 {main}
O mapa das superglobais
| Superglobal | O que guarda | De onde vem | Aula |
|---|---|---|---|
$_GET | Dados do endereço (?nome=Ana) | Visitante | $_GET e $_POST |
$_POST | Dados do corpo de um POST | Visitante | $_GET e $_POST |
$_FILES | Arquivos enviados | Visitante | Upload de Arquivos |
$_COOKIE | Cookies que o navegador devolveu | Visitante | Cookies |
$_SESSION | Dados da sessão, guardados no servidor (existe depois de session_start()) | Seu programa | Sessões |
$_REQUEST | Mistura de GET, POST e (talvez) cookies | Visitante | $_GET e $_POST (evite) |
$_SERVER | Informações do pedido e do servidor | Servidor + cabeçalhos do visitante | esta |
$_ENV | Variáveis de ambiente (muitas vezes vazio; use getenv) | Sistema | esta |
$GLOBALS | Todas as variáveis globais do programa | Seu programa | esta e Escopo |
Repare na coluna "De onde vem": tudo o que vem do visitante passa por validação na entrada e escape na saída (Validar Dados, Escapar a Saída).
Quando dá errado
PHP_SELF
<form action="<?= $_SERVER['PHP_SELF'] ?>" method="post">
$ curl -s "localhost:8000/self.php/%22%3E%3Cb%20style=%22color:red%22%3Ehackeado%3C/b%3E" <form action="/self.php/"><b style="color:red">hackeado</b>" method="post">
O que aconteceu: o servidor aceita texto extra depois do nome do arquivo (/self.php/qualquer-coisa ainda roda o self.php), e esse texto entra no PHP_SELF. A aspa fechou o action e o resto virou HTML. Esse código aparece em muitos tutoriais antigos.
Como resolver: para enviar à própria página, deixe o action de fora (Formulários HTML). Se precisar do caminho, use htmlspecialchars($_SERVER['SCRIPT_NAME']).
Warning: Undefined array key "HTTP_REFERER"
$ curl -s localhost:8000/ref.php <br /> <b>Warning</b>: Undefined array key "HTTP_REFERER" in <b>/home/ana/site/ref.php</b> on line <b>2</b><br />
O que aconteceu: chaves HTTP_* só existem se o navegador mandou aquele cabeçalho. O Referer não vem quando a pessoa digita o endereço, abre um favorito ou quando o navegador o esconde por privacidade. O mesmo vale para QUERY_STRING (sem ?) e REQUEST_METHOD (no terminal).
Como resolver: $_SERVER['HTTP_REFERER'] ?? ''.
Warning: Undefined global variable $_SESSION
<?php echo $_SESSION["usuario"];
$ curl -s localhost:8000/sess.php <br /> <b>Warning</b>: Undefined global variable $_SESSION in <b>/home/ana/site/sess.php</b> on line <b>2</b><br /> <br /> <b>Warning</b>: Trying to access array offset on null in <b>/home/ana/site/sess.php</b> on line <b>2</b><br />
O que aconteceu: $_SESSION só passa a existir depois de session_start(). Sem ela, a variável não existe (primeiro aviso) e ler ["usuario"] de "nada" (null) gera o segundo.
Como resolver: chame session_start() no começo do arquivo, como em Sessões.
$_ENV vem vazio no servidor
O que aconteceu: o variables_order do servidor não inclui o E.
Como resolver: troque $_ENV['X'] por getenv('X').
Exercícios
1. O navegador
Complete a chave que guarda o programa que fez o pedido:
2. O endereço pedido
Para o pedido /blog.php?post=3, qual chave vale /blog.php?post=3?
3. Variável de ambiente
Complete com a função que lê a variável de ambiente HOME mesmo quando $_ENV está vazio:
4. Qual destas chaves não pode ser escolhida pelo visitante?
5. Qual superglobal só existe depois de chamar uma função?
6. Por que action="<?= $_SERVER['PHP_SELF'] ?>" é perigoso?
Missão: a página foi aberta com ?pagina=2. Complete o rodapé para mostrar Método: seguido do REQUEST_METHOD e Você pediu: seguido do REQUEST_URI, os dois com htmlspecialchars.
<h1>Receitas</h1> <p>Página de receitas da Ana.</p> <footer> <?php // mostre aqui o método e o endereço pedido ?> </footer>
<h1>Receitas</h1> <p>Página de receitas da Ana.</p> <footer> <?php echo "<p>Método: " . htmlspecialchars($_SERVER['REQUEST_METHOD']) . "</p>"; echo "<p>Você pediu: " . htmlspecialchars($_SERVER['REQUEST_URI']) . "</p>"; ?> </footer>
Missão: mostre Seu navegador: seguido do HTTP_USER_AGENT. Lembre: esse cabeçalho pode não vir (use ?? 'desconhecido') e foi escrito pelo visitante (use htmlspecialchars).
<?php // seu código aqui
<?php $agente = $_SERVER['HTTP_USER_AGENT'] ?? 'desconhecido'; echo "<p>Seu navegador: " . htmlspecialchars($agente) . "</p>";
Resumo
- Superglobais existem em todo lugar, inclusive em funções. São nove:
$_GET,$_POST,$_FILES,$_COOKIE,$_SESSION,$_REQUEST,$_SERVER,$_ENV,$GLOBALS. $_SERVERdescreve o pedido e o servidor:REQUEST_METHOD,REQUEST_URI,QUERY_STRING,SCRIPT_NAME,REMOTE_ADDR…- Cada cabeçalho HTTP vira
HTTP_NOME; todos podem ser falsificados e podem não existir:?? ''ehtmlspecialchars. - Não use
PHP_SELFsem escape; para enviar à própria página, omita oaction. - No terminal,
$_SERVERnão tem dados de pedido, mas temargv/argc. - Para variáveis de ambiente,
getenv.$GLOBALSexiste, mas prefira parâmetros.