Sesiones

HTTP es un protocolo sin estado; sin embargo, casi todas las aplicaciones necesitan mantener el estado entre peticiones, como el contenido de un carrito de la compra. Para eso sirven justamente las sesiones. Mostraremos:

  • cómo usar las sesiones
  • cómo evitar conflictos de nombres
  • cómo establecer la expiración

Al usar sesiones, cada usuario recibe un identificador único llamado ID de sesión, que se transmite en una cookie. Este sirve de clave para los datos de la sesión. A diferencia de las cookies, que se guardan en el lado del navegador, los datos de la sesión se guardan en el lado del servidor.

Las sesiones se configuran en la configuración; la elección del tiempo de expiración es especialmente importante.

De la gestión de la sesión se encarga el objeto Nette\Http\Session, al que puede acceder haciendo que se lo pasen mediante inyección de dependencias. En los presenters basta con llamar a $session = $this->getSession().

Instalación y requisitos

Iniciar la sesión

De forma predeterminada, Nette inicia la sesión automáticamente en el momento en que empezamos a leer o escribir datos en ella. Para iniciarla manualmente use $session->start().

Al iniciar la sesión, PHP envía cabeceras HTTP que afectan a la caché (véase session_cache_limiter) y, eventualmente, una cookie con el ID de sesión. Por eso siempre hay que iniciar la sesión antes de enviar cualquier salida al navegador; de lo contrario se lanzará una excepción. Así que, si sabe que durante el renderizado de la página se usará una sesión, iníciela manualmente antes, por ejemplo en el presenter.

En modo de desarrollo, Tracy inicia la sesión porque la usa para mostrar las barras de las redirecciones y las peticiones AJAX en la Tracy Bar.

Secciones

En PHP puro, el almacén de datos de la sesión está implementado como un array accesible mediante la variable global $_SESSION. El problema es que las aplicaciones suelen constar de muchas partes independientes y, si todas ellas tienen a disposición un único array, tarde o temprano se producirá una colisión de nombres.

Nette Framework resuelve este problema dividiendo todo el espacio en secciones (objetos de Nette\Http\SessionSection). Cada unidad usa entonces su propia sección con un nombre único y no puede producirse ninguna colisión.

Obtenemos una sección de la sesión:

$section = $session->getSection('unique name');

En el presenter basta con usar getSession() con un parámetro:

// $this es un Presenter
$section = $this->getSession('unique name');

La existencia de una sección se puede comprobar con el método $session->hasSection('unique name'). La lista de los nombres de todas las secciones existentes la devuelve $session->getSectionNames().

Trabajar con la sección en sí es después facilísimo con los métodos set(), get() y remove():

// escritura de una variable
$section->set('userName', 'john');

// lectura de una variable, devuelve null si no existe
echo $section->get('userName');

// eliminación de una variable
$section->remove('userName');

Para obtener todas las variables de una sección puede usar un bucle foreach:

foreach ($section as $key => $val) {
	echo "$key = $val";
}

Cómo establecer la expiración

La expiración se puede establecer para secciones concretas o incluso para variables concretas. Podemos hacer que el acceso de un usuario caduque a los 20 minutos y recordar aun así el contenido del carrito de la compra.

// la sección expira a los 20 minutos
$section->setExpiration('20 minutes');

Para establecer la expiración de variables concretas use el tercer parámetro del método set():

// la variable 'flash' expira a los 30 segundos
$section->set('flash', $message, '30 seconds');

Recuerde que el tiempo de expiración de toda la sesión (véase la configuración de la sesión) debe ser igual o mayor que el tiempo establecido para las secciones o variables concretas.

Para anular una expiración establecida antes use el método removeExpiration(); para borrar la expiración de una variable concreta, pase su nombre: removeExpiration('flash'). Para eliminar de inmediato toda la sección use el método remove().

Eventos $onStart, $onBeforeWrite

El objeto Nette\Http\Session tiene los eventos $onStart y $onBeforeWrite, así que puede añadir callbacks que se invocan después de iniciar la sesión o antes de escribirla en disco y terminarla a continuación.

$session->onBeforeWrite[] = function () {
	// escribe datos en la sesión
	$this->section->set('basket', $this->basket);
};

Gestión de la sesión

Resumen de los métodos de la clase Nette\Http\Session para gestionar la sesión:

start(): void

Inicia la sesión.

isStarted(): bool

¿Está iniciada la sesión?

close(): void

Termina la sesión. La sesión termina automáticamente al final de la ejecución del script.

destroy(): void

Termina y borra la sesión.

exists(): bool

¿Contiene la petición HTTP una cookie con un ID de sesión?

regenerateId(): void

Genera un nuevo ID de sesión aleatorio. Los datos se conservan.

getId(): string

Devuelve el ID de la sesión.

Configuración

La sesión se configura en la configuración. Si escribe una aplicación que no usa un contenedor DI, use estos métodos para configurarla. Hay que llamarlos antes de iniciar la sesión.

setName (string $name): static

Establece el nombre de la cookie en la que se transmite el ID de sesión. El nombre estándar es PHPSESSID. Es útil si ejecuta varias aplicaciones distintas en el mismo sitio web.

getName(): string

Devuelve el nombre de la cookie en la que se transmite el ID de sesión.

setOptions (array $options)static

Configura la sesión. Se pueden establecer todas las directivas de sesión de PHP (en formato camelCase, p. ej. escriba savePath en lugar de session.save_path) y también readAndClose.

setExpiration (?string $expire)static

Establece el tiempo de inactividad tras el cual expira la sesión.

setCookieParameters (string $path, ?string $domain=null, ?bool $secure=null, SameSite|string|null $samesite=null)static

Establece los parámetros de las cookies. Puede cambiar los valores predeterminados de los parámetros en la configuración.

setSavePath (string $path)static

Establece el directorio donde se guardan los archivos de la sesión.

setHandler (\SessionHandlerInterface $handler)static

Establece un handler propio, véase la documentación de PHP.

La seguridad ante todo

El servidor da por hecho que se comunica con el mismo usuario mientras las peticiones vayan acompañadas del mismo ID de sesión. La tarea de los mecanismos de seguridad es asegurar que así sea de verdad y que el identificador no se pueda robar ni sustituir.

Por eso Nette Framework configura correctamente las directivas de PHP para transmitir el ID de sesión solo en cookies, hacerlo inaccesible para JavaScript e ignorar cualquier identificador que venga en la URL. Además, en momentos críticos, como el inicio de sesión del usuario, genera un nuevo ID de sesión.

Para configurar PHP se usa la función ini_set, pero por desgracia algunos proveedores de hosting prohíben su uso. Si es el caso de su hosting, intente acordar con ellos que le permitan esa función o, al menos, que configuren el servidor correctamente.

versión: 4.x