What is Symfony2?

Fabien Potencier

October 25, 2011

Last week, I held a keynote presentation about Symfony2 at the Symfony Day conference in Cologne. Many people asked me for the slides, but they were quite empty and not that useful. This blog post is more or less what I've said during the first part of the talk.

What is Symfony2?

When I ask people what Symfony2 is for them, most of them say something along the lines of: Symfony2 is a full-stack web framework written in PHP. Some also add that this is an MVC framework. And some others add that this is a decoupled framework. This is all fine and correct. But my definition is a bit different. Let me tell you what it is and why I think it matters.

Symfony2 is really about two different things.

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

Then, based on these components, Symfony2 is also a full-stack web framework.

Depending on your project and depending on your needs, you can either pick and choose some of the Symfony2 components and start your project with them, or you can use the full-stack framework and benefit from the tight integration it provides out of the box. And choosing between the two different approaches is really up to you.

Is Symfony2 an MVC framework?

If you look around, every single framework seems to implement the MVC pattern. And most of them are advertised as MVC frameworks... but not Symfony2. Have a look at the documentation, and you will see that the MVC pattern is only mentioned once or twice, but Symfony2 is never defined as being an MVC framework. Why?

Because I really don't care whether Symfony2 is MVC or not. Probably because the MVC word is so overloaded and because nobody implements exactly the same MVC pattern anyway. The separation of concerns is all I care about. And if you like to call Symfony2 an MVC framework, then you should know that Symfony2 is really about providing the tools for the Controller part, the View part, but not the Model part. It's up to you to create your model by hand or use any other tool, like an ORM. Of course, tight integration exists for the most well known ORMs like Doctrine2 and Propel; but they are optional dependencies. The Symfony2 core features do not and will never rely on any ORM.

I don't like MVC because that's not how the web works. Symfony2 is an HTTP framework; it is a Request/Response framework. That's the big deal. The fundamental principles of Symfony2 are centered around the HTTP specification.

I don't like MVC because the web has evolved a lot in the recent years and some projects are much different than projects we had some years ago. Sometimes, you just need a way to create a REST API. Sometimes, the logic is mostly in the browser and the server is just used to serve data (think backbone.js for instance). And for these projects, you don't need an MVC framework. You need something that handles a Request and returns a Response. You need a framework that implements the HTTP specification. HTTP streaming is yet another example that does not fit well with the MVC pattern.

Why does it matter?

We need to better advertise Symfony2 as a set of reusable components because I strongly believe that the community growth in the coming years will mainly come from the adoption of the components.

And Symfony2 adoption is all about how we sell the project. But it's also about the perception of the project people have. And as symfony 1.0 was mostly a monolithic framework, a lot of people still think that Symfony is indeed a monolithic framework. Hopefully, with the release of Symfony2, the perception is a bit different now, but it will definitely take a lot of time to change people minds.

It matters because even if new frameworks are created every single day, I think that in the "enterprise", there is room for only 2 or maybe 3 PHP frameworks. The question is: how will we win the framework war competition?

First, we need to lead the pack technically by constantly innovating. Getting inspiration from other technologies, other frameworks, and other web languages and adapting some of their concepts to PHP is a key factor here. Acknowledging that the web evolves is also very important. And Symfony2 is probably a great step forward in this direction with many great innovations like bundles, HTTP caching, distributions, dependency injection, templating engines, annotation configuration, asset management, the stable API, the web profiler, and a bunch more.

We will win the framework war competition if Symfony2 is everywhere. And a full-stack framework will never become a standard in the PHP world. So, we need to find another way.

Being everywhere is about sharing. First, by not reinventing the wheel ourselves and by integrating existing third-party products whenever possible (think Monolog, Composer, Doctrine, Propel, Assetic, Twig, Swiftmailer, and a few others); but at the same time we want to be able to share our work with others; and that's where the components come into play.

One of the Symfony2 goals is to provide building blocks for other projects. But what do I mean when I say projects? Who are the target users for Symfony2? Everybody: from personal projects to commercial ones; and Open-Source ones. Personal projects and Open-Source ones are the new targets.

