Sharing authentication between socket.io and a PHP frontend (using JSON Web Tokens)

I’ve written a previous post about Sharing authentication between socket.io and a PHP frontend but after publish the post a colleague (hi @mariotux) told me that I can use JSON Web Tokens (jwt) to do this. I had never used jwt before so I decided to study a little bit.

JWT are pretty straightforward. You only need to create the token and send it to the client. You don’t need to store this token within a database. Client can decode and validate it on its own. You also can use any programming language to encode and decode tokens (jwt is available in the most common ones)

We’re going to create the same example than the previous post. Today, with jwt, we don’t need to pass the PHP session and perform a http request to validate it. We’ll only pass the token. Our nodejs server will validate by its own.

[sourcecode language=”js”]
var io = require(‘socket.io’)(3000),
jwt = require(‘jsonwebtoken’),
secret = "my_super_secret_key";

// middleware to perform authorization
io.use(function (socket, next) {
var token = socket.handshake.query.token,
decodedToken;
try {
decodedToken = jwt.verify(token, secret);
console.log("token valid for user", decodedToken.user);
socket.connectedUser = decodedToken.user;
next();
} catch (err) {
console.log(err);
next(new Error("not valid token"));
//socket.disconnect();
}
});

io.on(‘connection’, function (socket) {
console.log(‘Connected! User: ‘, socket.connectedUser);
});
[/sourcecode]

That’s the client:

[sourcecode language=”html”]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
Welcome {{ user }}!

<script src="http://localhost:3000/socket.io/socket.io.js"></script&gt;
<script src="/assets/jquery/dist/jquery.js"></script>

<script>
var socket;
$(function () {
$.getJSON("/getIoConnectionToken", function (jwt) {
socket = io(‘http://localhost:3000&#8217;, {
query: ‘token=’ + jwt
});

socket.on(‘connect’, function () {
console.log("connected!");
});

socket.on(‘error’, function (err) {
console.log(err);
});
});
});
</script>

</body>
</html>
[/sourcecode]

And here the backend. A simple Silex server very similar than the previous post one. JWT has also several reserved claims. For example “exp” to set up an expiration timestamp. It’s very useful. We only set one value and validator will reject tokens with incorrect timestamp. In this example I’m not using expiration date. That’s means that my token will never expires. And never means never. In my first prototype I set up an small expiration date (10 seconds). That means my token is only available during 10 seconds. Sounds great. My backend generate tokens that are going to be used immediately. That’s the normal situation but, what happens if I restart the socket.io server? The client will try to reconnect again using the token but it’s expired. We’ll need to create a new jwt before reconnecting. Because of that I’ve removed expiration date in this example but remember: Without expiration date your generated tokens will be always valid (al always is a very big period of time)

[sourcecode language=”php”]
<?php
include __DIR__ . "/../vendor/autoload.php";

use Firebase\JWT\JWT;
use Silex\Application;
use Silex\Provider\SessionServiceProvider;
use Silex\Provider\TwigServiceProvider;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;

$app = new Application([
‘secret’ => "my_super_secret_key",
‘debug’ => true
]);
$app->register(new SessionServiceProvider());
$app->register(new TwigServiceProvider(), [
‘twig.path’ => __DIR__ . ‘/../views’,
]);

$app->get(‘/’, function (Application $app) {
return $app[‘twig’]->render(‘home.twig’);
});
$app->get(‘/login’, function (Application $app) {
$username = $app[‘request’]->server->get(‘PHP_AUTH_USER’, false);
$password = $app[‘request’]->server->get(‘PHP_AUTH_PW’);
if (‘gonzalo’ === $username && ‘password’ === $password) {
$app[‘session’]->set(‘user’, [‘username’ => $username]);

return $app->redirect(‘/private’);
}
$response = new Response();
$response->headers->set(‘WWW-Authenticate’, sprintf(‘Basic realm="%s"’, ‘site_login’));
$response->setStatusCode(401, ‘Please sign in.’);

return $response;
});

$app->get(‘/getIoConnectionToken’, function (Application $app) {
$user = $app[‘session’]->get(‘user’);
if (null === $user) {
throw new AccessDeniedHttpException(‘Access Denied’);
}

$jwt = JWT::encode([
// I can use "exp" reserved claim. It’s cool. My connection token is only available
// during a period of time. The problem is if I restart the io server. Client will
// try to re-connect using this token and it’s expired.
//"exp" => (new \DateTimeImmutable())->modify(‘+10 second’)->getTimestamp(),
"user" => $user
], $app[‘secret’]);

return $app->json($jwt);
});

$app->get(‘/private’, function (Application $app) {
$user = $app[‘session’]->get(‘user’);

if (null === $user) {
throw new AccessDeniedHttpException(‘Access Denied’);
}

$userName = $user[‘username’];

return $app[‘twig’]->render(‘private.twig’, [
‘user’ => $userName
]);
});
$app->run();
[/sourcecode]

Full project in my github.

Sharing authentication between socket.io and a PHP frontend

Normally, when I work with websockets, my stack is a socket.io server and a Silex frontend. Protect a PHP frontend with one kind of authentication of another is pretty straightforward. But if we want to use websockets, we need to set up another server and if we protect our frontend we need to protect our websocket server too.

