Symfony: Log outgoing responses with kernel events

One of the nicest features of Symfony2 is the Request/Response paradigm for processing a HTTP request and then sending a response back to a client. At a high level, Symfony’s HttpFoundation component provides an object oriented abstraction to easily deal with HTTP requests and generate responses to send back to a client. Assuming application code correctly uses HttpFoundation, it will only interact with request variables through the Request class, as opposed to $_REQUEST, and only send output using the Response class, as opposed to an “echo”. Because of this contract, the framework as a whole makes it easy to manipulate responses before they’re sent back to a client.

A typical use case that leverages this would be logging API responses before they’re sent back to a client. As much as an API might be RESTful, at some point it’s easier to debug things when you can see the responses that clients have been receiving. OK great so how do you do it? It’s actually pretty straightforward, just create a class to receive the “kernel.terminate” event and register it as a service with the appropriate tags:

# app/config/services.yml

services:
  apirequest_listner:
     class: Setfive\DemoBundle\Event\ApiControllerRequest
     tags:
       - { name: kernel.event_listener, event: kernel.terminate, method: onControllerResponse }

And then create the class where you want to manipulate or log the requests:

<?php

namespace Setfive\DemoBundle\Event;

use Setfive\DemoBundle\Entity\ApiResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\Kernel;

class ApiControllerRequest
{
    
    public function onControllerResponse(FilterResponseEvent $event){

        $kernel = $event->getKernel();

        // Only log requests from authenticated users
        if( !$kernel->getContainer()->get('security.context')->getToken() ){
            return;
        }

        $user = $kernel->getContainer()->get('security.context')->getToken()->getUser();        

        $resp = new ApiResponse();
        $resp->setUser($user);
        $resp->setUrl( $event->getRequest()->getRequestUri() );
        $resp->setResponse( $event->getResponse()->getContent() );

        try{
            $kernel->getContainer()->get("doctrine")->getEntityManager()->persist($resp);
            $kernel->getContainer()->get("doctrine")->getEntityManager()->flush($resp);
        }catch(\Exception $ex){ }

    }    

}

And that’s about it!

Note: Per Andras’ comment below the event has been switched to “kernel.terminate”.

JavaScript: Using ChipmunkJS to build ping pong

A couple of weeks ago, we picked up a RaspberryPi for the office and started brainstorming ideas for cool hacks for it. One of the ideas that was floated was using the Pi as a server for some sort of multiplayer JavaScript game. I’ve actually never written a game before so I figured it would be an interesting project. We ended up trying to use node-qt for graphics along with ws to receive input via websockets but the refresh rate of Qt was just to low. A story for another time.

Anyway, so back to ping pong. One of the things we wanted to avoid was writing our own code to manage the game objects, we really wanted to use a game engine for this. The only caveat we had was that the engine had to be UI agnostic since we were planning to output graphics on Qt instead of a HTML5 Canvas. After looking around, most of the JavaScript game engines have strong dependencies on Canvas so we started looking at other options. The two strongest options were Box2JS and ChipmunkJS. We decided to go with ChipmunkJS in part because it was handwritten while Box2JS was automatically converted from ActionScript so the resulting code is harder to follow.

Getting ChipmunkJS setup is relatively painless, just include the file and you’re off to the races. Since we were looking to eventually use Qt, we tried our best to cleanly separate the Chipmunk code from what we’d be using to draw the objects on the screen. Because of that, it’s pretty easy to follow what’s going on with the Chipmunk code. If you take a look at cpPong.js it basically initializes the physics space and then starts the game.

// The physics space size is 640x480, with the origin in the bottom left.
// Its really an arbitrary number except for the ratio - everything is done
// in floating point maths anyway.

var GRABABLE_MASK_BIT = 1<<31;
var NOT_GRABABLE_MASK = ~GRABABLE_MASK_BIT;

if( typeof cp == 'undefined') {
	var cp = require("./cp.js");
}
    
var cpPong = function(){

    this.addDrawables();
    
    var ball = null, paddleOne = null, paddleTwo = null;    
    
    this.v = cp.v;
    this.space = new cp.Space();
        
    this.space.iterations = 10;
    this.space.gravity = this.v(0, 0);
    this.space.sleepTimeThreshold = 0.5;
    this.space.collisionSlop = 0.1; 
    
    this.addFloor();
    this.addWalls();
    
    var mass = 10;    
    var radius = 20;
        
    this.body = this.space.addBody( new cp.Body(mass, cp.momentForCircle(mass, 0, radius, this.v(0, 0))) );            
    this.ball = this.space.addShape( new cp.CircleShape(this.body, radius, this.v(0, 0)) );
    this.ball.setElasticity(1);    
    this.ball.setFriction(0.0);   
    this.ball.setCollisionType(1);
        
    this.paddleOneBody = this.space.addBody( new cp.Body(100000, cp.momentForBox(Infinity, 75, 10)) );        
    this.paddleOne = this.space.addShape( new cp.BoxShape(this.paddleOneBody, 75, 10) ); 
    this.paddleOne.setElasticity(1);
    this.paddleOne.setFriction(0);         

    this.paddleTwoBody = this.space.addBody( new cp.Body(100000, cp.momentForBox(Infinity, 75, 10)) );        
    this.paddleTwo = this.space.addShape( new cp.BoxShape(this.paddleTwoBody, 75, 10) ); 
    this.paddleTwo.setElasticity(1);
    this.paddleTwo.setFriction(0);        
    
    this.scoreboard = {s1: 0, s2: 0};
    
    var self = this;    
    
    this.resetGameObjects();
    this.renderScoreBoard();
    this.isRunning = true;
    this.run();
        
    this.space.setDefaultCollisionHandler(null, null, function(el){     	    	
        if( el.a.isGameEnder || el.b.isGameEnder ){        	        	
        	var pointFor = el.a.pointFor ? el.a.pointFor : el.b.pointFor;
        	
        	self.isRunning = false;
        	
        	self.scoreboard[ pointFor ] += 1;            	
        	self.renderScoreBoard();
        	
        	self.alertRoundOver();
        	self.resetGame();        	        	        	
        }        
    });          
    
};

cpPong.prototype.resetGameObjects = function(){
    this.body.setPos( this.v(20, 320) );
    this.body.setVel( this.v(200, -200) );
    
    this.paddleOneBody.setPos( this.v(320, 60) );
    this.paddleTwoBody.setPos( this.v(320, 420) );
    
    this.paddleOneBody.setVel( this.v(0, 0) );
    this.paddleTwoBody.setVel( this.v(0, 0) );		
};

cpPong.prototype.addFloor = function() { 	
    this.floor = this.space.addShape( new cp.SegmentShape(this.space.staticBody, this.v(0, 0), this.v(640, 0), 0) );
    this.floor.setElasticity(1);
    this.floor.setFriction(0.0);    
    this.floor.setCollisionType(1);
    
    this.floor.isGameEnder = true;
    this.floor.pointFor = "s1";
    this.floor.lineWidth = 5;
    
    this.ceiling = this.space.addShape( new cp.SegmentShape(this.space.staticBody, this.v(0, 480), this.v(640, 480), 0) );
    this.ceiling.setElasticity(1);
    this.ceiling.setFriction(0.0);    
    this.ceiling.setCollisionType(1);
    
    this.ceiling.isGameEnder = true;
    this.ceiling.pointFor = "s2";
    this.ceiling.lineWidth = 5;
};

cpPong.prototype.addWalls = function() {
    var space = this.space;
    
    var wall1 = space.addShape(new cp.SegmentShape(space.staticBody, this.v(0, 0), this.v(0, 480), 0));
    wall1.setElasticity(1);
    wall1.setFriction(0.0);
    wall1.setLayers(NOT_GRABABLE_MASK);
    wall1.setCollisionType(1);
    wall1.lineWidth = 5;
    
    var wall2 = space.addShape(new cp.SegmentShape(space.staticBody, this.v(640, 0), this.v(640, 480), 0));
    wall2.setElasticity(1);
    wall2.setFriction(0.0);
    wall2.setLayers(NOT_GRABABLE_MASK);
    wall2.setCollisionType(1);
    wall2.lineWidth = 5;
};

