Create your own framework... on top of the Symfony2 Components (part 1)

Fabien Potencier

January 03, 2012

This article is part of a series of articles that explains how to create a framework with the Symfony2 Components: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12.

Symfony2 is a reusable set of standalone, decoupled, and cohesive PHP components that solve common web development problems.

Instead of using these low-level components, you can use the ready-to-be-used Symfony2 full-stack web framework, which is based on these components... or you can create your very own framework. This series is about the latter.

If you just want to use the Symfony2 full-stack framework, you'd better read its official documentation instead.

Why would you like to create your own framework?

Why would you like to create your own framework in the first place? If you look around, everybody will tell you that it's a bad thing to reinvent the wheel and that you'd better choose an existing framework and forget about creating your own altogether. Most of the time, they are right but I can think of a few good reasons to start creating your own framework:

  • To learn more about the low level architecture of modern web frameworks in general and about the Symfony2 full-stack framework internals in particular;

  • To create a framework tailored to your very specific needs (just be sure first that your needs are really specific);

  • To experiment creating a framework for fun (in a learn-and-throw-away approach);

  • To refactor an old/existing application that needs a good dose of recent web development best practices;

  • To prove the world that you can actually create a framework on your own (... but with little effort).

I will gently guide you through the creation of a web framework, one step at a time. At each step, you will have a fully-working framework that you can use as is or as a start for your very own. We will start with simple frameworks and more features will be added with time. Eventually, you will have a fully-featured full-stack web framework.

And of course, each step will be the occasion to learn more about some of the Symfony2 Components.

If you don't have time to read the whole series, or if you want to get started fast, you can also have a look at Silex, a micro-framework based on the Symfony2 Components. The code is rather slim and it leverages many aspects of the Symfony2 Components.

Many modern web frameworks call themselves MVC frameworks. We won't talk about MVC here as the Symfony2 Components are able to create any type of frameworks, not just the ones that follow the MVC architecture. Anyway, if you have a look at the MVC semantics, this series is about how to create the Controller part of a framework. For the Model and the View, it really depends on your personal taste and I will let you use any existing third-party libraries (Doctrine, Propel, or plain-old PDO for the Model; PHP or Twig for the View).

When creating a framework, following the MVC pattern is not the right goal. The main goal should be the Separation of Concerns; I actually think that this is the only design pattern that you should really care about. The fundamental principles of the Symfony2 Components are centered around the HTTP specification. As such, the frameworks that we are going to create should be more accurately labelled as HTTP frameworks or Request/Response frameworks.

Before we start

Reading about how to create a framework is not enough. You will have to follow along and actually type all the examples we will work on. For that, you need a recent version of PHP (5.3.8 or later is good enough), a web server (like Apache or NGinx), a good knowledge of PHP and an understanding of Object Oriented programming.

Ready to go? Let's start.

Bootstrapping

Before we can even think of creating our first framework, we need to talk about some conventions: where we will store our code, how we will name our classes, how we will reference external dependencies, etc.

To store our framework, create a directory somewhere on your machine:

$ mkdir framework
$ cd framework

Coding Standards

Before anyone starts a flame war about coding standards and why the one used here suck hard, let's all admit that this does not matter that much as long as you are consistent. For this book, we are going to use the Symfony2 Coding Standards.

Components Installation

To install the Symfony2 Components that we need for our framework, we are going to use Composer, a project dependency manager for PHP. First, list your dependencies in a composer.json file:

{
    "require": {
        "symfony/class-loader": "2.1.*"
    }
}
 

Here, we tell Composer that our project depends on the Symfony2 ClassLoader component, version 2.1.0 or later. To actually install the project dependencies, download the composer binary and run it:

$ wget http://getcomposer.org/composer.phar
$ # or
$ curl -O http://getcomposer.org/composer.phar

$ php composer.phar install

After running the install command, you must see a new vendor directory that must contain the Symfony2 ClassLoader code.

Even if we highly recommend you the use of Composer, you can also download the archives of the components directly or use Git submodules. That's really up to you.

Naming Conventions and Autoloading

We are going to autoload all our classes. Without autoloading, you need to require the file where a class is defined before being able to use it. But with some conventions, we can just let PHP do the hard work for us.

Symfony2 follows the de-facto PHP standard, PSR-0, for class names and autoloading. The Symfony2 ClassLoader Component provides an autoloader that implements this PSR-0 standard and most of the time, the Symfony2 ClassLoader is all you need to autoload all your project classes.

Create and empty autoloader in a new autoload.php file:

<?php
 
// framework/autoload.php
 
require_once __DIR__.'/vendor/symfony/class-loader/Symfony/Component/ClassLoader/UniversalClassLoader.php';
 
use Symfony\Component\ClassLoader\UniversalClassLoader;
 
$loader = new UniversalClassLoader();
$loader->register();
 

You can now run the autoload.php on the CLI, it should not do anything and should not throw any error:

$ php autoload.php

The Symfony website has more information about the ClassLoader component.

