Uma ideia maluca. Quero fazer um dump no output do meu backend no console do browser. Existem vários dumpers para PHP. Por exemplo, o LadyBug, de Raul Fraile. Também existem bibliotecas que fazem exatamente o que eu quero, como a Chrome Logger. Mas eu queria usar Websockets e fazer o dump de valores em tempo real, sem ter que esperar o final do script do backend. Por quê? Uma simples razão: porque sim! 🙂
Já escrevi diversos artigos sobre Websockets, Silex, PHP. Neste caso, vou usar uma abordagem similar à desse artigo anterior. Primeiramente, criei um servidor Websocket simples com socket.io. Esse servidor também inicia um servidor Express para fazer o handle de mensagens internas do Backend do Silex.
var CONF = { IO: {HOST: '0.0.0.0', PORT: 8888}, EXPRESS: {HOST: '0.0.0.0', PORT: 26300} }, express = require('express'), expressApp = express(), server = require('http').Server(expressApp), io = require('socket.io')(server, {origins: 'localhost:*'}) ; expressApp.get('/:type/:session/:message', function (req, res) { console.log(req.params); var session = req.params.session, type = req.params.type, message = req.params.message; io.sockets.emit('dumper.' + session, {title: type, data: JSON.parse(message)}); res.json('OK'); }); io.sockets.on('connection', function (socket) { console.log("Socket connected!"); }); expressApp.listen(CONF.EXPRESS.PORT, CONF.EXPRESS.HOST, function () { console.log('Express started'); }); server.listen(CONF.IO.PORT, CONF.IO.HOST, function () { console.log('IO started'); });
Agora, criaremos um provedor de serviços simples para conectar o backend do Silex ao nosso servidor Express (e enviar mensagens do dumper usando a conexão do websocket).
<?php namespace Dumper\Silex\Provider; use Silex\Application; use Silex\ServiceProviderInterface; use Dumper\Dumper; use Silex\Provider\SessionServiceProvider; use GuzzleHttp\Client; class DumperServiceProvider implements ServiceProviderInterface { private $wsConnector; private $client; public function __construct(Client $client, $wsConnector) { $this->client = $client; $this->wsConnector = $wsConnector; } public function register(Application $app) { $app->register(new SessionServiceProvider()); $app['dumper'] = function () use ($app) { return new Dumper($this->client, $this->wsConnector, $app['session']->get('uid')); }; $app['dumper.init'] = $app->protect(function ($uid) use ($app) { $app['session']->set('uid', $uid); }); $app['dumper.uid'] = function () use ($app) { return $app['session']->get('uid'); }; } public function boot(Application $app) { } }
Finalmente, nossa aplicação Silex vai se parecer com isto:
include __DIR__ . '/../vendor/autoload.php'; use Silex\Application; use Silex\Provider\TwigServiceProvider; use Dumper\Silex\Provider\DumperServiceProvider; use GuzzleHttp\Client; $app = new Application([ 'debug' => true ]); $app->register(new DumperServiceProvider(new Client(), 'http://192.168.1.104:26300')); $app->register(new TwigServiceProvider(), [ 'twig.path' => __DIR__ . '/../views', ]); $app->get("/", function (Application $app) { $uid = uniqid(); $app['dumper.init']($uid); return $app['twig']->render('index.twig', [ 'uid' => $uid ]); }); $app->get('/api/hello', function (Application $app) { $app['dumper']->error("Hello world1"); $app['dumper']->info([1,2,3]); return $app->json('OK'); }); $app->run();
No lado do cliente, temos um index.html. Eu criei um template Twig para passar uid para o objeto dumper (o canal do websocket a ser ouvido), mas nós também podemos buscar este uid do backend com um call ajax.
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>Dumper example</title> </head> <body> <a href="#" onclick="api('hello')">hello</a> <!-- We use jQuery just for the demo. Library doesn't need jQuery --> <script src="assets/jquery/dist/jquery.min.js"></script> <!-- We load the library --> <script src="js/dumper.js"></script> <script> dumper.startSocketIo('{{ uid }}', '//localhost:8888'); function api(name) { // we perform a remote api ajax call that triggers websockets $.getJSON('/api/' + name, function (data) { // Doing nothing. We only call the api to test php dumper }); } </script> </body> </html>
Eu uso jQuery para lidar com solicitações ajax e para fazer a conexão ao objeto dumper do websocket (ele não depende do jQuery, apenas do socket.io).
var dumper = (function () { var socket, sessionUid, socketUri, init; init = function () { if (typeof(io) === 'undefined') { setTimeout(init, 100); } else { socket = io(socketUri); socket.on('dumper.' + sessionUid, function (data) { console.group('Dumper:', data.title); switch (data.title) { case 'emergency': case 'alert': case 'critical': case 'error': console.error(data.data); break; case 'warning': console.warn(data.data); break; case 'notice': case 'info': //case 'debug': console.info(data.data); break; default: console.log(data.data); } console.groupEnd(); }); } }; return { startSocketIo: function (uid, uri) { var script = document.createElement('script'); var node = document.getElementsByTagName('script')[0]; sessionUid = uid; socketUri = uri; script.src = socketUri + '/socket.io/socket.io.js'; node.parentNode.insertBefore(script, node); init(); } }; })();
O código-fonte está disponível no meu GitHub.
***
Gonzalo Ayuso faz parte do time de colunistas internacionais do iMasters. A tradução do artigo é feita pela redação iMasters, com autorização do autor, e você pode acompanhar o artigo em inglês no link: http://gonzalo123.com/2015/05/11/php-dumper-using-websockets/