Silex is probably a great framework for beginners, small or personal projects, or even for small commercial ones. Symfony2 is a great framework for bigger projects where tens of developers are involved and where the business logic is more complex. And the Symfony2 components are for people who don't want to use a framework, or for other Open-Source projects that don't want to reinvent the wheel.

To sum up, Symfony2 aims to provide the low-level architecture of PHP projects.

Why Symfony2?

Why do I think that the Symfony2 components are in a good position to become the low-level architecture of the PHP world?

The code is rock solid. The major Symfony2 components is the result of many years of work and the contributions of many developers.

If you have a look at the code, you will see the @api tag on some classes and methods. It indicates the public stable API. This tag means that a method (its name, its arguments, its return value) won't change in any Symfony2 minor versions. If you are only using the stable API, your code will not need to be upgraded when you upgrade to a newer version of Symfony2. That's a great selling point.

Last, but not the least, we try to be as secure as possible. We provide many security features in the components and we also take code security very seriously. And thanks to our great community, we have been able to conduct a security audit from a professional company. That's something that is obviously not possible for smaller projects.

Here are some examples of software and libraries that are currently using some of the Symfony2 Components:

  • Silex: BrowerKit, CssSelector, DomCrawler, EventDispatcher, HttpFoundation, HttpKernel, Routing, Form, Translation, Validator

  • Goutte: BrowserKit, DomCrawler, CssSelector, Process, ClassLoader, Finder

  • Behat: Console, DependencyInjection, EventDispatcher, Finder, Yaml, Config, Translation

  • Assetic: Process

  • Doctrine2: Console, Yaml

  • Propel2: Console, ClassLoader, Yaml

  • PHPUnit: Yaml

  • FLOW3: Yaml

  • Midguard CMS: most of them in their next version?

  • phpBB 4: most of them?

  • Drupal 8*: ClassLoader, HttpFoundation, HttpKernel?

The Symfony2 Components

Let's see what those components can do for you. As of today, we have 21 of them and any of them can be used as a standalone library:

  • DependencyInjection
  • EventDispatcher
  • HttpFoundation
  • DomCrawler
  • ClassLoader
  • CssSelector
  • HttpKernel
  • BrowserKit
  • Templating
  • Translation
  • Serializer
  • Validator
  • Security
  • Routing
  • Console
  • Process
  • Config
  • Finder
  • Locale
  • Yaml
  • Form

Each component has its own Git repository. You can get the code directly from there, or you can download an archive, or install it with PEAR, or even install it with the upcoming Composer installer (Composer is the new PHP installer that the Symfony community started to work on some months ago and it will be heavily used in Symfony projects to install all their dependencies).

In the second part of this post, I will try to demonstrate the power of some of the components with as little code as possible.

ClassLoader

The ClassLoader component provides an autoloader that implements the PSR-0 standard (which is a standard way to autoload namespaced classes as available in PHP 5.3). It is also able to load classes that use the PEAR naming convention. It is really flexible as it can look for classes in different directories based on a sub-namespace. You can even give more than one directory for one namespace:

require_once __DIR__.'/src/Symfony/Component/ClassLoader/UniversalClassLoader.php';
 
use Symfony\Component\ClassLoader\UniversalClassLoader;
 
$loader = new UniversalClassLoader();
$loader->registerNamespaces(array(
    'Symfony'          => array(__DIR__.'/src', __DIR__.'/symfony/src'),
    'Doctrine\\Common' => __DIR__.'/vendor/doctrine-common/lib',
    'Doctrine\\DBAL'   => __DIR__.'/vendor/doctrine-dbal/lib',
    'Doctrine'         => __DIR__.'/vendor/doctrine/lib',
    'Monolog'          => __DIR__.'/vendor/monolog/src',
));
$loader->registerPrefixes(array(
    'Twig_' => __DIR__.'/vendor/twig/lib',
));
$loader->register();
 

