Bundles in Silex using Stack

In the last Desymfony conference I was speaking with Luis Cordova and he introduced me “Stack” (I must admit Stack was in my to-study-list but only marked as favorite). The idea behind Stack is really cool. (In fact every project where Igor Wiedler appears is brilliant, even the chicken one :)).

Nowadays almost every modern framework/applications implements HttpKernelInterface (Symfony, Laravel, Drupal, Silex, Yolo and even the framework that I’m working in ;)) and we can build complex applications mixing different components and decorate our applications with an elegant syntax.

The first thing than come to my mind after studying Stack is to join different Silex applications in a similar way than Symfony (the full stack framework) uses bundles. And the best part of this idea is that it’s pretty straightforward. Let me show you one example:

Imagine that we’re working with one application with a blog and one API. In this case our blog and our API are Silex applications (but they can be one Symfony application and one Silex application for example).

That’s our API application:

use Silex\Application;

$app = new Application();
$app->get('/', function () {
        return "Hello from API";
    });

$app->run();

And here our blog application:

use Silex\Application;

$app = new Application();
$app->get('/', function () {
        return "Hello from Blog";
    });

$app->run();

We can organize our application using mounted controllers or even using RouteCollections but today we’re going to use Stack and it’s cool url-map.

First we are going to create our base application. To do this we’re going to implement the simplest Kernel in the world, that’s answers with “Hello” to every request:

use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class MyKernel implements HttpKernelInterface
{
    public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
    {
        return new Response("Hello");
    }
}

Stack needs HttpKernelInterface and Silex\Application implements this interface, so we can change our Silex applications to return the instance instead to run the application:

// app/api.php
use Silex\Application;

$app = new Application();
$app->get('/', function () {
        return "Hello from API";
    });

return $app;
// app/blog.php
use Silex\Application;

$app = new Application();
$app->get('/', function () {
        return "Hello from API";
    });

return $app;

And now we will attach those two Silex applications to our Kernel:

use Symfony\Component\HttpFoundation\Request;

$app = (new Stack\Builder())
    ->push('Stack\UrlMap', [
            "/blog" => include __DIR__ . '/app/blog.php',
            "/api" => include __DIR__ . '/app/api.php'
        ])->resolve(new MyKernel());

$request = Request::createFromGlobals();

$response = $app->handle($request);
$response->send();

$app->terminate($request, $response);

And that’s all. I don’t know what you think but with Stack one big window just opened in my mind. Cool, isn’t it?

You can see this working example in my github

Advertisement

Working with jQuery and Silex as RestFull Resource provider

The previous post was about how to use AngularJS resources with Silex. AngularJS is great and when I need to switch back to jQuery it looks like I go back 10 years in web development, but business is business and I need to live with jQuery too. Because of that this post is about how to use the Silex RestFull resources from the previous post, now with jQuery. Let’s start:

We’re going to write a simple javascript object to handle the RestFull resource using jQuery:

var Resource = (function (jQuery) {
    function Resource(url) {
        this.url = url;
    }

    Resource.prototype.query = function (parameters) {
        return jQuery.getJSON(this.url, parameters || {});
    };

    Resource.prototype.get = function (id, parameters) {
        return jQuery.getJSON(this.url + '/' + id, parameters || {});
    };

    Resource.prototype.remove = function (id, parameters) {
        return jQuery.ajax({
            url:this.url + '/' + id,
            xhrFields:JSON.stringify(parameters || {}),
            type:'DELETE',
            dataType:'json'
        });
    };

    Resource.prototype.update = function (id, parameters) {
        return jQuery.post(this.url + '/' + id, JSON.stringify(parameters || {}), 'json');
    };

    Resource.prototype.create = function (parameters) {
        return jQuery.post(this.url, JSON.stringify(parameters || {}), 'json');
    };

    return Resource;
})(jQuery);

As you can see the library returns jQuery ajax promises, so we can use done() and error() callbacks to work with the server’s data.

Here our application example:

var host = document.location.protocol + '//' + document.location.host;
var resource = new Resource(host + '/api/message/resource');

resource.create({ id: 10, author: 'myname', message: 'hola'}).done(function (data) {
    console.log("create element", data);
});

resource.query().done(function (data) {
   console.log("query all", data);
});

resource.update(10, {message: 'hi'}).done(function (data) {
    console.log("update element 1", data);
});