cpPong.prototype.movePaddle = function(paddle, direction){	
	var target = paddle == "one" ? this.paddleOneBody : this.paddleTwoBody;
	var velocity = direction == "right" ? 500 : -500;
	
    var pt = target.getVel().add( this.v(velocity, 0) ); 
    target.setVel( this.v(velocity, 0) );
    // target.setVel( pt );
};

//Export the Underscore object for **Node.js**, with
// backwards-compatibility for the old `require()` API. If we're in
// the browser, add `_` as a global object via a string identifier,
// for Closure Compiler "advanced" mode.
if (typeof exports !== 'undefined') {
  if (typeof module !== 'undefined' && module.exports) {
    exports = module.exports = cpPong;
  }
  exports.cpPong = cpPong;
} else {
  window.cpPong = cpPong;
}

With the physics space setup, the next step is setting up the drawing. Following the Chipmunk demo’s lead, I added specific “draw” methods to the Prototypes of each shape. Doing this, allows you to just iterate over each shape in the scene and call “draw” to have it appear on Canvas or Qt. Check out pong.html to see how we added the draw methods to the shapes. And that’s about it! The only other interesting part is using “requestAnimationFrame” to avoid using setTimeout to update the scene.

var scoreboardTemplate = _.template( $("#scoreboardTemplate").html() );

var ctx;
var canvas = $("canvas")[0];
 
var canvasBackend = {
        
    scale: 0,
    
    run: function(){ 
    	
    	if( this.isRunning == false ){
    		return false;
    	}
    	
        this.drawScene();
        this.space.step(1/60);                
        
        var raf = window.requestAnimationFrame
        || window.webkitRequestAnimationFrame
        || window.mozRequestAnimationFrame
        || window.oRequestAnimationFrame
        || window.msRequestAnimationFrame
        || function(callback) {
            return window.setTimeout(callback, 1000 / 60);
        };      
        
        var draw = _.bind(this.run, this);
        raf(draw);             
    },
    
    drawScene: function(){
        var self = this;
        this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
        this.space.eachShape(function(shape) {          
            shape.draw(self.ctx);           
        });        
    },
    
    resetGame: function(){
    	var self = this;
        window.setTimeout(function(){
            self.resetGameObjects();
            self.isRunning = true;
            self.run();
        }, 3000);    	
    },
    
    renderScoreBoard: function(){
    	$("#scoreboard").html( scoreboardTemplate(this.scoreboard) );
    },
    
    alertRoundOver: function(){
    	alert("Round over!");
    },
    
    addDrawables: function(){
        
        this.canvas = $("canvas")[0];
        this.ctx = $("canvas")[0].getContext('2d');
                                  
        var width = Math.ceil( (window.innerHeight * 640) / 480 ) + 25;
        
        $("canvas").attr("height", window.innerHeight);
        $("canvas").attr("width", width);        
        
        // var width = this.canvas.width;
        var height = this.canvas.height;
                
        window.scale = 0;          
        
        if (width / height > 640 / 480) {
            window.scale = height / 480;
        } else {
            window.scale = width / 640;
        }                       
        
        var self = this;
        $(window).keydown(function(e){                    
            if( e.keyCode == 39 ){
                self.movePaddle("one", "right");
            }
            
            if( e.keyCode == 37 ){         
            	self.movePaddle("one", "left");
            }
                    
            if( e.keyCode == 87 ){
            	self.movePaddle("two", "right");
            }
            
            if( e.keyCode == 81 ){         
            	self.movePaddle("two", "left");
            }
        });         
        
        cp.Shape.prototype.point2canvas = function(point){          
            return cp.v( point.x * window.scale, (480 - point.y) * window.scale );        	
        };
        
        cp.Shape.prototype.drawLine = function(ctx, a, b){        
            a = this.point2canvas(a); 
            b = this.point2canvas(b);
            ctx.beginPath();
            ctx.moveTo(a.x + .5, a.y);
            ctx.lineTo(b.x + .5, b.y);
            ctx.stroke();
        };
        
        cp.Shape.prototype.drawCircle = function(ctx, c, radius){
            var c = this.point2canvas(c);
            ctx.beginPath();
            ctx.arc(c.x, c.y, window.scale * radius, 0, 2 * Math.PI, false);
            ctx.fill();
            ctx.stroke();           
        }
        
        cp.SegmentShape.prototype.draw = function(ctx) {                        
            ctx.strokeStyle = "green";                      
            ctx.fillStyle = "green";
            ctx.lineCap = 'round';
            ctx.lineWidth = this.lineWidth ? this.lineWidth : Math.max(1, this.r * window.scale * 2);
            this.drawLine(ctx, this.ta, this.tb);
        };

        cp.CircleShape.prototype.draw = function(ctx) { 
            ctx.lineWidth = 1;
            ctx.strokeStyle = "black";                      
            ctx.fillStyle = "red";  
            this.drawCircle(ctx, this.tc, this.r);
            
            ctx.strokeStyle = "white";                      
            ctx.fillStyle = "white";
            this.drawLine(ctx, this.tc, cp.v.mult(this.body.rot, this.r).add(this.tc));
        };
        
        cp.PolyShape.prototype.draw = function(ctx){
            
            ctx.strokeStyle = "blue";                      
            ctx.fillStyle = "blue";
            ctx.lineWidth = 1;
            
            ctx.beginPath();

            var verts = this.tVerts;
            var len = verts.length;
            var lastPoint = this.point2canvas( new cp.Vect(verts[len - 2], verts[len - 1]) );
            
            ctx.moveTo(lastPoint.x, lastPoint.y);

            for(var i = 0; i < len; i+=2){
                var p = this.point2canvas(new cp.Vect(verts[i], verts[i+1]));
                ctx.lineTo(p.x, p.y);
            }
            
            ctx.fill();
            ctx.stroke();
        };      
        
    },
};