Most of the time, the Symfony2 ClassLoader is all you need to autoload all your project classes. And for better performance, you can use an APC cached version of the universal class loader.

Console

Even if we are talking about a web framework, having some tools to manage your project from the command line is nice. In Symfony2, we use the console to generate CRUDs, update the database schema, etc. It's not required, but it is really convenient and it can boost your productivity a lot.

This example shows how to create a command line tool very easily:

use Symfony\Component\Console\Application;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
 
$console = new Application();
$console
    ->register('ls')
    ->setDefinition(array(
        new InputArgument('dir', InputArgument::REQUIRED, 'Directory name'),
    ))
    ->setDescription('Displays the files in the given directory')
    ->setCode(function (InputInterface $input, OutputInterface $output) {
        $dir = $input->getArgument('dir');
 
        $output->writeln(sprintf('Dir listing for <info>%s</info>', $dir));
    })
;
$console->run();
 

With only 10 lines of code or so, you have access to a lot of features like output coloring, input and output abstractions (so that you can easily unit-test your commands), validation, automatic help messages, and a lot more. It's really powerful.

YAML

YAML is a great configuration format. It's the most popular Symfony2 component right now, because this is probably the only plain-PHP library that implements most of the YAML 1.2 specification:

use Symfony\Component\Yaml\Yaml;
 
$array = Yaml::parse($file);
 
print Yaml::dump($array);
 

Finder

The Finder provides a very convenient and nice fluent interface to find files and directories on the filesystem:

use Symfony\Component\Finder\Finder;
 
$finder = new Finder();
 
$iterator = $finder
  ->files()
  ->name('*.php')
  ->depth(0)
  ->size('>= 1K')
  ->in(__DIR__);
 
foreach ($iterator as $file) {
    print $file->getRealpath()."\n";
}
 

But you can also use it to find files stored remotely like in this example where we are looking for files on Amazon S3:

$s3 = new \Zend_Service_Amazon_S3($key, $secret);
$s3->registerStreamWrapper("s3");
 
$finder = new Finder();
$finder->name('photos*')->size('< 100K')->date('since 1 hour ago');
foreach ($finder->in('s3://bucket-name') as $file) {
    print $file->getFilename()."\n";
}
 

Process

The Process component allows you to execute a command in a sub-process. In this example, I run a simple directory listing and get the result back:

use Symfony\Component\Process\Process;
 
$process = new Process('ls -lsa');
$process->setTimeout(3600);
$process->run();
if (!$process->isSuccessful()) {
    throw new RuntimeException($process->getErrorOutput());
}
 
print $process->getOutput();
 

You can think that this is easy to achieve with plain PHP but it's not especially if you want to take care of the subtle differences between the different platforms.

And if you want to be able to get some feedback in real-time, just pass an anonymous function to the run() method and you will get the output buffer as it becomes available:

use Symfony\Component\Process\Process;
 
$process = new Process('ls -lsa');
$process->run(function ($type, $buffer) {
    if ('err' === $type) {
        echo 'ERR > '.$buffer;
    } else {
        echo 'OUT > '.$buffer;
    }
});
 

That's great if you want to execute a long running command (like rsync-ing files to a remote server) and give feedback to the user in real-time.

DomCrawler

If you are familiar with jQuery, DomCrawler is a PHP equivalent. It allows you to navigate the DOM of an HTML or XML document:

use Symfony\Component\DomCrawler\Crawler;
 
$crawler = new Crawler();
$crawler->addContent('<html><body><p>Hello World!</p></body></html>');
 
print $crawler->filterXPath('descendant-or-self::body/p')->text();
 

CssSelector

In the previous example, XPath is used to get access to the DOM. Using CSS selectors instead is far easier and so, we also have a CssSelector component. Its only goal is to convert a CSS selector to its XPath equivalent:

use Symfony\Component\CssSelector\CssSelector;
 
print CssSelector::toXPath('div.item > h4 > a');
 