resource.get(10).done(function (data) {
    console.log("get element 1", data);
});

resource.remove(10).done(function (data) {
    console.log("remove element 1", data);
});

resource.query().done(function (data) {
    console.log("query all", data);
});

And that’s all. You can get the full code of the example from my github account

How to configure Symfony’s Service Container to use Twitter API

Keeping on with the series about Symfony’s Services container (another posts here and here), now we will use the service container to use Twitter API from a service.

To use Twitter API we need to handle http requests. I’ve written several post about http request with PHP (example1, example2), but today we will use one amazing library to build clients: Guzzle. Guzzle is amazing. We can easily build a Twitter client with it. There’s one example is its landing page:

<?php
$client = new Client('https://api.twitter.com/{version}', array('version' => '1.1'));
$oauth  = new OauthPlugin(array(
    'consumer_key'    => '***',
    'consumer_secret' => '***',
    'token'           => '***',
    'token_secret'    => '***'
));
$client->addSubscriber($oauth);

echo $client->get('/statuses/user_timeline.json')->send()->getBody();

If we are working within a Symfony2 application or a PHP application that uses the Symfony’s Dependency injection container component you can easily integrate this simple script in the service container. I will show you the way that I use to do it. Let’s start:

The idea is simple. First we include guzzle within our composer.json and execute composer update:

    "require": {
        "guzzle/guzzle":"dev-master"
    }

Then we will create two files, one to store our Twitter credentials and another one to configure the service container:

# twitter.conf.yml
parameters:
  twitter.baseurl: https://api.twitter.com/1.1

  twitter.config:
    consumer_key: ***
    consumer_secret: ***
    token: ***
    token_secret: ***
# twitter.yml
parameters:
  class.guzzle.response: Guzzle\Http\Message\Response
  class.guzzle.client: Guzzle\Http\Client
  class.guzzle.oauthplugin: Guzzle\Plugin\Oauth\OauthPlugin

services:
  guzzle.twitter.client:
    class: %class.guzzle.client%
    arguments: [%twitter.baseurl%]
    calls:
      - [addSubscriber, [@guzzle.twitter.oauthplugin]]

  guzzle.twitter.oauthplugin:
    class: %class.guzzle.oauthplugin%
    arguments: [%twitter.config%]

And finally we include those files in our services.yml:

# services.yml
imports:
- { resource: twitter.conf.yml }
- { resource: twitter.yml }

And that’s all. Now we can use the service without problems:

<?php

namespace Gonzalo123\AppBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;

class DefaultController extends Controller
{
    public function indexAction($name)
    {
        $twitterClient = $this->container->get('guzzle.twitter.client');
        $status = $twitterClient->get('statuses/user_timeline.json')
             ->send()->getBody();

        return $this->render('AppBundle:Default:index.html.twig', array(
            'status' => $status
        ));
    }
}

Building a Silex application from one Behat/Gherkin feature file

Last days I’ve playing with Behat. Behat is a behavior driven development (BDD) framework based on Ruby’s Cucumber. Basically with Behat we defenie features within one feature file. I’m not going to crate a Behat tutorial (you can read more about Behat here). Behat use Gherkin to write the features files. When I was playing with Behat I had one idea. The idea is simple: Can we use Gherking to build a Silex application?. It was a good excuse to study Gherking, indeed ;).

Here comes the feature file:

Feature: application API
  Scenario: List users
    Given url "/api/users/list.json"
    And request method is "GET"
    Then instance "\Api\Users"
    And execute function "listUsers"
    And format output into json

  Scenario: Get user info
    Given url "/api/user/{userName}.json"
    And request method is "GET"
    Then instance "\Api\User"
    And execute function "info"
    And format output into json

  Scenario: Update user information
    Given url "/api/user/{userName}.json"
    And request method is "POST"
    Then instance "\Api\User"
    And execute function "update"
    And format output into json

Our API use this simple library:

<?php

namespace Api;
use Symfony\Component\HttpFoundation\Request;

class User
{
    private $request;

    public function __construct(Request $request)
    {
        $this->request = $request;
    }

    public function info()
    {
        switch ($this->request->get('userName')) {
            case 'gonzalo':
                return array('name' => 'Gonzalo', 'surname' => 'Ayuso');
            case 'peter':
                return array('name' => 'Peter', 'surname' => 'Parker');
        }
    }