If our frontend is node too (express for example), sharing authentication is more easy but at this time we we want to use two different servers (a node server and a PHP server). I’ve written about it too but today we`ll see another solution. Let’s start.

Imagine we have this simple Silex application. It has three routes:

  • “/” a public route
  • “/login” to perform the login action
  • “/private” a private route. If we try to get here without a valid session we’ll get a 403 error

And this is the code. It’s basically one example using sessions taken from Silex documentation:

[sourcecode language=”php”]
use Silex\Application;
use Silex\Provider\SessionServiceProvider;
use Silex\Provider\TwigServiceProvider;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;

$app = new Application();

$app->register(new SessionServiceProvider());
$app->register(new TwigServiceProvider(), [
‘twig.path’ => __DIR__ . ‘/../views’,
]);

$app->get(‘/’, function (Application $app) {
return $app[‘twig’]->render(‘home.twig’);
});

$app->get(‘/login’, function () use ($app) {
$username = $app[‘request’]->server->get(‘PHP_AUTH_USER’, false);
$password = $app[‘request’]->server->get(‘PHP_AUTH_PW’);

if (‘gonzalo’ === $username && ‘password’ === $password) {
$app[‘session’]->set(‘user’, [‘username’ => $username]);

return $app->redirect(‘/private’);
}

$response = new Response();
$response->headers->set(‘WWW-Authenticate’, sprintf(‘Basic realm="%s"’, ‘site_login’));
$response->setStatusCode(401, ‘Please sign in.’);

return $response;
});

$app->get(‘/private’, function () use ($app) {
$user = $app[‘session’]->get(‘user’);
if (null === $user) {
throw new AccessDeniedHttpException(‘Access Denied’);
}

return $app[‘twig’]->render(‘private.twig’, [
‘username’ => $user[‘username’]
]);
});

$app->run();
[/sourcecode]

Our “/private” route also creates a connection with our websocket server.

[sourcecode language=”html”]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
Welcome {{ username }}!

<script src="http://localhost:3000/socket.io/socket.io.js"></script&gt;
<script>
var socket = io(‘http://localhost:3000/&#8217;);
socket.on(‘connect’, function () {
console.log("connected!");
});
socket.on(‘disconnect’, function () {
console.log("disconnected!");
});
</script>

</body>
</html>
[/sourcecode]

And that’s our socket.io server. A really simple one.

[sourcecode language=”js”]
var io = require(‘socket.io’)(3000);
[/sourcecode]

It works. Our frontend is protected. We need to login with our credentials (in this example “gonzalo/password”), but everyone can connect to our socket.io server. The idea is to use our PHP session to protect our socket.io server too. In fact is very easy how to do it. First we need to pass our PHPSESSID to our socket.io server. To do it, when we perform our socket.io connection in the frontend, we pass our session id
[sourcecode language=”html”]
<script>
var socket = io(‘http://localhost:3000/&#8217;, {
query: ‘token={{ sessionId }}’
});
socket.on(‘connect’, function () {
console.log("connected!");
});
socket.on(‘disconnect’, function () {
console.log("disconnect!");
});
</script>
[/sourcecode]

As well as we’re using a twig template we need to pass sessionId variable

[sourcecode language=”php”]
$app->get(‘/private’, function () use ($app) {
$user = $app[‘session’]->get(‘user’);
if (null === $user) {
throw new AccessDeniedHttpException(‘Access Denied’);
}

return $app[‘twig’]->render(‘private.twig’, [
‘username’ => $user[‘username’],
‘sessionId’ => $app[‘session’]->getId()
]);
});
[/sourcecode]

Now we only need to validate the token before stabilising connection. Socket.io provides us a middleware to perform those kind of operations. In this example we’re using PHP sessions out of the box. How can we validate it? The answer is easy. We only need to create a http client (in the socket.io server) and perform a request to a protected route (we’ll use “/private”). If we’re using a different provider to store our sessions (I hope you aren’t using Memcached to store PHP session, indeed) you’ll need to validate our sessionId against your provider.

[sourcecode language=”js”]
var io = require(‘socket.io’)(3000),
http = require(‘http’);

io.use(function (socket, next) {
var options = {
host: ‘localhost’,
port: 8080,
path: ‘/private’,
headers: {Cookie: ‘PHPSESSID=’ + socket.handshake.query.token}
};

http.request(options, function (response) {
response.on(‘error’, function () {
next(new Error("not authorized"));
}).on(‘data’, function () {
next();
});
}).end();
});

io.on(‘connection’, function () {
console.log("connected!");
});
[/sourcecode]

Ok. This example works but we’re generating dynamically a js file injecting our PHPSESSID. If we want to extract the sessionId from the request we can use document.cookie but sometimes it doesn’t work. That’s because HttpOnly. HttpOnly is our friend if we want to protect our cookies against XSS attacks but in this case our protection difficults our task.

We can solve this problem performing a simple request to our server. We’ll create a new route (a private route) called ‘getSessionID’ that gives us our sessionId.
[sourcecode language=”php”]
$app->get(‘/getSessionID’, function (Application $app) {
$user = $app[‘session’]->get(‘user’);
if (null === $user) {
throw new AccessDeniedHttpException(‘Access Denied’);
}

return $app->json($app[‘session’]->getId());
});
[/sourcecode]

So before establishing the websocket we just need to create a GET request to our new route to obtain the sessionID.

[sourcecode language=”js”]
var io = require(‘socket.io’)(3000),
http = require(‘http’);

io.use(function (socket, next) {
var sessionId = socket.handshake.query.token,
options = {
host: ‘localhost’,
port: 8080,
path: ‘/getSessionID’,
headers: {Cookie: ‘PHPSESSID=’ + sessionId}
};

http.request(options, function (response) {
response.on(‘error’, function () {
next(new Error("not authorized"));
});
response.on(‘data’, function (chunk) {
var sessionIdFromRequest;
try {
sessionIdFromRequest = JSON.parse(chunk.toString());
} catch (e) {
next(new Error("not authorized"));
}

if (sessionId == sessionIdFromRequest) {
next();
} else {
next(new Error("not authorized"));
}
});
}).end();
});

io.on(‘connection’, function (socket) {
setInterval(function() {
socket.emit(‘hello’, {hello: ‘world’});
}, 1000);
});
[/sourcecode]

And thats all. You can see the full example in my github account.

PHP Gearman Wrapper

I must admit that nowadays I’m using RabbitMQ more than Gearman but I’m still a big fan of gearman. PHP has a great api to connect to gearman work server but sometimes I miss another, how to say, “clean” way. Because of that I’ve creates a gearman wrapper. Let’s start.

I want to cover different areas: Workers, clients, background clients, and tasks.

Worker example:
[sourcecode language=”php”]
use G\Gearman\Builder;

$worker = Builder::createWorker();

$worker->on("slow.process", function ($response, \GearmanJob $job) {
echo "Response: {$response} unique: {$job->unique()}\n";
// we emulate a slow process with a sleep
sleep(2);

return $job->unique();
});

$worker->on("fast.process", function ($response, \GearmanJob $job) {
echo "Response: {$response} unique: {$job->unique()}\n";

return $job->unique();
});

$worker->on("exception.process", function () {
// we emulate a failing process
throw new \Exception("Something wrong happens");
});

$worker->run();
[/sourcecode]

And a client:
[sourcecode language=”php”]
use G\Gearman\Builder;

$client = Builder::createClient();

$client->onSuccess(function ($response) {
echo $response;
});

$client->doNormal(‘fast.process’, "Hello");
[/sourcecode]

One background client
[sourcecode language=”php”]
use G\Gearman\Builder;

$client = Builder::createClient();

$client->doBackground(‘slow.process’, "Hello1");
$client->doBackground(‘slow.process’, "Hello2");
$client->doBackground(‘slow.process’, "Hello3");
[/sourcecode]

And finally, tasks
[sourcecode language=”php”]
use G\Gearman\Builder;

$tasks = Builder::createTasks();

$tasks->onSuccess(function (\GearmanTask $task, $context) {
$out = is_callable($context) ? $context($task) : $task->data();
echo "onSuccess response: " . $out . " id: {$task->unique()}\n";
});

$tasks->onException(function (\GearmanTask $task) {
echo "onException response {$task->data()}\n";
});

$responseParser = function (\GearmanTask $task) {
return "Hello " . $task->data();
};

$tasks->addTask(‘fast.process’, "fast1", $responseParser, ‘g1’);
$tasks->addTaskHigh(‘slow.process’, "slow1", null, ‘xxxx’);
$tasks->addTask(‘fast.process’, "fast2");
$tasks->addTask(‘exception.process’, ‘hi’);

$tasks->runTasks();
[/sourcecode]

The library is just a wrapper to the official api. I’ve create a builder to simplify the creation of the instances:

[sourcecode language=”php”]
namespace G\Gearman;
class Builder
{
static function createWorker($servers=null)
{
$worker = new \GearmanWorker();
$worker->addServers($servers);
return new Worker($worker);
}
static function createClient($servers=null)
{
$client = new \GearmanClient();
$client->addServers($servers);
return new Client($client);
}
static function createTasks($servers=null)
{
$client = new \GearmanClient();
$client->addServers($servers);
return new Tasks($client);
}
}
[/sourcecode]

that’s the worker wrapper
[sourcecode language=”php”]
namespace G\Gearman;
class Worker
{
private $worker;
public function __construct(\GearmanWorker $worker)
{
$this->worker = $worker;
}
public function on($name, callable $callback, $context = null, $timeout = 0)
{
$this->worker->addFunction($name, function (\GearmanJob $job) use ($callback) {
return call_user_func($callback, json_decode($job->workload()), $job);
}, $context, $timeout);
}
public function run()
{
try {
$this->loop();
} catch (\Exception $e) {
echo $e->getMessage() . "\n";
$this->run();
}
}
private function loop()
{
while ($this->worker->work()) {
}
}
}
[/sourcecode]

Now the client one
[sourcecode language=”php”]
namespace G\Gearman;
class Client
{
private $onSuccessCallback;
private $client;
public function __construct(\GearmanClient $client)
{
$this->client = $client;
}
public function doHigh($name, $workload=null, $unique = null)
{
return $this->doAction(__FUNCTION__, $name, $workload, $unique);
}
public function doNormal($name, $workload=null, $unique = null)
{
return $this->doAction(__FUNCTION__, $name, $workload, $unique);
}
public function doLow($name, $workload=null, $unique = null)
{
return $this->doAction(__FUNCTION__, $name, $workload, $unique);
}
public function doBackground($name, $workload=null, $unique = null)
{
return $this->doAction(__FUNCTION__, $name, $workload, $unique);
}
public function doHighBackground($name, $workload=null, $unique = null)
{
return $this->doAction(__FUNCTION__, $name, $workload, $unique);
}
public function doLowBackground($name, $workload=null, $unique = null)
{
return $this->doAction(__FUNCTION__, $name, $workload, $unique);
}
private function doAction($action, $name, $workload=null, $unique)
{
$workload = (string)$workload;
$handle = $this->client->$action($name, json_encode($workload), $unique);
$returnCode = $this->client->returnCode();
if ($returnCode != \GEARMAN_SUCCESS) {
throw new \Exception($this->client->error(), $returnCode);
} else {
if ($this->onSuccessCallback) {
return call_user_func($this->onSuccessCallback, $handle);
}
}
return null;
}
public function onSuccess(callable $callback)
{
$this->onSuccessCallback = $callback;
}
}
[/sourcecode]

and finally the tasks
[sourcecode language=”php”]
namespace G\Gearman;
class Tasks
{
private $client;
private $tasks;
public function __construct(\GearmanClient $client)
{
$this->tasks = [];
$this->client = $client;
}
public function addTask($name, $workload=null, $context = null, $unique = null)
{
$this->tasks[] = [__FUNCTION__, $name, $workload, $context, $unique];
}
public function addTaskHigh($name, $workload=null, $context = null, $unique = null)
{
$this->tasks[] = [__FUNCTION__, $name, $workload, $context, $unique];
}
public function addTaskLow($name, $workload=null, $context = null, $unique = null)
{
$this->tasks[] = [__FUNCTION__, $name, $workload, $context, $unique];
}
public function addTaskBackground($name, $workload=null, $context = null, $unique = null)
{
$this->tasks[] = [__FUNCTION__, $name, $workload, $context, $unique];
}
public function addTaskHighBackground($name, $workload=null, $context = null, $unique = null)
{
$this->tasks[] = [__FUNCTION__, $name, $workload, $context, $unique];
}
public function addTaskLowBackground($name, $workload=null, $context = null, $unique = null)
{
$this->tasks[] = [__FUNCTION__, $name, $workload, $context, $unique];
}
public function runTasks()
{
foreach ($this->tasks as list($actionName, $name, $workload, $context, $unique)) {
$this->client->$actionName($name, json_encode($workload), $context, $unique);
}
$this->client->runTasks();
}
public function onSuccess(callable $callback)
{
$this->client->setCompleteCallback($callback);
}
public function onException(callable $callback)
{
$this->client->setExceptionCallback($callback);
}
public function onFail(callable $callback)
{
$this->client->setFailCallback($callback);
}
}
[/sourcecode]

Library is available in packagist and source code in my github account.

Reading Modbus devices with Python from a PHP/Silex Application via Gearman worker

Yes. I know. I never know how to write a good tittle to my posts. Let me show one integration example that I’ve been working with this days. Let’s start.

In industrial automation there’re several standard protocols. Modbus is one of them. Maybe isn’t the coolest or the newest one (like OPC or OPC/UA), but we can speak Modbus with a huge number of devices.

I need to read from one of them, and show a couple of variables in a Web frontend. Imagine the following fake Modbus server (it emulates my real Modbus device)

[sourcecode language=”python”]
#!/usr/bin/env python

##
# Fake modbus server
# – exposes "Energy" 66706 = [1, 1170]
# – exposes "Power" 132242 = [2, 1170]
##

from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext
from pymodbus.datastore import ModbusSequentialDataBlock
from pymodbus.server.async import StartTcpServer
import logging

logging.basicConfig()
log = logging.getLogger()
log.setLevel(logging.DEBUG)

hrData = [1, 1170, 2, 1170]
store = ModbusSlaveContext(hr=ModbusSequentialDataBlock(2, hrData))

context = ModbusServerContext(slaves=store, single=True)

StartTcpServer(context)
[/sourcecode]

This server exposes two variables “Energy” and “Power”. This is a fake server and it will returns always 66706 for energy and 132242 for power. Mobus is a binary protocol so 66706 = [1, 1170] and 132242 = [2, 1170]

I can read Modbus from PHP, but normally use Python for this kind of logic. I’m not going to re-write an existing logic to PHP. I’m not crazy enough. Furthermore my real Modbus device only accepts one active socket to retrieve information. That’s means if two clients uses the frontend at the same time, it will crash. In this situations Queues are our friends.

I’ll use a Gearman worker (written in Python) to read Modbus information.

[sourcecode language=”python”]
from pyModbusTCP.client import ModbusClient
from gearman import GearmanWorker
import json

def reader(worker, job):
c = ModbusClient(host="localhost", port=502)

if not c.is_open() and not c.open():
print("unable to connect to host")

if c.is_open():

holdingRegisters = c.read_holding_registers(1, 4)

# Imagine we’ve "energy" value in position 1 with two words
energy = (holdingRegisters[0] << 16) | holdingRegisters[1]

# Imagine we’ve "power" value in position 3 with two words
power = (holdingRegisters[2] << 16) | holdingRegisters[3]

out = {"energy": energy, "power": power}
return json.dumps(out)
return None

worker = GearmanWorker([‘127.0.0.1’])

worker.register_task(‘modbusReader’, reader)

print ‘working…’
worker.work()
[/sourcecode]

Our backend is ready. Now we’ll work with the frontend. In this example I’ll use PHP and Silex.

[sourcecode language=”php”]
<?php
include __DIR__ . ‘/../vendor/autoload.php’;
use Silex\Application;
$app = new Application([‘debug’ => true]);
$app->register(new Silex\Provider\TwigServiceProvider(), array(
‘twig.path’ => __DIR__.’/../views’,
));
$app[‘modbusReader’] = $app->protect(function() {
$client = new \GearmanClient();
$client->addServer();
$handle = $client->doNormal(‘modbusReader’, ‘modbusReader’);
$returnCode = $client->returnCode();
if ($returnCode != \GEARMAN_SUCCESS) {
throw new \Exception($this->client->error(), $returnCode);
} else {
return json_decode($handle, true);
}
});
$app->get("/", function(Application $app) {
return $app[‘twig’]->render(‘home.twig’, $app[‘modbusReader’]());
});
$app->run();
[/sourcecode]

As we can see the frontend is a simple Gearman client. It uses our Python worker to read information from Modbus and render a simple html with a Twig template

[sourcecode language=”html”]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Demo</title>
</head>
<body>
Energy: {{ energy }}
Power: {{ power }}
</body>
</html>
[/sourcecode]

And that’s all. You can see the full example in my github account

Sending logs to a remote server using RabbitMQ

Time ago I wrote an article to show how to send Silex logs to a remote server. Today I want to use a messaging queue to do it. Normally, when I need queues, I use Gearman but today I want to play with RabbitMQ.

When we work with web applications it’s important to have, in some way or another, one way to decouple operations from the main request. Messaging queues are great tools to perform those operations. They even allow us to create our workers with a different languages than the main request. This days, for example, I’m working with modbus devices. The whole modbus logic is written in Python and I want to use a Frontend with PHP. I can rewrite the modbus logic with PHP (there’re PHP libraries to connect with modbus devices), but I’m not so crazy. Queues are our friends.

The idea in this post is the same than the previous post. We’ll use event dispatcher to emit events and we’ll send those events to a RabitMQ queue. We’ll use a Service Provider called.

[sourcecode language=”php”]
<?php
include __DIR__ . ‘/../vendor/autoload.php’;

use PhpAmqpLib\Connection\AMQPStreamConnection;
use RabbitLogger\LoggerServiceProvider;
use Silex\Application;
use Symfony\Component\HttpKernel\Event;
use Symfony\Component\HttpKernel\KernelEvents;

$connection = new AMQPStreamConnection(‘localhost’, 5672, ‘guest’, ‘guest’);
$channel = $connection->channel();

$app = new Application([‘debug’ => true]);
$app->register(new LoggerServiceProvider($connection, $channel));

$app->on(KernelEvents::TERMINATE, function (Event\PostResponseEvent $event) use ($app) {
$app[‘rabbit.logger’]->info(‘TERMINATE’);
});

$app->on(KernelEvents::CONTROLLER, function (Event\FilterControllerEvent $event) use ($app) {
$app[‘rabbit.logger’]->info(‘CONTROLLER’);
});

$app->on(KernelEvents::EXCEPTION, function (Event\GetResponseForExceptionEvent $event) use ($app) {
$app[‘rabbit.logger’]->info(‘EXCEPTION’);
});

$app->on(KernelEvents::FINISH_REQUEST, function (Event\FinishRequestEvent $event) use ($app) {
$app[‘rabbit.logger’]->info(‘FINISH_REQUEST’);
});

$app->on(KernelEvents::RESPONSE, function (Event\FilterResponseEvent $event) use ($app) {
$app[‘rabbit.logger’]->info(‘RESPONSE’);
});

$app->on(KernelEvents::REQUEST, function (Event\GetResponseEvent $event) use ($app) {
$app[‘rabbit.logger’]->info(‘REQUEST’);
});

$app->on(KernelEvents::VIEW, function (Event\GetResponseForControllerResultEvent $event) use ($app) {
$app[‘rabbit.logger’]->info(‘VIEW’);
});

$app->get(‘/’, function (Application $app) {
$app[‘rabbit.logger’]->info(‘inside route’);
return "HELLO";
});

$app->run();
[/sourcecode]

Here we can see the service provider:

[sourcecode language=”php”]
<?php
namespace RabbitLogger;

use PhpAmqpLib\Channel\AMQPChannel;
use PhpAmqpLib\Connection\AMQPStreamConnection;
use Silex\Application;
use Silex\ServiceProviderInterface;

class LoggerServiceProvider implements ServiceProviderInterface
{
private $connection;
private $channel;

public function __construct(AMQPStreamConnection $connection, AMQPChannel $channel)
{
$this->connection = $connection;
$this->channel = $channel;
}

public function register(Application $app)
{
$app[‘rabbit.logger’] = $app->share(
function () use ($app) {
$channelName = isset($app[‘logger.channel.name’]) ? $app[‘logger.channel.name’] : ‘logger.channel’;
return new Logger($this->connection, $this->channel, $channelName);
}
);
}

public function boot(Application $app)
{
}
}
[/sourcecode]

And here the logger:
[sourcecode language=”php”]
<?php
namespace RabbitLogger;

use PhpAmqpLib\Channel\AMQPChannel;
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;
use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;
use Silex\Application;

class Logger implements LoggerInterface
{
private $connection;
private $channel;
private $queueName;

public function __construct(AMQPStreamConnection $connection, AMQPChannel $channel, $queueName = ‘logger’)
{
$this->connection = $connection;
$this->channel = $channel;
$this->queueName = $queueName;
$this->channel->queue_declare($queueName, false, false, false, false);
}

function __destruct()
{
$this->channel->close();
$this->connection->close();
}

public function emergency($message, array $context = [])
{
$this->sendLog($message, $context, LogLevel::EMERGENCY);
}

public function alert($message, array $context = [])
{
$this->sendLog($message, $context, LogLevel::ALERT);
}

public function critical($message, array $context = [])
{
$this->sendLog($message, $context, LogLevel::CRITICAL);
}

public function error($message, array $context = [])
{
$this->sendLog($message, $context, LogLevel::ERROR);
}

public function warning($message, array $context = [])
{
$this->sendLog($message, $context, LogLevel::WARNING);
}

public function notice($message, array $context = [])
{
$this->sendLog($message, $context, LogLevel::NOTICE);
}

public function info($message, array $context = [])
{
$this->sendLog($message, $context, LogLevel::INFO);
}

public function debug($message, array $context = [])
{
$this->sendLog($message, $context, LogLevel::DEBUG);
}
public function log($level, $message, array $context = [])
{
$this->sendLog($message, $context, $level);
}

private function sendLog($message, array $context = [], $level = LogLevel::INFO)
{
$msg = new AMQPMessage(json_encode([$message, $context, $level]), [‘delivery_mode’ => 2]);
$this->channel->basic_publish($msg, ”, $this->queueName);
}
}
[/sourcecode]

And finally the RabbitMQ Worker to process our logs

[sourcecode language=”php”]
require_once __DIR__ . ‘/../vendor/autoload.php’;
use PhpAmqpLib\Connection\AMQPStreamConnection;
$connection = new AMQPStreamConnection(‘localhost’, 5672, ‘guest’, ‘guest’);
$channel = $connection->channel();
$channel->queue_declare(‘logger.channel’, false, false, false, false);
echo ‘ [*] Waiting for messages. To exit press CTRL+C’, "\n";
$callback = function($msg){
echo " [x] Received ", $msg->body, "\n";
//$msg->delivery_info[‘channel’]->basic_ack($msg->delivery_info[‘delivery_tag’]);
};
//$channel->basic_qos(null, 1, null);
$channel->basic_consume(‘logger.channel’, ”, false, false, false, false, $callback);
while(count($channel->callbacks)) {
$channel->wait();
}
$channel->close();
$connection->close();
[/sourcecode]

To run the example we must:

Start RabbitMQ server
[sourcecode]
rabbitmq-server
[/sourcecode]

start Silex server

[sourcecode]
php -S 0.0.0.0:8080 -t www
[/sourcecode]

start worker

[sourcecode]
php worker/worker.php
[/sourcecode]

You can see whole project in my github account

Foreign Data Wrappers with PostgreSQL and PHP

PostgreSQL is more than a relational database. It has many cool features. Today we’re going to play with Foreign Data Wrappers (FDW). The idea is crate a virtual table from an external datasource and use it like we use a traditional table.

Let me show you an example. Imagine that we’ve got a REST datasource on port 8888. We’re going to use this Silex application, for example

[sourcecode language=”php”]
use Silex\Application;

$app = new Application();

$app->get(‘/’, function(Application $app) {

return $app->json([
[‘name’ => ‘Peter’, ‘surname’ => ‘Parker’],
[‘name’ => ‘Clark’, ‘surname’ => ‘Kent’],
[‘name’ => ‘Bruce’, ‘surname’ => ‘Wayne’],
]);
});

$app->run();
[/sourcecode]

We want to use this datasource in PostgreSQL, so we need to use a “www foreign data wrapper”.

First we create the extension (maybe we need to compile the extension. We can follow the installation instructions here)

[sourcecode language=”sql”]
CREATE EXTENSION www_fdw;
[/sourcecode]

Now with the extension we need to create a “server”. This server is just a proxy that connects to the real Rest service

[sourcecode language=”sql”]
CREATE SERVER myRestServer FOREIGN DATA WRAPPER www_fdw OPTIONS (uri ‘http://localhost:8888&#8242;);
[/sourcecode]

Now we need to map our user to the server

[sourcecode language=”sql”]
CREATE USER MAPPING FOR gonzalo SERVER myRestServer;
[/sourcecode]

And finally we only need our “Foreign table”

[sourcecode language=”sql”]
CREATE FOREIGN TABLE myRest (
name text,
surname text
) SERVER myRestServer;
[/sourcecode]

Now we can perform SQL queries using our Foreign table

[sourcecode language=”sql”]
SELECT * FROM myRest
[/sourcecode]

We must take care with one thing. We can use WHERE clauses but if we run

[sourcecode language=”sql”]
SELECT * FROM myRest WHERE name=’Peter’
[/sourcecode]

We’ll that the output is the same than “SELECT * FROM myRest”. That’s because if we want to filter something with WHERE clause within Foreign we need to do it in the remote service. WHERE name=‘Peter’ means that our Database will execute the following request:

[sourcecode]
http://localhost:8888?name=Peter
[/sourcecode]

And we need to handle this parameter. For example doing something like that

[sourcecode language=”sql”]
use Silex\Application;
use Symfony\Component\HttpFoundation\Request;

$app = new Application();

$app->get(‘/’, function(Application $app, Request $request) {
$name = $request->get(‘name’);

$data = [
[‘name’ => ‘Peter’, ‘surname’ => ‘Parker’],
[‘name’ => ‘Clark’, ‘surname’ => ‘Kent’],
[‘name’ => ‘Bruce’, ‘surname’ => ‘Wayne’],
];
return $app->json(array_filter($data, function($reg) use($name){
return $name ? $reg[‘name’] == $name : true;
}));
});

$app->run();
[/sourcecode]

Calling Silex backend from command line. Creating SAAS command line tools

Sometimes we need to create command line tools. We can build those tools using different technologies. In Symfony world there’s Symfony Console. I feel very confortable using it. But if we want to distribute our tool we will need to face with one “problem”. User’ll need to have PHP installed. It sounds trivial but it isn’t installed in every computer. We can use nodeJs to build our tool. Nowadays nodeJs is a de-facto standard but we still have the problem. Another “problem” is how to distribute new version of our tool. Problems everywhere.

Software as a service tools are great. We can build a service (a web based service for example) and we can even monetize our service with one kind of paid-plan or another. With our SAAS we don’t need to worry about redistribute our software within each release. But, what happens when our service is a command line one?

Imagine for example that we’re going to build one service to convert text to uppercase (I thing this idea will become me rich, indeed 🙂

We can create one simple Silex example to convert to upper case strings:
[sourcecode language=”php”]
<?php
include __DIR__ . "/../vendor/autoload.php";

use Silex\Application;
use Symfony\Component\HttpFoundation\Request;

$app = new Application();
$app->post("/", function (Request $request) {
return strtoupper($request->getContent());
});
$app->run();
[/sourcecode]

And now we only to call this service from the command line. We can use curl for example and convert one file content to upper case:

[sourcecode language=”php”]
cat myfile.txt | curl -d @- localhost:8080 > MYFILE.txt
[/sourcecode]

You can see the example in my github account here

Working with Ionic and PHP Backends. Remote debugging with PHP7 and Xdebug working with real devices

Sometimes I speak with PHP developers and they don’t use remote debugging in their development environments. Some people don’t like to use remote debugging. They prefer to use TDD and rely on the unit tests. That’s a good point of view, but sometimes they don’t use remote debugging only because they don’t know how to do it, and that’s inadmissible. Remote debugger is a powerful tool especially to handle with legacy applications. I’ve using xdebug for years with my linux workstation for years. This days I’m using Mac and it’s also very simple to set up xdebug here.

First we need to install PHP:

[sourcecode language=”bash”]
brew install php70
[/sourcecode]

Then Xdebug
[sourcecode language=”bash”]
brew install php70-xdebug
[/sourcecode]

(in a Ubuntu box we only need to use apt-get instead of brew)

Now we need to setup xdebug to enable remote debugging:
In a standard installation xdebug configuration is located at: /usr/local/etc/php/7.0/conf.d/ext-xdebug.ini

[sourcecode language=”bash”]
[xdebug]
zend_extension="/usr/local/opt/php70-xdebug/xdebug.so"

xdebug.remote_enable=1
xdebug.remote_port=9000
xdebug.profiler_enable=0
xdebug.profiler_output_dir="/tmp"
xdebug.idekey= "PHPSTORM"
xdebug.remote_connect_back = 1
xdebug.max_nesting_level = 250
[/sourcecode]

And basically that’s all. To set/unset the cookie you can use one bookmarklet in your browser (you can generate your bookmarklets here). Or use a Chrome extension to enable xdebug.

Now se only need to start the built-in server with

[sourcecode language=”bash”]
php -S 0.0.0.0:8080
[/sourcecode]

And remote debugging will be available
Remote debugger works this way:

  • We open on port within our IDE. In my case PHPStorm (it happens when we click on “Start listening for PHP debug connections”)
  • We set one cookie in our browser (it happens when click on Chrome extension)
  • When our server receives one request with the cookie, it connects to the port that our IDE opens (usually port 9000). If you use a personal firewall in your workstation, ensure that you allow incoming connections to this port.

Nowadays I’m involved with several projects building hybrid applications with Apache Cordova. In the Frontend I’m using ionic and Silex in the Backend. When I’m working with hybrid applications normally I go through two phases.

In the first one I build a working prototype. To to this I run a local server and I use my browser to develop the application. This phase is very similar than a traditional Web development process. If we also set up properly LiveReload, our application will be reloaded each time we change one javaScript file. Ionic framework integrates LiveReload and we only need to run:

[sourcecode language=”bash”]
ionic serve -l
[/sourcecode]

to start our application. We also need to start our backend server. For example

[sourcecode language=”bash”]
php -S 0.0.0.0:8080 -t api/www
[/sourcecode]

Now we can debug our Backend with remote debugger and Frontend with Chrome’s developer’s tools. Chrome also allows us to edit Frontend files and save them within the filesystem using workspaces. This phase is the easy one. But sooner or later we’ll need start working with a real device. We need a real device basically if we use plugins such as Camera plugin, Geolocation plugin, or things like that. OK there are emulators, but usually emulators don’t allow to use all plugins in the same way than we use then with a real device. Chrome also allow us to see the console logs of the device from our workstation. OK we can see all logs of our plugged Android device using “adb logcat” but follow the flow of our logs with logcat is similar than understand Matrix code. It’s a mess.

If we plug our android device to our computer and we open with Chrome:
[sourcecode]
chrome://inspect/#devices
[/sourcecode]
We can see our device’s console, use breakpoints and things like that. Cool, isn’t it? Of course it only works if we compile our application without “–release” option. We can do something similar with Safary and iOS devices.

With ionic if we want to use LiveReload from the real device and not to recompile and re-install again and again our application each time we change our javaScript files, we can run the application using

[sourcecode]
ionic run android –device -l
[/sourcecode]

When we’re developing our application and we’re in this phase we also need to handle with CORS. CORS isn’t a problem when we run our hybrid application in production. When we run the hybrid application with our device our “origin” is the local filesystem. That’s means CORS don’t apply, but when we run our application in the device, but served from our computer (when we use “-l” option), our origin isn’t local filesystem. So if our Backend is served from another origin we need to enable CORS.

We can enable CORS in the backend. I’ve written about it here, but ionic people allows us a easier way. We can set up a local proxy to serve our backend through the same origin than the application does and forget about CORS. Here we can read a good article about it.

Anyway if we want to start the remote debugger we need to create one cookie called XDEBUG_SESSION. In the browser we can use chrome extension, but when we inspect the plugged device isn’t so simple. It would be cool that ionic people allows us to inject cookies to our proxy server. I’ve try to see how to do it with ionic-cli. Maybe is possible but I didn’t realize how to do it. Because of that I’ve created a simple AngularJS service to inject this cookie. Then, if I start listening debug connections in my IDE I’ll be able to use remote debugger as well as I do when I work with the browser.

First we need to install service via Bower:

[sourcecode]
bower install ng-xdebugger –save
[/sourcecode]

Now we need to include javaScript files
[sourcecode language=”html”]
<script src="lib/angular-cookies/angular-cookies.min.js"></script>
<script src="lib/ng-xdebugger/dist/gonzalo123.xdebugger.min.js"></script>
[/sourcecode]

then we add our service to the project.
[sourcecode language=”js”]
angular.module("starter", ["ionic", "gonzalo123.xdebugger"])
[/sourcecode]

Now we only need to configure our application and set de debugger key (it must be the same key than we use within the server-side configuration of xdebug)
[sourcecode language=”js”]
.config(function (xdebuggerProvider) {
xdebuggerProvider.setKey(‘PHPSTORM’);
})
})
[/sourcecode]

And that’s all. The service is very simple. It only uses one http interceptor to inject the cookie in our http requests:
[sourcecode language=”js”]
(function () {
"use strict";

angular.module("gonzalo123.xdebugger", ["ngCookies"])
.provider("xdebugger", [‘$httpProvider’, function ($httpProvider) {
var debugKey;

this.$get = function () {
return {
getDebugKey: function () {
return debugKey;
}
};
};

this.setKey = function (string) {
if (string) {
debugKey = string;
$httpProvider.interceptors.push("xdebuggerCookieInterceptor");
}
};
}])

.factory("xdebuggerCookieInterceptor", [‘$cookieStore’, ‘xdebugger’, function ($cookieStore, xdebugger) {
return {
response: function (response) {
$cookieStore.put("XDEBUG_SESSION", xdebugger.getDebugKey());

return response;
}
};
}])
;
})();
[/sourcecode]

And of course you can see the whole project in my github account.

POST Request logger using websockets

Last days I’ve been working with background geolocation with an ionic application. There’s a cool plugin to do that. The free version of the plugin works fine. But there’s a also a premium version with improvements, especially in battery consumption with Android devices.

Basically this plugin performs a POST request to the server with the GPS data. When I was developing my application I needed a simple HTTP server to see the POST requests. Later I’ll code the backend to handle those requests. I can develop a simple Silex application with a POST route and log the request in a file or flush those request to the console. This’d have been easy but as far as I’m a big fan of WebSockets (yes I must admit that I want to use WebSockets everywere 🙂 I had one idea in my mind. The idea was create a simple HTTP server to handle my GPS POST requests but instead of logging the request I will emit a WebSocket. Then I can create one site that connects to the WebSocket server and register on screen the POST request. Ok today I’m a bit lazy to fight with the Frontend so my log will be on the browser’s console.

To build the application I’ll reuse one of my projects in github: The PHP dumper. The idea is almost the same. I’ll create a simple HTTP server with Silex with two routes. One to handle POST requests (the GPS ones) and another GET to allow me to connect to the WebSocket

That’s the server. Silex, a bit of Twig, another bit of Guzzle and that’s all

[sourcecode language=”php”]
use GuzzleHttp\Client;
use Silex\Application;
use Silex\Provider\TwigServiceProvider;
use Symfony\Component\HttpFoundation\Request;

$app = new Application([
‘debug’ => true,
‘ioServer’ => ‘//localhost:8888’,
‘wsConnector’ => ‘http://127.0.0.1:26300&#8217;
]);

$app->register(new TwigServiceProvider(), [
‘twig.path’ => __DIR__ . ‘/../views’,
]);

$app[‘http.client’] = new Client();

$app->get("/{channel}", function (Application $app, $channel) {
return $app[‘twig’]->render(‘index.twig’, [
‘channel’ => $channel,
‘ioServer’ => $app[‘ioServer’]
]);
});

$app->post("/{channel}", function (Application $app, $channel, Request $request) {
$app[‘http.client’]->get($app[‘wsConnector’] . "/info/{$channel}/" . json_encode($request->getContent()));

return $app->json(‘OK’);
});

$app->run();
[/sourcecode]

That’s the Twig template. Nothing especial: A bit of Bootstrap and one socket.io client. Each time user access to one “channel”‘s url (GET /mychannel). It connects to websocket server

[sourcecode language=”js”]
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’);
});
[/sourcecode]

And each time background geolocation plugin POSTs GPS data Silex POST route will emit a WebSocket to the desired channel. Our WebSocket client just logs the GPS data using console.log. Is hard to explain but very simple process.

We also can emulate POST requests with this simple node script:

[sourcecode language=”js”]
var request = require(‘request’);

request.post(‘http://localhost:8080/Hello&#8217;, {form: {key: ‘value’}}, function (error, response, body) {
if (!error && response.statusCode == 200) {
console.log(body)
}
});
[/sourcecode]

And that’s all. You can see the whole code within my github account.

Book review: Socket.IO Cookbook

Last summer I collaborated as a technical reviewer in the book “Socket.IO Cookbook” written by Tyson Cadenhead and finally I’ve got the book in my hands

I’m a big fan of real time technologies and I’m normally Socket.io user. Because of that, when people of Packt Publishing contacted me to join to the project as technical reviewer my answer was yes. I’ve got serious problems nowadays to find time to pet projects and extra activities, but if there’re WebSockets inside I cannot resists.

The book is correct and it’s a good starting point to event-based communication with JavaScript. I normally don’t like beginners books (even if I’m a beginner in the technology). I don’t like the books where author explains how to do one thing that I can see how to do it within the website of the. OK. This book isn’t one of those of books. The writer don’t assume reader is a totally newbie. Because of that newbies sometimes can be lost in some chapters, but this exactly the way we all learn new technologies. I like the way Tyson introduces concepts about socket.io.

The book is focused in JavaScript and also uses JavaScript to the backend (with node). Maybe I miss the integration with non-JavaScript environments, but as socket.io is a javascript library I understand that the usage of JavaScript in all application lifecycle is a good approach.

IMG_20151106_204902_jpg

Also those days I was reading and playing a little bit with WebRTC and the book has one chapter about it! #cool