That way, you can just use CSS Selectors with the DomCrawler instead of XPath:

use Symfony\Component\DomCrawler\Crawler;
 
$crawler = new Crawler();
$crawler->addContent('<html><body><p>Hello World!</p></body></html>');
 
print $crawler->filter('body > p')->text();
 

By the way, that's one example of a component (DomCrawler) that relies on another one (CssSelector) for some optional features.

HttpFoundation

The Symfony2 HttpFoundation component adds an object-oriented layer on top of PHP for everything related to the Web: Requests, Responses, Uploaded files, Cookies, Sessions, ...

In this example, we get a Request object from the current PHP global variables:

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
 
$request = Request::createFromGlobals();
echo $request->getPathInfo();
 

You can also create a Request directly -- that's interesting for unit testing:

$request = Request::create('/?foo=bar', 'GET');
echo $request->getPathInfo();
 

And here is how to create and send a Response:

$response = new Response('Not Found', 404, array('Content-Type' => 'text/plain'));
$response->send();
 

The Request and the Response classes have many other methods that implement the HTTP specification.

Routing

The Routing component is a way to associate a Request with the code that will convert it somehow to a Response. The example below demonstrates that with only 10 lines of code, you can set up a fully working routing system:

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Matcher\UrlMatcher;
use Symfony\Component\Routing\RequestContext;
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
 
$routes = new RouteCollection();
$routes->add('hello', new Route('/hello', array('controller' => 'foo')));
 
$context = new RequestContext();
 
// this is optional and can be done without a Request instance
$context->fromRequest(Request::createFromGlobals());
 
$matcher = new UrlMatcher($routes, $context);
 
$parameters = $matcher->match('/hello');
 

EventDispatcher

use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\Event;
 
$dispatcher = new EventDispatcher();
 
$dispatcher->addListener('event_name', function (Event $event) {
    // ...
});
 
$dispatcher->dispatch('event_name');
 

DependencyInjection

Even the DependencyInjection component is really easy. It has many features, but its core is simple to use. See how easy it is to configure a service that relies on another service:

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
 
$sc = new ContainerBuilder();
$sc
    ->register('foo', '%foo.class%')
    ->addArgument(new Reference('bar'))
;
$sc->setParameter('foo.class', 'Foo');
 
$sc->get('foo');
 

HttpKernel

The HttpKernel component provides the dynamic part of the HTTP specification. It provides the HttpKernelInterface, which is the core interface of the Symfony2 full-stack framework:

interface HttpKernelInterface
{
    /**
     * Handles a Request to convert it to a Response.
     *
     * @param  Request $request A Request instance
     *
     * @return Response A Response instance
     */
    function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true);
}
 

It takes a Request as an input and should return a Response as an output. Using this interface makes your code compatible with all frameworks using the Symfony2 components. And this will gives you many cool features for free.

Creating a framework based on the Symfony2 components is really easy. Here is a very simple, but fully-featured framework based on the Symfony2 components:

$routes = new RouteCollection();
$routes->add('hello', new Route('/hello', array('_controller' =>
    function (Request $request) {
        return new Response(sprintf("Hello %s", $request->get('name')));
    }
)));
 
$request = Request::createFromGlobals();
 
$context = new RequestContext();
$context->fromRequest($request);
 
$matcher = new UrlMatcher($routes, $context);
 
$dispatcher = new EventDispatcher();
$dispatcher->addSubscriber(new RouterListener($matcher));
 
$resolver = new ControllerResolver();
 
$kernel = new HttpKernel($dispatcher, $resolver);
 
$kernel->handle($request)->send();
 

This is all you need to create a flexible framework with the Symfony2 components.

Want to add an HTTP reverse proxy and benefit from HTTP caching and Edge Side Includes?

$kernel = new HttpKernel($dispatcher, $resolver); 
 
$kernel = new HttpCache($kernel, new Store(__DIR__.'/cache'));
 

Want to functional test this small framework?

$client = new Client($kernel);
$crawler = $client->request('GET', '/hello/Fabien');
 