    public function update()
    {
        return array('infoUpdated');
    }
}
<?php

namespace Api;
use Symfony\Component\HttpFoundation\Request;

class Users
{
    public function listUsers()
    {
        return array('gonzalo', 'peter');
    }
}

The idea is simple. Parse the feature file with behat/gherkin component and create a silex application. And here comes the “magic”. This is a simple working prototype, just an experiment for a rainy sunday.

<?php
include __DIR__ . '/../vendor/autoload.php';
define(FEATURE_PATH, __DIR__ . '/api.feature');

use Behat\Gherkin\Lexer,
        Behat\Gherkin\Parser,
        Behat\Gherkin\Keywords\ArrayKeywords,
        Behat\Gherkin\Node\FeatureNode,
        Behat\Gherkin\Node\ScenarioNode,
        Symfony\Component\HttpFoundation\Request,
        Silex\Application;

$keywords = new ArrayKeywords([
    'en' => [
        'feature' => 'Feature',
        'background' => 'Background',
        'scenario' => 'Scenario',
        'scenario_outline' => 'Scenario Outline',
        'examples' => 'Examples',
        'given' => 'Given',
        'when' => 'When',
        'then' => 'Then',
        'and' => 'And',
        'but' => 'But'
    ],
]);

function getMatch($subject, $pattern) {
    preg_match($pattern, $subject, $matches);
    return isset($matches[1]) ? $matches[1] : NULL;
}

$app = new Application();

function getScenarioConf($scenario) {
    $silexConfItem = [];

    /** @var $scenario  ScenarioNode */
    foreach ($scenario->getSteps() as $step) {
        $route = getMatch($step->getText(), '/^url "([^"]*)"$/');

        if (!is_null($route)) {
            $silexConfItem['route'] = $route;
        }

        $requestMethod = getMatch($step->getText(), '/^request method is "([^"]*)"$/');
        if (!is_null($requestMethod)) {
            $silexConfItem['requestMethod'] = strtoupper($requestMethod);
        }

        $instance = getMatch($step->getText(), '/^instance "([^"]*)"$/');
        if (!is_null($instance)) {
            $silexConfItem['className'] = $instance;
        }

        $method = getMatch($step->getText(), '/^execute function "([^"]*)"$/');
        if (!is_null($method)) {
            $silexConfItem['method'] = $method;
        }

        if ($step->getText() == 'format output into json') {
            $silexConfItem['jsonEncode'] = TRUE;
        }
    }
    return $silexConfItem;
}

/** @var $features FeatureNode */
$features = (new Parser(new Lexer($keywords)))->parse(file_get_contents(FEATURE_PATH), FEATURE_PATH);

foreach ($features->getScenarios() as $scenario) {
    $silexConfItem = getScenarioConf($scenario);
    $app->match($silexConfItem['route'], function (Request $request) use ($app, $silexConfItem) {
            function getConstructorParams($rClass, $request) {
                $parameters =[];
                foreach ($rClass->getMethod('__construct')->getParameters() as $parameter) {
                    if ('Symfony\Component\HttpFoundation\Request' == $parameter->getClass()->name) {
                        $parameters[$parameter->getName()] = $request;
                    }
                }
                return $parameters;
            }

            $rClass = new ReflectionClass($silexConfItem['className']);
            $obj = ($rClass->hasMethod('__construct')) ?
                    $rClass->newInstanceArgs(getConstructorParams($rClass, $request)) :
                    new $silexConfItem['className'];

            $output = $obj->{$silexConfItem['method']}();

            return ($silexConfItem['jsonEncode'] === TRUE) ? $app->json($output, 200) : $output;
        }
    )->method($silexConfItem['requestMethod']);
}

$app->run();

You can see the source code in github. What do you think?

How to rewrite urls with PHP 5.4’s built-in web server

PHP 5.4 comes with a flaming built-in web server. This server is (obviously) not suitable to use in production environments, but it’s great if we want to check one project quickly:

  • git clone from github
  • composer install to install dependencies
  • run the built-in web server and test the application.
php -S localhost:8888 -t www/

But is very usual to use mod_rewrite or similar to send all requests to the front controller. With apache is pretty straight forward to do it:

<IfModule mod_rewrite.c>
    Options -MultiViews
    RewriteEngine On
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^(.*)$ index.php [QSA,L]
</IfModule>