_.extend( cpPong.prototype, canvasBackend );

There’s a live demo running here and of course the code on GitHub.

Any questions or comments welcome!

SQL: 5 Tips and tricks to impress your DBA friends

I was poking around the ThoughtBot blog a couple of days ago and ran across a post titled Refactoring Ruby Iteration Patterns to the Database. At a high level, the post was summarizing how you can take an ActiveRecord aggregation (a sum in this case) and run it in directly in your RDMS with SQL. Not really rocket science, but it was a keen reminder of how ORMs often mask over much of the power of “regular” SQL. This isn’t a specific criticism of ActiveRecord, it’s an issue with every ORM from Doctrine to Hybernate.

We’ve actually been writing some straight SQL lately, mostly for analytics work, so I had the team shoot over their favorite “maybe hidden” SQL feature. Since life is better with examples, the sample use cases and queries are written against a schema describing the “items” found on a “receipt” which are optionally related to a “category”. The SQL to create the schema is:

CREATE TABLE IF NOT EXISTS `receipt` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `total` decimal(15,2) DEFAULT 0,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1 ;

INSERT INTO `receipt` (id) VALUES (7006),(3493);

CREATE TABLE IF NOT EXISTS `item` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `category_id` int(11) DEFAULT NULL,
  `receipt_id` int(11) DEFAULT NULL,
  `total` decimal(15,2) DEFAULT '0.00',
  `quadrant` varchar(8) COLLATE utf8_unicode_ci NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=40779 ;

--
-- Dumping data for table `item`
--

INSERT INTO `item` (`id`, `category_id`, `receipt_id`, `total`, `quadrant`) VALUES
(1925, 390, 1712, 1.00, 'WE'),
(11834, 263, 3168, 8.40, 'AX'),
(18012, 263, 3493, 7.99, 'AX'),
(25128, 390, 5681, 14.99, 'WE'),
(26898, 267, 4214, 9.99, 'AX'),
(27486, 267, 7345, 1.49, 'AX'),
(33331, 263, 7006, 29.94, 'BT'),
(34947, 390, 9053, 20.01, 'BT'),
(39309, 390, 13544, 14.09, 'BT'),
(40778, 267, 14153, 5.94, 'BT');

