Frameworks

17

Mar 12

PHP Micro-Framework Roundup

I stumbled on what folks are calling micro-frameworks a few months ago when searching for an easier way to create RESTful services. I was using Zend Framework at the time with Zend_Rest_Route and Zend_Controller_Rest. That worked fine, except that it was super slow for something so simple, and there was no way to handle hierarchical resources. For example it was impossible to route requests to something like: /folder/3/file/2. Instead one would have to create a resource like /folder-files/2/?folder_id=3. Thugly.

Anyway, I knew that all the cool Ruby kids were using Sinatra – at least for prototyping, and so I started looking for Sinatra inspired PHP frameworks, and guess what? There are lots of them! Not surprisingly, they appear very similar to one another in a Sinatra-ish sort of way. Most of them allow one to define a resource and its implementation using lambdas. For example:

$app = new App();
 
$app->get('/folder/{folder_id}/file/{file_id}', function($folder_id, $file_id) {
    return "Hello World!";
});

After experimenting with a number of these frameworks, I thought I’d give my initial impressions along with some benchmarks and any interesting contextual information I found.

The Players:

1. Silex (Requires PHP >= 5.3):

The Silex project is led by the ever prolific Fabien Potencier, the creator of the Symphony framework.  Silex is available under an MIT License, and has commercial backing from Sensio Labs – which also backs Symphony, Doctrine and Twig to name a few.

Silex has a couple of interesting features that are unique among PHP micro-frameworks.  First is its simple and elegant DI container based on Pimple, another Sensio Labs product.  The Silex Application class extends Pimple, which is an implementation of the ArrayAccess interface that has been part of PHP since v5.0.  Doing this makes it possible to use an instance of its Application class as if it were an array.  For example, you can attach any object to an Application object with the following syntax:

$app = new Silex\Application();
 
$app['config'] = new Config($config_path);

This gives us an incredibly easy way to inject dependencies when testing, or using the application in other environments, such as the command line.

The other unique aspect of Silex is that it is built on a number of Symphony2 components, which are included in a phar distribution.  These include an autoloader, and request and response classes that make handling things like json and http headers much easier.  There is decent but not extensive documentation available on their website.

2. Slim (Requires PHP >= 5.1)

Slim takes a minimalist approach while providing much of what you need to get started.  It supports hooks for executing code at different points in its life-cycle, and tie-ins for Rack-like middleware.  In addition, it contains classes for managing requests, responses, cookies, logging, views, HTTP caching, and more.  Because Slim supports PHP >= 5.1, it does not utilize namespaces or lambdas in its library code.  However, it allows for the use of lambdas when defining routes as long as you’re running PHP >= 5.3. The following is an example of defining routes with and without lambdas.

require 'Slim/Slim.php'; 
 
$app = new Slim();
$app->get('/hello/:name', function ($name) {
    echo "Hello, $name!";
});
 
$app->get('/hello2/:name', 'hello2');
 
function hello2($name) {
    echo "Hola $name!";
}
 
$app->run();

The Slim framework has well organized and thorough documentation, and it has an active user base with over a thousand users watching it on github.

3. Limonade (Requires PHP >= 5.1.6, may work with older versions)

Limonade is the oldest of the Sinatra inspired frameworks.  Rather than defining an application class, Limonade’s single library file defines a number of functions that you use to define routes and run the application.  Here’s what that looks like:

require_once 'vendors/limonade.php';
 
dispatch('/', 'hello');
 
function hello() {
    return 'Hello world!';
}
run();

This may be terse, but it seems messy to rely on global functions, especially considering the number of functions limonade.php defines.  If you can get past this design decision, the rest of the library looks well written, and because its been out there for a while, it appears quite mature.  The framework provides hooks and filters, as well as a view component that supports layouts.  Additionally, it has good documentation on its github page.

4. Flight (Requires PHP >= 5.3)

Everything about Flight appears simple and elegant, including its website and documentation.  It is a bare-bones framework that takes a somewhat unique approach in a couple of ways.  The most obvious is that it uses static methods, and so does not require that the application class be instantiated.  Here’s what a sample app looks like:

require 'flight/Flight.php';
 
Flight::route('/', function(){
    echo 'hello world!';
});
 
Flight::start();

A lot of people decry even the liberal use of static methods, but it makes sense when all that is needed is a single instance of something. It does make it impossible to implement the ArrayAccess interface that Silex makes use of, but Flight does something similar with its register method which allows you to map classes to a static method that will in turn return a shared instance of the class you specify. For example:

// Register class with constructor parameters
Flight::register('db', 'Database', array('localhost','mydb','user','pass'));
 
// Get an instance of your class
// This will create an object with the defined parameters
//
//     new Database('localhost', 'mydb', 'user', 'pass');
//
$db = Flight::db();

The first parameter is the name of the method to create, the second is a class name, and the third is an array of parameters. Flight::db() will subsequently lazy load the Database class the first time it is called, then return a shared instance on each additional invocation.

The second unique feature is what the author calls filters, which can be executed before and after any function, and can change both the parameters and output of the function they’re attached to.  This feature is in place of the hooks that other frameworks use to inject code into different parts of the application life-cycle.  Here’s an example from the Flight documentation.

// Map a custom method
Flight::map('hello', function($name){
    return "Hello, $name!";
});
 
// Add a before filter
Flight::before('hello', function(&$params, &$output){
    // Manipulate the parameter
    $params[0] = 'Fred';
});
 
// Add an after filter
Flight::after('hello', function(&$params, &$output){
    // Manipulate the output
    $output .= " Have a nice day!";
}
 
// Invoke the custom method
echo Flight::hello('Bob');

Additionally, Flight contains views with layouts, request and response objects and http caching.  All in all it appears to be a well executed and innovative framework.

5. Fat-Free Framework (Requires PHP >= 5.3)

Coming soon . . .