Back-End

1 set, 2015

PHP Dumper usando Websockets

100 visualizações
Publicidade

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/