Back-End

1 set, 2015

PHP Dumper usando Websockets

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/