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().

Instalação e requisitos

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.

versão: 4.0