Sessões
HTTP é um protocolo sem estado, no entanto, quase toda aplicação precisa manter o estado entre as requisições, por exemplo, o conteúdo de um carrinho de compras. É exatamente para isso que servem as sessões. Vamos mostrar:
- como usar sessões
- como evitar conflitos de nomes
- como definir a expiração
Ao usar sessões, cada utilizador recebe um identificador único chamado ID de sessão, que é passado num cookie. Ele serve como chave para os dados da sessão. Ao contrário dos cookies, que são armazenados no lado do navegador, os dados da sessão são armazenados no lado do servidor.
Configuramos a sessão na configuração, a escolha do tempo de expiração é especialmente importante.
O gerenciamento da sessão é feito pelo objeto Nette\Http\Session, ao qual pode aceder solicitando-o por
meio de injeção de dependência. Nos
presenters, basta chamar $session = $this->getSession()
.
Iniciar sessão
Nette, por padrão, inicia automaticamente a sessão no momento em que começamos a ler ou escrever dados nela. Manualmente, a
sessão é iniciada usando $session->start()
.
O PHP envia cabeçalhos HTTP que afetam o cache ao iniciar a sessão, veja session_cache_limiter, e possivelmente também um cookie com o ID da sessão. Portanto, é sempre necessário iniciar a sessão antes de enviar qualquer saída para o navegador, caso contrário, uma exceção será lançada. Se sabe que a sessão será usada durante a renderização da página, inicie-a manualmente antes, por exemplo, no presenter.
No modo de desenvolvimento, o Tracy inicia a sessão porque a usa para exibir barras com redirecionamentos e requisições AJAX na Tracy Bar.
Seções
Em PHP puro, o armazenamento de dados da sessão é implementado como um array acessível através da variável global
$_SESSION
. O problema é que as aplicações geralmente consistem em várias partes independentes e, se todas
tiverem acesso a apenas um array, mais cedo ou mais tarde ocorrerá uma colisão de nomes.
O Nette Framework resolve o problema dividindo todo o espaço em seções (objetos Nette\Http\SessionSection). Cada unidade então usa a sua própria seção com um nome exclusivo e nenhuma colisão pode mais ocorrer.
Obtemos a seção da sessão:
$section = $session->getSection('nome unico');
No presenter, basta usar getSession()
com um parâmetro:
// $this é Presenter
$section = $this->getSession('nome unico');
A existência da seção pode ser verificada com o método $session->hasSection('nomeUnico')
.
Trabalhar com a própria seção é então muito fácil usando os métodos set()
, get()
e
remove()
:
// escrever variável
$section->set('userName', 'franta');
// ler variável, retorna null se não existir
echo $section->get('userName');
// cancelar variável
$section->remove('userName');
Para obter todas as variáveis da seção, é possível usar o ciclo foreach
:
foreach ($section as $key => $val) {
echo "$key = $val";
}
Definir expiração
É possível definir a expiração para seções individuais ou até mesmo variáveis individuais. Podemos, assim, deixar a sessão do utilizador expirar em 20 minutos, mas ainda lembrar o conteúdo do carrinho.
// a seção expirará após 20 minutos
$section->setExpiration('20 minutes');
Para definir a expiração de variáveis individuais, serve o terceiro parâmetro do método set()
:
// a variável 'flash' expirará em 30 segundos
$section->set('flash', $message, '30 seconds');
Não se esqueça que o tempo de expiração de toda a sessão (veja configuração da sessão) deve ser igual ou maior que o tempo definido para seções ou variáveis individuais.
A remoção da expiração definida anteriormente é feita pelo método removeExpiration()
. A remoção imediata
de toda a seção é garantida pelo método remove()
.
Eventos $onStart, $onBeforeWrite
O objeto Nette\Http\Session
possui os eventos
$onStart
e $onBeforeWrite
, então pode adicionar callbacks que serão chamados após o início da
sessão ou antes da sua escrita no disco e subsequente encerramento.
$session->onBeforeWrite[] = function () {
// escrevemos dados na sessão
$this->section->set('basket', $this->basket);
};
Gerenciamento de sessão
Visão geral dos métodos da classe Nette\Http\Session
para gerenciamento de sessão:
start(): void
Inicia a sessão.
isStarted(): bool
A sessão está iniciada?
close(): void
Encerra a sessão. A sessão é encerrada automaticamente no final da execução do script.
destroy(): void
Encerra e exclui a sessão.
exists(): bool
A requisição HTTP contém um cookie com o ID da sessão?
regenerateId(): void
Gera um novo ID de sessão aleatório. Os dados permanecem preservados.
getId(): string
Retorna o ID da sessão.
Configuração
Configuramos a sessão na configuração. Se está a escrever uma aplicação que não usa um contêiner DI, estes métodos são usados para configuração. Devem ser chamados antes de iniciar a sessão.
setName (string $name): static
Define o nome do cookie no qual o ID da sessão é transmitido. O nome padrão é PHPSESSID
. É útil caso
execute várias aplicações diferentes no mesmo site.
getName(): string
Retorna o nome do cookie no qual o ID da sessão é transmitido.
setOptions (array $options): static
Configura a sessão. É possível definir todas as diretivas
de sessão do PHP (no formato camelCase, por exemplo, em vez de session.save_path
escrevemos
savePath
) e também readAndClose.
setExpiration (?string $time): static
Define o tempo de inatividade após o qual a sessão expira.
setCookieParameters (string $path, ?string $domain=null, ?bool $secure=null, ?string $samesite=null): static
Define os parâmetros para o cookie. Os valores padrão dos parâmetros podem ser alterados na configuração.
setSavePath (string $path): static
Define o diretório onde os ficheiros de sessão são armazenados.
setHandler (\SessionHandlerInterface $handler): static
Define um manipulador personalizado, veja a documentação do PHP.
Segurança em primeiro lugar
O servidor assume que está a comunicar sempre com o mesmo utilizador, desde que as requisições sejam acompanhadas pelo mesmo ID de sessão. A tarefa dos mecanismos de segurança é garantir que isso realmente aconteça e que não seja possível roubar ou falsificar o identificador.
O Nette Framework, portanto, configura corretamente as diretivas PHP para que o ID da sessão seja transmitido apenas em cookies, o torne inacessível ao JavaScript e ignore quaisquer identificadores na URL. Além disso, em momentos críticos, como o login do utilizador, ele gera um novo ID de sessão.
Para configurar o PHP, usa-se a função ini_set, que infelizmente alguns provedores de hospedagem proíbem. Se este for o caso do seu provedor, tente negociar com ele para permitir a função ou pelo menos configurar o servidor.