First Venture into the Slim Framework

One of the traditions at Shutterstock is a quarterly Coderage or Hackathon, when all the developers group up and build something outside of planned workflows. These projects could vary from quick prototypes of internal service mashups to large-scale refactors of problematic areas in the codebase. For the latest 'rage, the 2014 Q4 one, I had a clear and ambitious project to tackle. Together with a team of fellow PHP developers we set forward on building out our Bigstock API, with me focusing on rewriting the core leaning on the Slim framework for routing.

This was my first venture into Slim. I've been hesitant on routing micro-frameworks, dismissing them as a bloated conditional pattern for the REQUEST_URI parameter, but figured on giving it a chance for this cross-team project. Between the pseudo-REST interface of the API and the standardized json response structure maybe Slim could simplify our application a bit.

My first wanders into the documentation were rough. I was focused on building out a bootstrap, something to load up the configuration and do basic authentication, and most of their examples and documentation were focused on a pre-loaded application. Which makes sense. The Slim documentation should focus on working with Slim, not a how-to on writing a generic bootstrap that just happens to load it up.

Once I got past that I started plugging in objects via both the dependency injection and singleton options. This was very useful for things like the database class and memcache client. The primary object (Slim\Slim) can hold onto instantiated objects so you can pass them from route to route w/o much difficulty. As an example…

  1. $app = new Slim\Slim();

  2. $app->container->singleton('db', function() use ($config) {

  3. return new Database($config);

  4. });

  5. $app->get('/categories', function() use ($app) {

  6. $categories = $app->db->select("QUERY");

  7. // do stuff

  8. });

Since the main logic for each route is housed in a callback function, which is the new coolness for today's modern PHP developer, you need a way to inject in necessary pieces from the bootstrap and config step. The primary object can be treated like a resource locater for this, allowing you to only pass in one object for each route. If you really wanted to you could pass in extra pieces of information after $app for each route, too.

Another cool thing outside of the routing was the concept of hooks. Several hooks are called during the lifecycle of the Slim object, hooks that trigger both before and after each route is matched, allowing you to execute code at different steps of the application. This allowed us to do global stat collection after each route was matched. Of course, we could just do this collection at the bottom of index.php or something, but this way we kept our stats handling wrapped in a callback. Because that's cool.

I ran into two difficulties with Slim, one due to my limited knowledge and one just frustrating. The first one was the authentication. There are a lot of checks to perform before any routes are matched, checks to make sure that the requesting agent is authenticated and that they are enabled and such, and if those fail we have specific error responses to return. However, there does not appear to be an easy way to eject out of the bootstrap via a Response object. One of my colleagues has suggested using middleware, an offering of Slim that I didn't have time to look into during the 24-hour Coderage. So there may be a solution, just one that is not explored yet.

The other one is the use case. Slim is a microframework that does routing. It is not geared especially for APIs or json returns. I resisted for a while but eventually gave into the idea of overriding the Response object to add in our json message structure. Still, I feel like this may be a bit bloated for our usage. We don't need any views or rendering, flash messaging or sessions. If there is an alternative for building APIs, an alternative that has the callback-style routing, a method of injection, and a way of filling out a pre-defined json message structures, I feel like that would have been a better fit for this project.

So, Slim worked. We got the majority of the bootstrapping and routing done, now its just a matter of mapping functionality over and testing. And it was fun to embark on a crash course of this framework. It'd be nice to have something more geared for API builds, but Slim works. Now I just have to figure out if I want to use it or Aura.Router for some of the internal service builds I have been toying around with…