Eventos enviados por el servidor
1Presentación
Esta página documenta la funcionalidad de eventos enviados por el servidor de Temma, que permite una comunicación unidireccional (del servidor al navegador) en tiempo real.
Cuando un código Javascript se conecta a una URL para recibir SSE (eventos enviados por el servidor), la conexión permanece abierta, de forma similar a un websocket. Si la conexión se corta, el cliente se reconectará automáticamente.
Temma ha introducido un nuevo tipo de controlador, el controlador de eventos, derivado del objeto básico \Temma\Web\EventController. Estos controladores solo pueden usarse para procesar SSE, y presentan algunas diferencias respecto a los controladores convencionales (que derivan de \Temma\Web\Controller):
- Están diseñados para ejecutarse durante el tiempo que sea necesario.
- No ejecutan una vista al final de la ejecución, ya que envían mensajes de eventos durante la ejecución.
- Sin vista = sin plantilla = sin variables de plantilla.
- Cuando se define un valor (de la misma forma en que normalmente se definen las variables de plantilla), se envía inmediatamente al cliente, que lo recibe como un evento.
Ten en cuenta que puede haber un gran número de conexiones abiertas simultáneamente (tantas como clientes conectados al sitio). Comprueba que tu servidor acepta un número suficiente de conexiones simultáneas.
2Ejemplo
Imaginemos una página web muy sencilla. Incluye un código Javascript que se conectará a la URL /message/fetch, y mostrará en una lista los eventos del canal "demo".
Aquí está el código HTML de la página:
<html>
<head>
<!-- carga del código Javascript -->
<script src="event-manager.js"></script>
</head>
<body>
<!-- lista que contiene los mensajes de eventos -->
<ul id="liste">
</ul>
</body>
</html>
Y el código Javascript (archivo event-manager.js):
// conexión al flujo de eventos
const evtSource = new EventSource("/message/fetch");
// procesa los eventos entrantes en el canal "demo"
evtSource.addEventListener("demo", function(event) {
// recupera el texto (serializado en JSON en el evento)
const str = JSON.parse(event.data);
// crea un nuevo elemento de la lista
const newElement = document.createElement("li");
// añade el texto del evento como contenido del elemento de la lista
newElement.textContent = str;
// añade el elemento a la lista (en la página HTML)
document.getElementById("liste").appendChild(newElement);
});
Aquí está el código del controlador que enviará los eventos (envía un mensaje de texto cada 2 segundos):
// Controlador Message
class Message extends \Temma\Web\EventController {
// acción fetch
public function fetch() {
$i = 1;
// bucle infinito
while (true) {
// envía un evento en el canal "demo"
$this['demo'] = "Mensaje n°$i";
// incrementa el contador
$i++;
// espera 2 segundos antes de enviar el siguiente mensaje
sleep(2);
}
}
}
Cada dos segundos, el controlador enviará un evento, y el cliente añadirá el mensaje del evento a la lista en la página web.
3Principios de los controladores de eventos
Aparte de las características específicas mencionadas anteriormente (sin plantilla ni vista), los controladores de eventos funcionan de forma muy similar a los controladores convencionales. De hecho, el objeto \Temma\Web\EventController hereda de \Temma\Web\Controller.
Así, los conceptos de acción raíz, acción proxy y acción por defecto siguen siendo idénticos, al igual que la inicialización y la finalización del controlador (consulta la documentación de los controladores).
Los plugins también funcionan de la misma manera. Ten en cuenta, sin embargo, que los post-plugins no se procesarán cada vez que se envíe un evento, sino solo al final de la ejecución (teniendo en cuenta que la ejecución puede ser interrumpida por el servidor debido a un tiempo de ejecución excesivamente largo).
4Envío de eventos
Como se vio en el ejemplo anterior, para enviar un evento basta con definir un valor de la misma manera en que normalmente se define una variable de plantilla, usando la escritura de array asociativo. La clave es el nombre del canal de eventos, y el valor puede contener cualquier dato PHP (que se serializará en JSON).
Ejemplos:
// envía dos mensajes de texto en el canal "msg"
$this['msg'] = "Primer mensaje";
$this['msg'] = "Segundo mensaje";
// envía una lista de elementos en el canal "update"
$this['update'] = [
123 => [
'id' => 123,
'name' => "Proyecto Neptuno",
'author' => "Jane Doe",
],
456 => [
'id' => 456,
'name' => "Proyecto Plutón",
'author' => "John Doe",
],
];
Cada vez que se envían datos, el controlador comprueba automáticamente primero si la conexión con el cliente sigue abierta. Si no es el caso, se lanza una excepción \Temma\Exceptions\FlowQuit; si no se intercepta, esto provocará una interrupción del procesamiento (consulta la documentación del flujo de ejecución).
Si el mensaje pudo enviarse, se extiende el tiempo máximo de ejecución del script. El valor utilizado es el configurado en la directiva max_execution_time del archivo php.ini. Si este valor no está definido, el tiempo máximo de ejecución se establece en 30 segundos. Si este valor se establece en cero (tiempo ilimitado), no se modifica.
5Gestión de canales de eventos
Es posible saber si se han enviado mensajes en un canal y, en caso afirmativo, cuántos.
Ejemplos:
// ¿se ha usado ya un canal?
if (isset($this['myChannel']))
print("El canal 'myChannel' ha sido usado.");
// recupera el número de eventos enviados en un canal
$cnt = $this['myChannel'];
// reinicia un canal (como si nunca se hubiera usado)
unset($this['myChannel']);
6Métodos disponibles en los controladores de eventos
Los controladores de eventos ofrecen métodos específicos que pueden usarse en el código de las acciones.
$this->_checkConnection()
Comprueba que la conexión con el cliente sigue abierta. Si no es así, se lanza una excepción
\Temma\Exceptions\FlowQuit (consulta la documentación del flujo de ejecución).
$this->_renewTimeLimit()
Este método primero llama al método _checkConnection(), y luego extiende el tiempo máximo de
ejecución del script. El valor utilizado es el configurado en la directiva
max_execution_time del archivo php.ini. Si este valor no
está definido, el tiempo máximo de ejecución se establece en 30 segundos.
Si este valor se establece en cero (tiempo ilimitado), no se modifica.
$this->_ping()
Envía un evento en el canal ping, que contiene un array asociativo cuya clave time
contiene la fecha y hora actuales en formato ISO 8601.