No estaba muy contento con el código de la librería reactphp-slim, parecía poco elegante. Así que me he puesto manos a la obra y he decidido cambiar un poco la forma en que trabaja, de paso terminar de añadir cosas que faltaban. He generado un objeto Server encargado de setear los callbacks a ReactPHP para que haga la llamada al método process de la aplicación basada en Slim Framework. He generado dos clases estáticas encargadas del paso de datos entre los objetos request/response de ambos frameworks, siendo el objeto Server el pegamento de todo el paquete. Él se encarga de recoger la request, del arranque de la aplicación y de poblar la response, ocultando las llamadas que nosotros antes implementábamos a mano.

Uso

Ahora es bastante más simple que antes, sencillamente instanciamos Slim/App para hacerle un bootstrap normal, las dependencias, el router, middleware… Después de tener la aplicación preparada, solamente deberemos instanciar el servidor reactphp-slim para setear el puerto a escuchar y hacer el run, esto pondrá nuestra aplicación a la escucha de cualquier petición HTTP.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
require '../vendor/autoload.php';

use \mbarquin\reactSlim;

// We keep a new Slim app instance.
$app = new \Slim\App();

// We add a closure to attend defined request routes
$app->any('/hello/{name}', function (
\Slim\Http\Request $request,
\Slim\Http\Response $response) {

$name = $request->getAttribute('name');

$response->getBody()->write("Hello, $name");
$response->getBody()->write(print_r($request->getParsedBody(), true));
$response->getBody()->write(print_r($request->getCookieParams(), true));
$response->getBody()->write(print_r($request->getHeaders(), true));
// $response->getBody()->write(print_r($request->getUploadedFiles(), true));

return $response;
});

$server = new \mbarquin\reactSlim\Server();

$server->withHost('192.168.67.1')->withPort(1337)->run($app);

La clase Server esta construida de manera que al invocar al método withPort o withHost, este nos devuelve propio objeto para poder concatenar el resto de los métodos.

withHost($string)
Define la IP que el servidor va a estar escuchando, por defecto localhost. Este método retorna la misma instancia Server.

withPort($int)
Configura el puerto al que el servidor va a estar escuchando, por defecto será 1337. Este método retorna la misma instancia Server.

run(\Slim\App $app)
Lanza el proceso de servidor y espera hasta que sea realizada una petición, momento en que lanza la instancia de Slim\App pasada como parámetro.

Uploaded files

Lo más laborioso ha sido imitar el comportamiento para recoger archivos desde los formularios multipart, la verdad que no conocía en profundidad el proceso conjunto que hacían el navegador y servidor para crear los archivos y ponerlos a disposición mediante $_FILES y move_uploaded_files. Los ficheros recibidos no serán validos para las funciones move_uploaded_files ni is_uploaded_file, PHP hace comprobaciones contra los datos de lo que él ha procesado, y estos no entrarían. Aun no lo he podido probar con usuarios concurrentes, el boundary enviado para la segmentación de los datos por parte del navegador sirve como identificador global de las partes de una misma descarga. A falta de más pruebas parece que los archivos recibidos son iguales a los enviados.

Para gestionarlos utilizaremos los objetos nativos de Slim obtenidos mediante $request->getUploadedFiles(), que ya tienen un método (moveTo) para poder copiarlos donde nosotros deseemos.

Conclusión

He actualizado el proceso para poder recoger el contenido del Body, en la anterior versión quedó como algo pendiente, ahora ya se recoge y se pasa al objeto Request de Slim. Lo mismo con las Cookies y Headers, había un bug que evitaba la propagación de las nuevas cabeceras de la response seteadas en la aplicación Slim hacia el navegador, ya corregido.

Es un servidor experimental, pero el proceso de poner en marcha algo funcional esta siendo positiva, me está llevando por derroteros a los que de otra manera creo que no llegaría jamás. Queda pendiente algo de código para emular el comportamiento de las sessions, pero eso ya forma parte de un futuro hipotético…