$this->assertEquals('Fabien', $crawler->filter('p > span')->text());
 

Want nice error pages instead of ugly PHP exceptions?

$dispatcher->addSubscriber(new ExceptionListener(function (Request $request) {
    $msg = 'Something went wrong! ('.$request->get('exception')->getMessage().')';
 
    return new Response($msg, 500);
}));
 

I can continue on and on but I think you get the point.

And that's why the simple looking HttpKernelInterface is so powerful. It gives you access to a lot of cool features, ready to be used out of the box, with no efforts.

To sum up, the Symfony2 components are reusable PHP 5.3 libraries that can be used standalone. They are highly configurable, tested, and secured. And you should consider using them even if you don't need or don't want to use a full-stack framework for your next project.

Discussion

gravatar Christian  — October 25, 2011 11:15   #1
Thanks for that post! I think it emphasizes the right things.

What I still waiting for though is a public explanation of the distribution strategy. It holds so much potential that isn't at all leveraged atm and it would underpin what you covered in this post.
gravatar lol  — October 25, 2011 11:41   #2
?? ???
gravatar Anton Stoychev  — October 25, 2011 12:33   #3
More posts like this one are necessity!

However I'm not completely certain of the examples.
What exactly do you mean by standalone or decoupled?

I'm asking because I was trying to get the router working to a comfortable state where it catches the request `path-info` and matches against it all the defined routes.

You say that HttpFoundation component is optional but if you don't include this component the Router will not be fully functional. The "_scheme" requirement won't work. A bit of middleware logic is required. It may not be the best example but there seem to be other similar situations.

A full working examples which use the bare minimum of components and implement the rest with just PHP will be a boost to Symfony components.
gravatar cryptocompress  — October 25, 2011 13:36   #4
"Is Symfony2 an MVC framework?"
YES! there is no such thing as MVC in the Web World! Really!

read it:
http://heim.ifi.uio.no/~trygver/themes/mvc/mvc-index.html
gravatar leonidas  — October 25, 2011 13:41   #5
good to see where @fabpot is coming from. hopefully, Drupal starts adopting more bundles, and hopefully a magento-type port into sf2! Symfony Rocks!
gravatar Amitay Horwitz  — October 25, 2011 14:03   #6
Thanks for the post, Fabien.
I really think Symfony2 is a great framework suitable for anything from small websites to enterprise applications.

However, I think the weak point currently is lack of documentation - especially for the standalone components.

I would love to see more in-detail guides for these components.

Keep up the good work!
gravatar Lukas  — October 25, 2011 17:25   #7
@Amitay: yes we are very aware that the components need to be documented if we want the above vision to become a reality. hopefully soon we will start remedying this situation.
gravatar Javier Garcia  — October 25, 2011 18:19   #8
Good post Fabie, but is there really a war? I don't like too much that word..
gravatar Grayson Koonce  — October 25, 2011 19:00   #9
Great post. Would really like to see some better documentation for these components. For example, to better integrate into Zend Framework 1 to ease the migration to Symfony2.
gravatar erheme318  — October 25, 2011 21:20   #10
Thank you for your great effort. I think that Symfony2 could make the PHP one step up.

Thanks Developers.
gravatar Nicolás Moreira  — October 25, 2011 22:00   #11
Thanks you for Symfony2, best regards from Uruguay
gravatar chema  — October 26, 2011 23:41   #12
war? competition? I think is everything about collaboration and helping each other.

I think you are mistaking using such word or that kind of thoughts.

The Open Source Community don't want war, wants to help and provide as good as possible and doing good, not destroying.

For the rest I love the components decoupled aproach like Zend does, great work, and sure really useful.
gravatar neki chan  — October 28, 2011 12:03   #13
waaawwwwwwwwwww.........
thanks so much fabien n____n
gravatar Lukas Smith  — October 28, 2011 12:26   #14
@chema: I am also not so big on "war" rhetoric, which is why I am trying to look how to instead collaborate on common interfaces. See my blog for details. That being said you can also see that I am not quite sure how to really make this happen either. Also whatever we do it will take years before it will have an impact.