But, does it work with the built-in web server? The answer is yes, but with another syntax. We only need to create one router file and start our server with this router:

<?php
// www/routing.php
if (preg_match('/\.(?:png|jpg|jpeg|gif)$/', $_SERVER["REQUEST_URI"])) {
    return false;
} else {
    include __DIR__ . '/index.php';
}

And now we start the server with:

php -S localhost:8888 www/routing.php

Easy, isn’t it?

How to send the output of Symfony’s process Component to a node.js server in Real Time with Socket.io

Today another crazy idea. Do you know Symfony Process Component? The Process Component is a simple component that executes commands in sub-processes. I like to use it when I need to execute commands in the operating system. The documentation is pretty straightforward. Normally when I want to collect the output of the script (imagine we run those scripts within a crontab) I save the output in a log file and I can check it even in real time with tail -f command.

This approach works, but I want to do it in a browser (call me crazy :)). I’ve written a couple of posts with something similar. What I want to do now? The idea is simple:

First I want to execute custom commands with process. Just follow the process documentation:

<?php
use Symfony\Component\Process\Process;

$process = new Process('ls -lsa');
$process->setTimeout(3600);
$process->run();
if (!$process->isSuccessful()) {
    throw new \RuntimeException($process->getErrorOutput());
}

print $process->getOutput();

Process has one cool feature, we can give feedback in real-time by passing an anonymous function to the run() method:

<?php
use Symfony\Component\Process\Process;

$process = new Process('ls -lsa');
$process->run(function ($type, $buffer) {
    if ('err' === $type) {
        echo 'ERR > '.$buffer;
    } else {
        echo 'OUT > '.$buffer;
    }
});

The idea now is to use this callback to send a TCP socket to one server with node.js

var LOCAL_PORT = 5600;
var server = require('net').createServer(function (socket) {
    socket.on('data', function (msg) {
        console.log(msg);
    });
}).listen(LOCAL_PORT);

server.on('listening', function () {
    console.log("TCP server accepting connection on port: " + LOCAL_PORT);
});

Now we change our php script to

<?php
include __DIR__ . "/../vendor/autoload.php";

use Symfony\Component\Process\Process;

function runProcess($command)
{
    $address = 'localhost';
    $port = 5600;
    $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
    socket_connect($socket, $address, $port);

    $process = new Process($command);
    $process->setTimeout(3600);
    $process->run(
        function ($type, $buffer) use ($socket) {
            if ('err' === $type) {
                socket_write($socket, "ERROR\n", strlen("ERROR\n"));
                socket_write($socket, $buffer, strlen($buffer));
            } else {

                socket_write($socket, $buffer, strlen($buffer));
            }
        }
    );
    if (!$process->isSuccessful()) {
        throw new \RuntimeException($process->getErrorOutput());
    }
    socket_close($socket);
}

runProcess('ls -latr /');

Now with the node.js started, if we run the php script, we will see the output of the process command in the node’s terminal. But we want to show it in a browser. What can we do? Of course, socket.io. We change the node.js command to:

var LOCAL_PORT = 5600;
var SOKET_IO_PORT = 8000;
var ioClients = [];

var io = require('socket.io').listen(SOKET_IO_PORT);

var server = require('net').createServer(function (socket) {
    socket.on('data', function (msg) {
        ioClients.forEach(function (ioClient) {
            ioClient.emit('log', msg.toString().trim());
        });
    });
}).listen(LOCAL_PORT);

io.sockets.on('connection', function (socketIo) {
    ioClients.push(socketIo);
});

server.on('listening', function () {
    console.log("TCP server accepting connection on port: " + LOCAL_PORT);
});

and finally we create a simple web client:

<script src="http://localhost:8000/socket.io/socket.io.js"></script>
<script>
    var socket = io.connect('http://localhost:8000');
    socket.on('log', function (data) {
        console.log(data);
    });
</script>

Now if we start one browser we will see the output of our command line process within the console tab of the browser.

If you want something like webConsole, I also have created the example, With a Web UI enabling to send custom commands. You can see it in github.

Obviously that’s only one experiment with a lot of security issues that we need to take into account if we want to use it in production. What do you think?

Scroll desktop’s Web pages remotely with our smartphone, using Node.js and WebSockets