Composer automatically creates an autoloader for all your installed dependencies; instead of using the ClassLoader component, you can also just require vendor/.composer/autoload.php.

Our Project

Instead of creating our framework from scratch, we are going to write the same "application" over and over again, adding one abstraction at a time. Let's start with the simplest web application we can think of in PHP:

<?php
 
$input = $_GET['name'];
 
printf('Hello %s', $_GET['name']);
 

That's all for the first part of this series. Next time, we will introduce the HttpFoundation Component and see what it brings us.

Discussion

gravatar Eanz  — January 03, 2012 23:16   #1
Thanks, glad you could find the time for these tutorials on top of maintaining Sf2
gravatar Alessandro Desantis  — January 03, 2012 23:52   #2
Shouldn't it be:

printf('Hello %s', $input);

Otherwise I don't see why you are declaring the variable.
gravatar Tuong Le  — January 04, 2012 00:38   #3
Can't wait for the next tutorials
gravatar Marijn.huizendveld@gmail.com  — January 04, 2012 01:26   #4
Happy New Year Fabien,

It's great to see you writing articles like these again :-)

Perhaps you should add a small warning that this example is vulnerable for XSS attacks?

`$input = htmlspecialchars($_GET['name'], ENT_COMPAT, 'UTF-8');`

Cheers,

Marijn
gravatar Nihao  — January 04, 2012 07:49   #5
Perhaps even

$input = htmlspecialchars(isset($_GET['name'])?$_GET['name']:null, ENT_COMPAT, 'UTF-8');
gravatar superhaggis  — January 04, 2012 09:21   #6
To #4 and #5:

"Let's start with the simplest web application we can think of in PHP"

Best practice will be enforced by the use of Components and other custom code, I'm sure. Patience is a virtue! :)
gravatar Knarf  — January 04, 2012 17:31   #7
Drop the htmlspecialchars and drop the printf. Write instead:

$input = isset($_GET['name']) ? $_GET['name'] : '';
echo 'Hello ', $input;
gravatar Leric  — January 05, 2012 02:56   #8
Great post, symfony2 has created a evolving environment for php development.
But is it more consistent to rely on the composer generated autoload.php?
gravatar Andrew  — January 05, 2012 10:18   #9
I think you are all missing the point here, he's not writing production code here - the point is to write a peice of code and then replace it with something from the sf2 components, probably explaining why it's better / safer etc.

:)
gravatar Marc Weistroff  — January 05, 2012 11:52   #10
Cool !
gravatar nimmen dimmur  — January 05, 2012 12:00   #11
when i started writing framework using some of symfony2 components i have run into namespace problems(unexpected behaviour) with classloader,
https://github.com/nimmen/ClassLoader/commit/0881a02d3210fd6376abd3b62f0c665642d310bb
hope this will help someone.
(im using ZF, SF2 and several other components/frameworks/libraries)
gravatar Marijn Huizendveld  — January 05, 2012 12:59   #12
@Andrew, sure we're not writing production code. However, there are going to be a lot of newbies that will simply copy paste code. At least give them a fair warning about the vulnerabilities of the script.
gravatar Fabien Potencier  — January 05, 2012 14:24   #13
The problems mentioned in the comment with the PHP script are indeed covered in part 2.
gravatar Tito Miguel Costa  — January 07, 2012 00:20   #14
For me, to make this example work, had to fix the path for the UniversalClassLoader.php file to __DIR__.'/vendor/symfony/symfony/src/Symfony/Component/ClassLoader/UniversalClassLoader.php' and had to remove the comment on top of the composer.json file
gravatar Luis Cordova  — January 15, 2012 12:23   #15
you know what Fabien? it could have been fun to expound further the custom use of the autoloading so for instance the usage of this class here https://gist.github.com/221634

perhaps an example of how to customize our autoloader with say a different directory separation character like - and such.

Therefore we could have classes going like

Framework-Controller
Framework-View
Framework-Model
etc classes

hmm just an idea, that would be a good way of knowing the inner parts of the components which is what the goal of this series is.
gravatar Javier L√≥pez  — January 18, 2012 17:08   #16
"For this **book**, we are going to use ..." I wonder if this is just a typo, ;)
gravatar ???? ???????????  — January 24, 2012 21:33   #17
PHP bible :)
gravatar mina gerges  — January 29, 2012 12:00   #18
when i try $ php composer.phar install
I get "???????????????????????????????????????????????????????????????????????????????????????????????????%?????????????" Can any one help me ??
gravatar kevin folinus  — February 21, 2012 03:00   #19
$ php composer.phar install was giving me the error
[Composer\DependencyResolver\SolverProblemsException]
[Job(cmd=install, target=symfony/class-loader, packages=[]), ]
This may be caused by the most recent change to composer http://nelm.io/blog/2012/02/an-update-on-composer/
I was able to install the autoloader by changing "symfony/class-loader": "2.1.*" to "symfony/class-loader": "dev-master"