So I totally agree with Fabien, that at the same time we need to make sure that people understand what we have to offer to them today already with all these components. And yes we are pitching "against" the other alternatives. That being said, I feel that what we have to offer excels on technical merits, so I wouldn't be very concerned that we would loose a competitive edge if we would have some common interfaces with other projects.
gravatar Julien  — October 29, 2011 23:16   #15
C'est quand même un peu gonflé de sapproprier de la sorte des patterns existants depuis bien longtemps comme linjection de dépendance :(

Ok, Symfony2 est excellent mais faut pas non plus exagérer et se croire tout permis !

Lorsquon parle dune API REST cela n'est rien de plus quune façon dimplementer MVC, et il faut être assez sur de soi pour snober ce terme sous prétexte qu'il est trop "mainstream" ou tendance... Sil l'est, ce n'est pas pour rien !

Bonne guerre !
gravatar Greg  — November 07, 2011 15:13   #16
I tried the routing example and it did not work.
First I was getting and error that fromRequest() does is not a method of RequestContext. After a lot of searching I found out that the code in Routing in Symfony 2.0.5 branch in github is different than that from the dedicated branch.

Once I managed to get past the error above, I realized that the route matched no matter what. So it matched /hello, /hello/Greg, /hell etc. So basically it was not working as intended.

Has anyone else tried this?



gravatar cedricus  — November 10, 2011 15:20   #17
@Fabien Talking about separation of concerns I think the annotation system does not fit.

For instance when you put routing information in a controller class, you cannot reuse this controller class as is in another project ; because routing annotations depends on a more global website strategy.

Same thing with annotations in entities, I think a Product class can be used in many projects, PHP handmade, frameworks, CMS... as a model but if you put some annotations about ORM which is current project specific it loses OOP aims as the class contains wide project data.

Even in validations and other things.

So I think Yaml or XML configurations are better.

Am I missing something?
gravatar Lukas  — November 11, 2011 14:33   #18
@cedricus: no you are not missing anything. annotations always model behavior that is not directly related to the code so they always have this draw back.

their use is mostly for convenience, because sometimes architectural purity must be sacrificed for ease of use.
gravatar cedricus  — November 14, 2011 09:18   #19
Ok. Thanks for the reply.
gravatar Patrick  — November 15, 2011 11:37   #20
Wow Fabien, this was really a head up for me. I've never used simfony before, but this sound great and of course testworthy. I'll set up a small project and share my opinions <a href="http://www.netzaffin.de">on my on Blog</a>, so that symfony can grow!

Thank you for this detailed post!
gravatar xtt1341  — November 21, 2011 20:25   #21
I think Symfony2 is the ONLY framework that will win the WAR in enterprise room, so I'm learning it hard. Symfony2 has namespace, and the source code is ahead of the others, it's very strict and elegant. I'm reviewing the OOP basics and patterns, so now I learn the Symfony2 much clearly than before. Thanks to the great frameworks and Fabien is the real ???? of PHP. Thanks Again!~?????????
gravatar Onki  — November 27, 2011 00:19   #22
nice good job
gravatar Ala eddine Khefifi  — December 03, 2011 17:57   #23
Good post, Fabien, I find that Symfony2 is a great Framework in various areas,I develop with it for a while now.
but I think that the part of standalone components needs a lot more of documentations, examples, guidance and direction.
keep up the good work.
gravatar Menno Plaum  — December 16, 2011 11:40   #24
Hi Fabien,

I downloaded sf 2.0.7 today and I was hoping to find the per action timer in the profiler that you showed in Cologne... but I didn't ;-(
Any idea when this will be added to the stable version?

Regards,
Menno.
gravatar this is true  — December 21, 2011 02:40   #25
I hate php. But symfony2 is so unique and well thought I have to ditch python / ruby for php. People who are *still* raving about RoR should look at symfony2.