Using the event dispatcher in a Silex application


Symfony has one component called The Event Dispatcher. This component is one implementation of Mediator pattern and it’s widely used in modern frameworks, such as Symfony. Silex, as a part of Symfony, also uses this component and we can easily use it in our projects. Let me show you one little example. Imagine one simple route in Silex to create one png file containing one text:

$app->get("/gd/{text}", function($text) {
    $path = "/tmp/qr.png." . uniqid();
    $im = imagecreate(90, 30);
    $background = imagecolorallocate($im, 255, 255, 255);
    $color = imagecolorallocate($im, 0, 0, 0);
    imagestring($im, 5, 5, 5,  $text, $color);
    imagepng($im, $path);
    imagedestroy($im);
    return $app->sendFile($path);
});

It works, but there’s one mistake. We need to unlink our temporally file $path, but where? We need do if after “return $app->sendFile($path);” but it’s not possible.

$app->get("/gd/{text}", function($text) {
    $path = "/tmp/qr.png." . uniqid();
    $im = imagecreate(90, 30);
    $background = imagecolorallocate($im, 255, 255, 255);
    $color = imagecolorallocate($im, 0, 0, 0);
    imagestring($im, 5, 5, 5,  $text, $color);
    imagepng($im, $path);
    imagedestroy($im);
    return $app->sendFile($path, 200, ['Content-Type' => 'image/png']);;
    unlink($path); // unreachable code
});

We can use BinaryFileResponse instead of the helper function “sendFile”, but there’s one smarter solution: The event dispatcher.

use Symfony\Component\HttpKernel\KernelEvents;

$app->get("/gd/{text}", function($text) use (app) {
    $im = imagecreate(90, 30);
    $path = "/tmp/qr.png." . uniqid();
    $background = imagecolorallocate($im, 255, 255, 255);
    $color = imagecolorallocate($im, 0, 0, 0);
    imagestring($im, 5, 5, 5,  $text, $color);
    imagepng($im, $path);
    imagedestroy($im);
    
    $app['dispatcher']->addListener(KernelEvents::TERMINATE, function() use ($path) {
        unlink($path);
    });

    return $app->sendFile($path, 200, ['Content-Type' => 'image/png']);
});

(Updated! thanks to Hakin’s recommendation)
Or even better using Silex’s Filters. In this case we after or finish. In fact those filters are nothing more than an elegant way to speak to the event dispatcher.


$app->get("/gd/{text}", function($text) use (app) {
    $im = imagecreate(90, 30);
    $path = "/tmp/qr.png." . uniqid();
    $background = imagecolorallocate($im, 255, 255, 255);
    $color = imagecolorallocate($im, 0, 0, 0);
    imagestring($im, 5, 5, 5,  $text, $color);
    imagepng($im, $path);
    imagedestroy($im);
    
    $app->after(function() use ($path) {
        unlink($path);
    });

    return $app->sendFile($path, 200, ['Content-Type' => 'image/png']);
});

We also can use the generic function to add events to the event listener:

use Symfony\Component\HttpKernel\KernelEvents;

$app->get("/gd/{text}", function($text) use (app) {
    $im = imagecreate(90, 30);
    $path = "/tmp/qr.png." . uniqid();
    $background = imagecolorallocate($im, 255, 255, 255);
    $color = imagecolorallocate($im, 0, 0, 0);
    imagestring($im, 5, 5, 5,  $text, $color);
    imagepng($im, $path);
    imagedestroy($im);
    
    $app->on(KernelEvents::TERMINATE, function() use ($path) {
        unlink($path);
    });

    return $app->sendFile($path, 200, ['Content-Type' => 'image/png']);
});

Now our temporally file will be deleted once a response is sent. Life is simpler with event dispatcher :)

About Gonzalo Ayuso

Web Architect specialized in Open Source technologies. PHP, Python, JQuery, Dojo, PostgreSQL, CouchDB and node.js but always learning.

Posted on October 14, 2013, in silex, Symfony, Symfony2, Technology and tagged , , . Bookmark the permalink. 10 Comments.

  1. You could also use $app->after(…); or $app->get()->after(); It uses the dispatcher too and is probably better readable.

  2. It’s exactly what I was looking for, thanks!

    One question though, what if I used Ajax to retrieve the content, and use $app->after()…
    The ajax request would wait for the response to be finished “receiving” data right?

    • It depends on the event that you are using. The answer is here (read the description of each event): https://github.com/symfony/symfony/blob/master/src/Symfony/Component/HttpKernel/KernelEvents.php

      • Thank you for your answer.

        I’am actually trying to execute some actions after sending the response (using KernelEvents::TERMINATE) and I though it was executed after the response was processed in my success callback.

        But, by adding a sleep() in the callback passed to $app->on(), it turned out that the ajax call was waiting for the whole response : success & complete are called after the callback.

        Did I do something wrong? Do you have any idea? I don’t want to use a queue since I only have a fews calls that require these kind of post actions execution.

      • Gonzalo Ayuso

        The problem is that the conection is not closed (untill the scripts ends). The data is sent but your js client is waiting. The best way to do this kind of things is to decouple the procerss. I normally use Gearman (a job server) for this kind of things. Instead of doing all the things within the request, I send (enqueue) all the stuff that the user don’t really need (such as logs, background process, …) to the gearman server. Then another process (a gearman worker) do the work and the user dont’t need to wait. It adds a extra complexity, but the user experience is considerably improved.

      • Ok, I get it, thank you so much!

  1. Pingback: Playing with event dispatcher and Silex. Sending logs to a remote server. | Gonzalo Ayuso | Web Architect

  2. Pingback: Дайджест интересных новостей и материалов из мира PHP за последние две недели (6—20 октября 2013) | Juds

  3. Pingback: Brincando com event dispatcher e Silex - enviando logs para um servidor remoto | iMasters

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 953 other followers

%d bloggers like this: