Introducing Silex Bootstrap, Aptoma Edition

Introducing Silex Bootstrap, Aptoma Edition

At Aptoma, we use the Silex micro-framework for quite a few projects. As opposed to its bigger brother, Symfony2 Full Stack, Silex provides only a minimal prescription for how to do things, meaning you’re free to structure and organize your application as you see fit. Having no rules or guidelines also means that you’re free to create a total mess. In fact, Silex makes that rather easy. There’s nothing stopping you from putting your entire application into a single file, even if that application contains tens of thousands lines of code.

The benefit of using Silex instead of the full stack framework is that it’s a lot less intrusive. You only use the parts you need, and it’s quite simple to keep the fact that you’re using Silex hidden from most of your code. That has two clear benefits:

  1. You can easily reuse most of your code in a non-Silex app
  2. You can easily replace Silex with something else

But, as the saying goes, with great flexibility comes great opportunities for messing up.

How not to mess up

The first example on Silex’s homepage is a great example of how simple Silex is. You can literally get to “Hello World” with autoloading, full blown routing and output escaping in six lines of code:

require_once __DIR__.'/../vendor/autoload.php'; 

$app = new SilexApplication(); 

$app->get('/hello/{name}', function($name) use($app) { 
    return 'Hello '.$app->escape($name); 
}); 

$app->run(); 

However, this file is actually doing a lot of stuff:

  1. Act as front controller
  2. Set up autoloading
  3. Initialise application
  4. Define controllers
  5. Map dynamic urls to controllers
  6. Render views

You can easily imagine that an app with more complex config, more actions, more complex controllers, and more complex views will easily get unwieldy if kept in one file.

For our projects, we’ve decided to split this off into multiple files and folders.

Our front controller is put in the web folder, and only triggers bootstrap and execution:

$app = require __DIR__ . '/../app/app.php';

if ($app instanceof SilexApplication) {
    $app->run();
} else {
    echo 'Failed to initialize application.';
}

The basic bootstrap and config is placed in an app folder, with app.php as the starting point. Here we define default config that we pass off to our own AppSilexApplication class, that extends SilexApplication. This is also where we register controllers. We use the “controllers as services” approach. A typical entry looks like this:

$app['app.default_controller'] = $app->share(
    function () use ($app) {
        return new AppControllerDefaultController($app['twig'], $app['logger']);
    }
);

As you can see, we use objects rather than closures for our controllers, as it allows us to separate the controller logic from the bootstrap and routing. Also, these objects are completely unaware of the existence of Silex. We’ll revisit the reasoning behind this in a later blog post, but the link above is a good start for now.

From app.php we also include a routing.php file with all the url to controller mapping:

$app->get('/', 'app.default_controller:indexAction');

All our application classes are located in a src folder.

This gives us a simple three-folder setup:

  • web for front controller and public files
  • app for bootstrap and config
  • src for classes

Silex Bootstrap

To make it easy to get up and running fast, and for encouraging a consistent approach to code organization and design, we have put this common bootstrap in a repo on GitHub: aptoma/silex-bootstrap.

We’ve made this public and also added it to Packagist. When we want to start a new project, we simply run:

composer create-project aptoma/silex-bootstrap my-new-project

This gives the basic setup, and also installs our companion package silex-extras, which contains various services and utilities that are used by multiple projects.

The idea is that Silex Bootstrap provides the starting point, while Silex Extras is for sharing code and keeping up to date across projects.

We also include a Gruntfile with basic commands for running PHPUnit and a few QA tools.

Silex Extras

Silex Extras is our collection of commonly used services and helpers. This is generally stuff that is either too specific or too small to warrant a separate repo, but comprehensive enough that simple copy and paste between projects would become tedious.

As a lot of projects tend use similar services, it’s also helpful to store them in a centralised repo, so that any improvements can easily be applied to other projects by simply doing composer update aptoma/silex-extras.

When we develop applications, anything app specific goes into the App namespace, whereas anything that is not app specific goes into the Aptoma namespace. Anything in the Aptoma namespace is a candidate for reuse, and thus also a candidate for going into Silex Extras. However, we don’t put stuff into Silex Extras until at least two different projects need it.

Contributions?

We’ve primarily made these two repos public and open source in order to easily integrate through Packagist and to show the world how we do stuff, and thus hopefully help others get started quickly with Silex.

We believe Silex Bootstrap is rather stripped down and generic, so it should be reusable with very little modification by others. It does have a dependency on Silex Extras for a more verbose Application class, but nothing that can’t easily be stripped out after project initialisation. If you have any suggestions or improvements, feel free to open issues or pull requests.

As for Silex Extras, a key feature of this repo is to not have to repeat boilerplate code across projects. That means that it is a bit intrusive in setting up logging, error handlers and services, which may or may not fit everyone’s needs. We are open for modifications that allow overriding and extending behavior, but we are not likely to accept changes that will require modifying existing projects depending on this package. However, feel free to voice any suggestions, and we might very well end up with a more flexible design.

Gunnar Lium