Note: SQLFiddle is unfortunately down right now or I’d add this as a fiddle. Anyway, if you have the schema setup feel free to run the queries as you go down the list.

Order rows by fixed ordering of a column

At some point, you might find yourself needing to sort a list of rows by a column in an arbitrarily enforced order. For example, say on our item table, you needed to sort the rows by the “quadrant” column such that WE was first, followed by AX, and finally BT.

Turns out, it’s possible to specify an arbitrary ordering using the ORDER BY FIELD statement:

SELECT * FROM item WHERE 1 ORDER BY FIELD (quadrant, "WE", "AX", "BT")

Check that a LEFT JOIN relation exists

If you’re only running JOINs on columns with foreign key relations this isn’t an issue, but what happens if you need to run a JOIN where a FK doesn’t necessarily exist? In our example, lets say you wanted to select only the items which had a corresponding row in the “receipt” table.

The most straightforward way to accomplish this is generally to check that the JOIN’ed column on the related table isn’t NULL:

SELECT * FROM item
LEFT JOIN receipt ON receipt.id = item.receipt_id
WHERE receipt.id IS NOT NULL

Assign an aggregate value to a variable and re-use

One of the SQL features that’s usually glossed over or ignored in web development is the ability to create variables and then reuse them in subsequent statements. With this schema, an example would be calculating the “% of total spend” for the individual items - most people would run one query to generate the total and then a separate query to calculate the % of spend. For something trivial like this it doesn’t matter but if you were involving complex WHERE predicates it could be a nice performance boost.

The syntax for variables is relatively easy and it’s actually a powerful concept:

SET @total_spend = (SELECT total_spend FROM (SELECT SUM(item.total) AS total_spend FROM item) it);
SELECT receipt_id, (SUM(item.total) / @total_spend) * 100 FROM item GROUP BY receipt_id;

Add synthetic “pseudo” columns using variables

This one is a Matt Daum favorite and pretty handy. Looking at the example, say that you wanted to assign a “sequence” value to each item depending on their rank order based on “total” within their “category_id”. In plain English, for each “category_id” you want to assign the most expensive item a “1”, the second most a “2”, and so on.

This seems straightforward, but try and construct a result set using only a GROUP BY or some combination of sub queries, I’ll wait. Turns out, the easiest way to accomplish this is to use variables to construct a “pseudo” column that increments and resets when the category changes.

SET @type='';
SET @num = 1;
 
SELECT @num := if(@type = category_id, @num + 1, 1) as sequence, @type := category_id as extra, bfa.*
FROM (
   SELECT id, category_id, total FROM item ORDER BY category_id ASC, total DESC
) bfa

Select only GROUP’ed rows that fulfill a second clause

Sorry for the terrible description, an example will make it clearer. Given our schema, lets say you wanted to select *only* the most expensive items per category, how could you set about doing it? The obvious approach would be using some combination of GROUP BY and MAX but unfortunately because of the semantics of GROUP BY that wont work as expected.

A better approach, is to leverage an INNER JOIN along with MAX() to only select the rows that match the max total per category:

 SELECT item.id,
       item.category_id,
       item.total
FROM   item
       INNER JOIN (SELECT Max(it0.total) AS total,
                          it0.id, it0.category_id
                   FROM   item it0 GROUP BY it0.category_id
                   ) it0
               ON item.total = it0.total AND it0.category_id = item.category_id  
GROUP  BY item.category_id

The caveat here is that you’re really selecting the highest total, so if two rows have the same total you’re not guaranteed which one you’ll end up with. This approach also scales out, in the sense that you can add additional INNER JOINs to limit the resultset in situations where you’re getting tripped up by GROUP BYs and ORDER BYs.

Anyway, as always, we’d love to hear your favorite tips and tricks in the comments!

Phonegap: Fixing black bars on iOS7/iPhone5

Last week, we were using Phonegap Build to build an iOS IPA for a project an ran into an odd issue. When we launched the app on an iPhone 5 running iOS7 black bars appeared at the top and bottom of the screen. On top of that, when launching the app we were observing the same issue with the splash screen.