Why this script? OK. It was a crazy idea. It started with one “Is it possible? Yes, let’s code it” in my mind. Let start. I want to scroll one web page in the TV’s web browser (or PC’s browser) using my smartphone lying on my couch. I’ve got a wireless mouse so I don’t really need it, but scroll the TV browser with the smartphone sounds cool, isn’t it?

The idea is the following one:

  • One QR code in our web page (added dinamically with JavaScrip with Google’s Chart API ). Write urls with the smartphone is hard and QR has a good hype, so we will add a QR code at the bottom of the web page with the link to the node.js server.
  • One socket.io server built with a node.js server for the real time communications. This node.js server will serve also a jQuery Mobile application with four buttons (with express and jade):
  • The server will register the WebSocket and send the real time commands to the browser (with one easy-to-hack security token).
  • The browser will handle the socket.io actions and controls the scroll of the web page.

The code is probably crowded by bugs and security problems, but it works and it was enough in my experiment 🙂 :

The node.js server:

var io = require('socket.io').listen(8080);

var app = require('express').createServer();
app.set('view engine', 'jade');

app.set('view options', {
    layout:false
});

app.get('/panel/:key', function (req, res) {
    var key = req.params.key;
    console.log(key);
    res.render('mobile.jade', {key:key});
});

app.get('/action/:key/:y/:action', function (req, res) {
    var key = req.params.key;
    var y = req.params.y;
    var action = req.params.action;
    sockets[key].emit('scrollTo', {y:y, action:action});
    res.send('OK');
});

app.listen(8000);

var sockets = {};
io.sockets.on('connection', function (socket) {
    socket.on('setKey', function (key) {
        sockets[key] = socket;
    });
});

The jade template with the jquery mobile application:

!!! 5
html
    head
        meta(charset="utf-8")
        meta(name="viewport", content="width=device-width, initial-scale=1")
        title title
        link(rel='stylesheet', href='http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.css')
        script(src='http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js')
        script(src='http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.js')
        script(type='text/javascript')
            $(document).bind('pageinit', function() {
                $('#toTop').tap(function() {
                    $.get('/action/#{key}/0/go', function() {});
                });
                $('#toBotton').tap(function() {
                    $.get('/action/#{key}/max/go', function() {});
                });
                $('#toUp').tap(function() {
                    $.get('/action/#{key}/100/rew', function() {});
                });
                $('#toDown').tap(function() {
                    $.get('/action/#{key}/100/ffd', function() {});
                });
            });
    body
        #page1(data-role="page")
            #header(data-theme="a", data-role="header")
                h3 Header
            #content(data-role="content")
                a(data-role="button", data-transition="fade", data-theme="a", href="#", id="toTop", data-icon="minus", data-iconpos="left") Top
                a(data-role="button", data-transition="fade", href="#", id="toUp", data-icon="arrow-u", data-iconpos="left") Up
                a(data-role="button", data-transition="fade", href="#", id="toDown", data-icon="arrow-d", data-iconpos="left") Down
                a(data-r

Our Html client page

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
    <title>jQuery Smooth Scroll - Design Woop</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
</head>
<body>
<p>
    Lorem ipsum ….. put a big lorem ipsum here to make possible the scroll
</p>
<script src="http://localhost:8080/socket.io/socket.io.js"></script>
<script src="client.js"></script>
</body>
</html>

And finally our client magic with the QR code and the functions to handle the node.js actions

var key = "secret";
function getDocHeight() {
    var D = document;
    return Math.max(
            Math.max(D.body.scrollHeight, D.documentElement.scrollHeight),
            Math.max(D.body.offsetHeight, D.documentElement.offsetHeight),
            Math.max(D.body.clientHeight, D.documentElement.clientHeight)
    );
}
var socket = io.connect('http://localhost:8080');
var y = 0;

socket.emit('setKey', key);
socket.on('scrollTo', function (data) {
    if (data.y == 'max') {
        y = getDocHeight();
    } else {
        if (data.action == 'ffd') {
            y += parseInt(data.y);
        } else if (data.action == 'go') {
            y = parseInt(data.y);
        } else {
            y -= parseInt(data.y);
        }
    }
    window.scrollTo(0, y);

});
document.write('<img src="https://chart.googleapis.com/chart?chs=150x150&cht=qr&chl=http://192.168.2.3:8000/panel/' + key + '&choe=UTF-8" alt=""/>');

You can see the code in github too. We also can see the script in action here:

We can also add more features to our application but that’s enought for this experiment. What do you think?