In typical Phonegap fashion, Googling for people suffering from similar issues brought back dozens of results across several versions each with a different root cause and solution. One of the first promising leads we noticed was this comment in the top of the default Phonegap template:

<!-- WARNING: for iOS 7, remove the width=device-width and height=device-height attributes. See https://issues.apache.org/jira/browse/CB-4323 -->
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi" />

Unfortunately, that comment seems either be invalid or the issue has since been resolved since removing those meta attributes had no effect.

As it turns out, the issue is that the “config.xml” file created by the default Phonegap “Hello world” project is missing an entry for the iPhone5’s screen size. Oddly enough, there’s actually a splash screen image of the correct height in the demo project but its not referenced in the config file. To resolve this issue, you just need to add this line to your config.xml:

<gap:splash gap:platform="ios" height="1136" src="res/screen/ios/screen-iphone-portrait-568h-2x.png" width="640" />

Just make sure that you have a file named “res/screen/ios/screen-iphone-portrait-568h-2x.png” where the rest of your splash screens are and you should be good to go.

Javascript: Phonegap with Backbone and Marionette

We’ve worked with our clients to execute a couple of Phonegap apps lately and in doing so used Backbone and Marionette to structure the apps. For some background, Phonegap, now Apache Cordova, is a project that allows developers to build native iOS, Android, and WP apps using HTML, CSS, and of course Javascript. As a developer, you write some HTML and Javascript, pass it to Phonegap, and Phonegap returns a native app that displays your code inside a WebView without any surrounding chrome. On top of this, Phonegap provides a set of Javascript APIs that allow you to leverage some of the device’s native functions, like the accelerometer or camera.

Writing apps with HTML/JS is great, but it presents some issues particularly that triggering a full page reload for navigation appears “non-native” on mobile. On technique to combat this issue is developing single page Javascript apps. In a single page app, the entire page is never reloaded, instead portions of the DOM are dynamically re-rendered using Javascript. Because of the complexity of managing this process with straight Javascript, several libraries, including Backbone, have been developed to simplify this process. Marionette is a companion library to Backbone which provides a set of features to make managing complex applications easier. So, what were the pros and cons of using Backbone with Marionette to build a Phonegap app?

The Good

Structure: Using a library like Backbone guides you to structuring your code in loosely a MVC design pattern. Coupled with a templating framework, this ends up producing code that’s much easier to follow and maintain. Before Phonegap, I’d already started using Backbone in traditional Symfony2 projects just to get the benefit of better structured code.

It’s familiar: This is a personal preference, but compared to declarative frameworks like AngularJS, Backbone/Marionette apps “look” like regular HTML/JS. Primarily because of the regular HTML templates and use of jQuery, the learning curve for Backbone isn’t very steep. A team member without Backbone experience can quickly grok how things work and make changes quickly.

The Not So Good

“There’s more than one way to do it”: Although flexibility is good, having a generally “well recommended” way usually helps decrease confusion and frustration. With Backbone/Marionette, there doesn’t seem to be much consensus on how to do “standard” things like message passing or even structuring the app as a whole. There’s dozens of StackOverflow answers debating the “best” way to approach things, often with outright conflicting viewpoints. In contrast, Symfony2 and Rails typically have “best practices” for approaching common tasks, even if they’re not appropriate in every circumstance.

Documentation: The documentation for Marionette, and to a lesser degree Backbone, is pretty lacking. The Marionette documentation explains how the individual components work but they didn’t provide much insight to the “big picture”. The docs were also missing some explanation into the “why”, which of course lead to StackOverflow answers and then differing viewpoints. Marionette is also short example apps which Backbone does have. The Backbone documentation is thorough, its just a bit hard to navigate and purposefully introduces the “There’s more than one way to do it” mantra.

Anyway, on the whole using Backbone with Marionette to build a Phonegap app was a positive experience. Unfortunately, our clients’ own the code so we can’t release it. That said, we’ll do our best to build something in-house that we can share.