Symfony Quick Tour 2.0
Symfony Quick Tour 2.0
What could be better to make up your own mind than to try out Symfony yourself? Aside from
a little time, it will cost you nothing. Step by step you will explore the Symfony universe. Be
careful, Symfony can become addictive from the very first encounter!
Contents at a Glance
The Big Picture ...................................................................................................................................4
The View ..........................................................................................................................................13
The Controller ..................................................................................................................................19
The Architecture ...............................................................................................................................25
Chapter 1
Downloading Symfony2
First, check that you have installed and configured a Web server (such as Apache) with PHP 5.3.2 or
higher.
If you have PHP 5.4, you could use the built-in web server. The built-in server should be used only
for development purpose, but it can help you to start your project quickly and easily.
Just use this command to launch the server:
Listing
Listing
1
1-1 1-2
where "/path/to/www" is the path to some directory on your machine that you'll extract Symfony
into so that the eventual URL to your application is "http://localhost/Symfony/app_dev.php1". You
can also extract Symfony first and then start the web server in the Symfony "web" directory. If you
do this, the URL to your application will be "http://localhost/app_dev.php2".
1. http://localhost/Symfony/app_dev.php
2. http://localhost/app_dev.php
Ready? Start by downloading the "Symfony2 Standard Edition3", a Symfony distribution that is
preconfigured for the most common use cases and also contains some code that demonstrates how to use
Symfony2 (get the archive with the vendors included to get started even faster).
After unpacking the archive under your web server root directory, you should have a Symfony/ directory
that looks like this:
Listing <- your web root directory
1 www/
1-3
2
Symfony/ <- the unpacked archive
3
app/
4
cache/
5
config/
6
logs/
7
Resources/
8
bin/
9
src/
10
Acme/
11
DemoBundle/
12
Controller/
13
Resources/
14
...
15
vendor/
16
symfony/
17
doctrine/
18
...
19
web/
20
app.php
21
...
Listing
1-4
If you downloaded the Standard Edition without vendors, simply run the following command to
download all of the vendor libraries:
1 $Listing
php bin/vendors install
1-5
Listing
1-6
Listing
1-8
If there are any outstanding issues listed, correct them. You might also tweak your configuration by
following any given recommendations. When everything is fine, click on "Bypass configuration and go to
the Welcome page" to request your first "real" Symfony2 webpage:
3. http://symfony.com/download
Listing
Listing
1
1-9 1-10
http://localhost/Symfony/web/app_dev.php/
Symfony2 should welcome and congratulate you for your hard work so far!
The distribution comes with some sample code that you can use to learn more about the main Symfony2
concepts. Go to the following URL to be greeted by Symfony2 (replace Fabien with your first name):
Listing
Listing
1
1-11 1-12
http://localhost/Symfony/web/app_dev.php/demo/hello/Fabien
4. http://en.wikipedia.org/wiki/Separation_of_concerns
Routing
Symfony2 routes the request to the code that handles it by trying to match the requested URL against
some configured patterns. By default, these patterns (called routes) are defined in the app/config/
routing.yml configuration file. When you're in the dev environment - indicated by the app_**dev**.php
front controller - the app/config/routing_dev.yml configuration file is also loaded. In the Standard
Edition, the routes to these "demo" pages are placed in that file:
1
2
3
4
5
6
7
8
9
10
11
#Listing
app/config/routing_dev.yml
1-13
_welcome:
pattern: /
defaults: { _controller: AcmeDemoBundle:Welcome:index }
Listing
1-14
_demo:
resource: "@AcmeDemoBundle/Controller/DemoController.php"
type:
annotation
prefix:
/demo
# ...
The first three lines (after the comment) define the code that is executed when the user requests the "/"
resource (i.e. the welcome page you saw earlier). When requested, the AcmeDemoBundle:Welcome:index
controller will be executed. In the next section, you'll learn exactly what that means.
The Symfony2 Standard Edition uses YAML5 for its configuration files, but Symfony2 also supports
XML, PHP, and annotations natively. The different formats are compatible and may be used
interchangeably within an application. Also, the performance of your application does not depend
on the configuration format you choose as everything is cached on the very first request.
Controllers
A controller is a fancy name for a PHP function or method that handles incoming requests and returns
responses (often HTML code). Instead of using the PHP global variables and functions (like $_GET
or header()) to manage these HTTP messages, Symfony uses objects: Request6 and Response7. The
simplest possible controller might create the response by hand, based on the request:
Listing
Listing
1
1-15 1-16
use Symfony\Component\HttpFoundation\Response;
2
3 $name = $request->query->get('name');
4
5 return new Response('Hello '.$name, 200, array('Content-Type' => 'text/plain'));
Symfony2 embraces the HTTP Specification, which are the rules that govern all communication on
the Web. Read the "Symfony2 and HTTP Fundamentals" chapter of the book to learn more about
this and the added power that this brings.
Symfony2 chooses the controller based on the _controller value from the routing configuration:
AcmeDemoBundle:Welcome:index. This string is the controller logical name, and it references the
indexAction method from the Acme\DemoBundle\Controller\WelcomeController class:
Listing Listing
1
1-17 1-18
2
3
4
5
6
7
8
9
10
11
12
// src/Acme/DemoBundle/Controller/WelcomeController.php
namespace Acme\DemoBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class WelcomeController extends Controller
{
public function indexAction()
{
return $this->render('AcmeDemoBundle:Welcome:index.html.twig');
}
}
You
could
have
used
the
full
class
and
method
name
Acme\DemoBundle\Controller\WelcomeController::indexAction - for the _controller value.
But if you follow some simple conventions, the logical name is shorter and allows for more
flexibility.
5. http://www.yaml.org/
6. http://api.symfony.com/2.0/Symfony/Component/HttpFoundation/Request.html
7. http://api.symfony.com/2.0/Symfony/Component/HttpFoundation/Response.html
The WelcomeController class extends the built-in Controller class, which provides useful shortcut
methods,
like
the
render()8
method
that
loads
and
renders
a
template
(AcmeDemoBundle:Welcome:index.html.twig). The returned value is a Response object populated with
the rendered content. So, if the needs arise, the Response can be tweaked before it is sent to the browser:
Listing
1 public
function indexAction()
1-19
2 {
3
$response = $this->render('AcmeDemoBundle:Welcome:index.txt.twig');
4
$response->headers->set('Content-Type', 'text/plain');
5
6
return $response;
7 }
Listing
1-20
No matter how you do it, the end goal of your controller is always to return the Response object that
should be delivered back to the user. This Response object can be populated with HTML code, represent
a client redirect, or even return the contents of a JPG image with a Content-Type header of image/jpg.
Extending the Controller base class is optional. As a matter of fact, a controller can be a plain
PHP function or even a PHP closure. "The Controller" chapter of the book tells you everything
about Symfony2 controllers.
Listing
1-22
Symfony2 can read/import the routing information from different files written in YAML, XML, PHP,
or even embedded in PHP annotations. Here, the file's logical name is @AcmeDemoBundle/Controller/
DemoController.php and refers to the src/Acme/DemoBundle/Controller/DemoController.php file.
In this file, routes are defined as annotations on action methods:
1
2
3
4
5
6
7
8
9
10
11
Listing
//
src/Acme/DemoBundle/Controller/DemoController.php
1-23
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
Listing
1-24
8. http://api.symfony.com/2.0/Symfony/Bundle/FrameworkBundle/Controller/Controller.html#render()
12
13
14
15
16
17 }
{
return array('name' => $name);
}
// ...
The @Route() annotation defines a new route with a pattern of /hello/{name} that executes the
helloAction method when matched. A string enclosed in curly brackets like {name} is called a
placeholder. As you can see, its value can be retrieved through the $name method argument.
Even if annotations are not natively supported by PHP, you use them extensively in Symfony2 as a
convenient way to configure the framework behavior and keep the configuration next to the code.
If you take a closer look at the controller code, you can see that instead of rendering a template
and returning a Response object like before, it just returns an array of parameters. The @Template()
annotation tells Symfony to render the template for you, passing in each variable of the array to
the template. The name of the template that's rendered follows the name of the controller. So, in
this example, the AcmeDemoBundle:Demo:hello.html.twig template is rendered (located at src/Acme/
DemoBundle/Resources/views/Demo/hello.html.twig).
The @Route() and @Template() annotations are more powerful than the simple examples shown
in this tutorial. Learn more about "annotations in controllers" in the official documentation.
Templates
The controller renders the src/Acme/DemoBundle/Resources/views/Demo/hello.html.twig template
(or AcmeDemoBundle:Demo:hello.html.twig if you use the logical name):
Listing
Listing
1
1-25 1-26
2
3
4
5
6
7
8
{# src/Acme/DemoBundle/Resources/views/Demo/hello.html.twig #}
{% extends "AcmeDemoBundle::layout.html.twig" %}
{% block title "Hello " ~ name %}
{% block content %}
<h1>Hello {{ name }}!</h1>
{% endblock %}
By default, Symfony2 uses Twig9 as its template engine but you can also use traditional PHP templates if
you choose. The next chapter will introduce how templates work in Symfony2.
Bundles
You might have wondered why the bundle word is used in many names we have seen so far. All the code
you write for your application is organized in bundles. In Symfony2 speak, a bundle is a structured set
of files (PHP files, stylesheets, JavaScripts, images, ...) that implements a single feature (a blog, a forum,
9. http://twig.sensiolabs.org/
...) and which can be easily shared with other developers. As of now, we have manipulated one bundle,
AcmeDemoBundle. You will learn more about bundles in the last chapter of this tutorial.
But what you see initially is only the tip of the iceberg; click on the weird hexadecimal number to reveal
yet another very useful Symfony2 debugging tool: the profiler.
Of course, you won't want to show these tools when you deploy your application to production. That's
why you will find another front controller in the web/ directory (app.php), which is optimized for the
production environment:
Listing
1 http://localhost/Symfony/web/app.php/demo/hello/Fabien
1-27
Listing
1-28
And if you use Apache with mod_rewrite enabled, you can even omit the app.php part of the URL:
PDF brought to you by
generated on August 29, 2012
Listing
Listing
1
1-29 1-30
http://localhost/Symfony/web/demo/hello/Fabien
Last but not least, on the production servers, you should point your web root directory to the web/
directory to secure your installation and have an even better looking URL:
Listing
Listing
1
1-31 1-32
http://localhost/demo/hello/Fabien
Note that the three URLs above are provided here only as examples of how a URL looks like
when the production front controller is used (with or without mod_rewrite). If you actually try
them in an out of the box installation of Symfony Standard Edition you will get a 404 error
as AcmeDemoBundle is enabled only in dev environment and its routes imported in app/config/
routing_dev.yml.
To make your application respond faster, Symfony2 maintains a cache under the app/cache/ directory.
In the development environment (app_dev.php), this cache is flushed automatically whenever you make
changes to any code or configuration. But that's not the case in the production environment (app.php)
where performance is key. That's why you should always use the development environment when
developing your application.
Different environments of a given application differ only in their configuration. In fact, a configuration
can inherit from another one:
# app/config/config_dev.yml
2 imports:
3
- { resource: config.yml }
4
5 web_profiler:
6
toolbar: true
7
intercept_redirects: false
Listing
Listing
1
1-33 1-34
The dev environment (which loads the config_dev.yml configuration file) imports the global
config.yml file and then modifies it by, in this example, enabling the web debug toolbar.
Final Thoughts
Congratulations! You've had your first taste of Symfony2 code. That wasn't so hard, was it? There's a lot
more to explore, but you should already see how Symfony2 makes it really easy to implement web sites
better and faster. If you are eager to learn more about Symfony2, dive into the next section: "The View".
Chapter 2
The View
After reading the first part of this tutorial, you have decided that Symfony2 was worth another 10
minutes. Great choice! In this second part, you will learn more about the Symfony2 template engine,
Twig1. Twig is a flexible, fast, and secure template engine for PHP. It makes your templates more readable
and concise; it also makes them more friendly for web designers.
Instead of Twig, you can also use PHP for your templates. Both template engines are supported by
Symfony2.
A Twig template is a text file that can generate any type of content (HTML, XML, CSV, LaTeX, ...). Twig
defines two kinds of delimiters:
{{ ... }}: Prints a variable or the result of an expression;
{% ... %}: Controls the logic of the template; it is used to execute for loops and if
statements, for example.
Below is a minimal template that illustrates a few basics, using two variables page_title and
navigation, which would be passed into the template:
Listing
2-1
Listing
2-2
1. http://twig.sensiolabs.org/
2. http://twig.sensiolabs.org/documentation
1 <!DOCTYPE html>
2 <html>
3
<head>
4
<title>My Webpage</title>
5
</head>
6
<body>
7
<h1>{{ page_title }}</h1>
8
9
<ul id="navigation">
10
{% for item in navigation %}
11
<li><a href="{{ item.href }}">{{ item.caption }}</a></li>
12
{% endfor %}
13
</ul>
14
</body>
15 </html>
To render a template in Symfony, use the render method from within a controller and pass it any
variables needed in the template:
Listing
Listing
1
2-3 2-4
$this->render('AcmeDemoBundle:Demo:hello.html.twig', array(
2
'name' => $name,
3 ));
Variables passed to a template can be strings, arrays, or even objects. Twig abstracts the difference
between them and lets you access "attributes" of a variable with the dot (.) notation:
Listing Listing
1
2-5 2-6
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
It's important to know that the curly braces are not part of the variable but the print statement. If
you access variables inside tags don't put the braces around.
Decorating Templates
More often than not, templates in a project share common elements, like the well-known header and
footer. In Symfony2, we like to think about this problem differently: a template can be decorated by
another one. This works exactly the same as PHP classes: template inheritance allows you to build a
base "layout" template that contains all the common elements of your site and defines "blocks" that child
templates can override.
The hello.html.twig template inherits from layout.html.twig, thanks to the extends tag:
1
2
3
4
5
6
7
8
Listing
{#
src/Acme/DemoBundle/Resources/views/Demo/hello.html.twig #}
2-7
{% extends "AcmeDemoBundle::layout.html.twig" %}
Listing
2-8
The AcmeDemoBundle::layout.html.twig notation sounds familiar, doesn't it? It is the same notation
used to reference a regular template. The :: part simply means that the controller element is empty, so
the corresponding file is directly stored under the Resources/views/ directory.
Now, let's have a look at a simplified layout.html.twig:
Listing
1 {#
src/Acme/DemoBundle/Resources/views/layout.html.twig #}
2-9
2 <div class="symfony-content">
3
{% block content %}
4
{% endblock %}
5 </div>
Listing
2-10
The {% block %} tags define blocks that child templates can fill in. All the block tag does is to tell the
template engine that a child template may override those portions of the template.
In this example, the hello.html.twig template overrides the content block, meaning that the "Hello
Fabien" text is rendered inside the div.symfony-content element.
Listing
Listing
1
2-11 2-12
Listing
Listing
1
2-13 2-14
2
3
4
5
6
7
{# src/Acme/DemoBundle/Resources/views/Demo/hello.html.twig #}
{% extends "AcmeDemoBundle::layout.html.twig" %}
{# override the body block from embedded.html.twig #}
{% block content %}
{% include "AcmeDemoBundle:Demo:embedded.html.twig" %}
{% endblock %}
Listing
Listing
1
2-15 2-16
Here, the AcmeDemoBundle:Demo:fancy string refers to the fancy action of the Demo controller. The
arguments (name and color) act like simulated request variables (as if the fancyAction were handling a
whole new request) and are made available to the controller:
// src/Acme/DemoBundle/Controller/DemoController.php
2
3 class DemoController extends Controller
4 {
5
public function fancyAction($name, $color)
6
{
7
// create some object, based on the $color variable
8
$object = ...;
9
10
return $this->render('AcmeDemoBundle:Demo:fancy.html.twig', array('name' =>
11 $name, 'object' => $object));
12
}
13
14
// ...
}
Listing Listing
1
2-17 2-18
Listing
2-20
The path function takes the route name and an array of parameters as arguments. The route name is the
main key under which routes are referenced and the parameters are the values of the placeholders defined
in the route pattern:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Listing
//
src/Acme/DemoBundle/Controller/DemoController.php
2-21
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
Listing
2-22
// ...
/**
* @Route("/hello/{name}", name="_demo_hello")
* @Template()
*/
public function helloAction($name)
{
return array('name' => $name);
}
The url function generates absolute URLs: {{ url('_demo_hello', { 'name': 'Thomas'}) }}.
Listing
2-24
The asset function's main purpose is to make your application more portable. Thanks to this function,
you can move the application root directory anywhere under your web root directory without changing
anything in your template's code.
Escaping Variables
Twig is configured to automatically escape all output by default. Read Twig documentation3 to learn more
about output escaping and the Escaper extension.
PDF brought to you by
generated on August 29, 2012
Final Thoughts
Twig is simple yet powerful. Thanks to layouts, blocks, templates and action inclusions, it is very easy to
organize your templates in a logical and extensible way. However, if you're not comfortable with Twig,
you can always use PHP templates inside Symfony without any issues.
You have only been working with Symfony2 for about 20 minutes, but you can already do pretty amazing
stuff with it. That's the power of Symfony2. Learning the basics is easy, and you will soon learn that this
simplicity is hidden under a very flexible architecture.
But I'm getting ahead of myself. First, you need to learn more about the controller and that's exactly the
topic of the next part of this tutorial. Ready for another 10 minutes with Symfony2?
3. http://twig.sensiolabs.org/documentation
Chapter 3
The Controller
Still with us after the first two parts? You are already becoming a Symfony2 addict! Without further ado,
let's discover what controllers can do for you.
Using Formats
Nowadays, a web application should be able to deliver more than just HTML pages. From XML for RSS
feeds or Web Services, to JSON for Ajax requests, there are plenty of different formats to choose from.
Supporting those formats in Symfony2 is straightforward. Tweak the route by adding a default value of
xml for the _format variable:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Listing
//
src/Acme/DemoBundle/Controller/DemoController.php
3-1
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
Listing
3-2
// ...
/**
* @Route("/hello/{name}", defaults={"_format"="xml"}, name="_demo_hello")
* @Template()
*/
public function helloAction($name)
{
return array('name' => $name);
}
By using the request format (as defined by the _format value), Symfony2 automatically selects the right
template, here hello.xml.twig:
Listing src/Acme/DemoBundle/Resources/views/Demo/hello.xml.twig -->
1 <!-3-3
2 <hello>
Listing
3-4
3
<name>{{ name }}</name>
4 </hello>
That's all there is to it. For standard formats, Symfony2 will also automatically choose the best ContentType header for the response. If you want to support different formats for a single action, use the
{_format} placeholder in the route pattern instead:
Listing Listing
1
3-5 3-6
2
3
4
5
6
7
8
9
10
11
12
13
14
// src/Acme/DemoBundle/Controller/DemoController.php
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
// ...
/**
* @Route("/hello/{name}.{_format}", defaults={"_format"="html"},
requirements={"_format"="html|xml|json"}, name="_demo_hello")
* @Template()
*/
public function helloAction($name)
{
return array('name' => $name);
}
The controller will now be called for URLs like /demo/hello/Fabien.xml or /demo/hello/
Fabien.json.
The requirements entry defines regular expressions that placeholders must match. In this example, if
you try to request the /demo/hello/Fabien.js resource, you will get a 404 HTTP error, as it does not
match the _format requirement.
Listing
Listing
1
3-7 3-8
The generateUrl() is the same method as the path() function we used in templates. It takes the route
name and an array of parameters as arguments and returns the associated friendly URL.
You can also easily forward the action to another one with the forward() method. Internally, Symfony
makes a "sub-request", and returns the Response object from that sub-request:
Listing
Listing
1
3-9 3-10
1
2
3
4
5
6
7
8
9
Listing
$request
= $this->getRequest();
Listing
3-12
3-11
In a template, you can also access the Request object via the app.request variable:
Listing
1 {{
app.request.query.get('page') }}
3-13
2
3 {{ app.request.parameter('page') }}
Listing
3-14
Listing
$session
= $this->getRequest()->getSession();
3-15
Listing
3-16
You can also store small messages that will only be available for the very next request:
1
2
3
4
5
Listing
//
store a message for the very next request (in a controller)
3-17
$session->setFlash('notice', 'Congratulations, your action succeeded!');
Listing
3-18
This is useful when you need to set a success message before redirecting the user to another page (which
will then show the message).
Securing Resources
The Symfony Standard Edition comes with a simple security configuration that fits most common needs:
# app/config/security.yml
2 security:
3
encoders:
4
Symfony\Component\Security\Core\User\User: plaintext
5
6
role_hierarchy:
7
ROLE_ADMIN:
ROLE_USER
8
ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
9
10
providers:
11
in_memory:
12
users:
13
user: { password: userpass, roles: [ 'ROLE_USER' ] }
14
admin: { password: adminpass, roles: [ 'ROLE_ADMIN' ] }
15
16
firewalls:
17
dev:
18
pattern: ^/(_(profiler|wdt)|css|images|js)/
19
security: false
20
21
login:
22
pattern: ^/demo/secured/login$
23
security: false
24
25
secured_area:
26
pattern:
^/demo/secured/
27
form_login:
28
check_path: /demo/secured/login_check
29
login_path: /demo/secured/login
30
logout:
31
path:
/demo/secured/logout
32
target: /demo/
Listing Listing
1
3-19 3-20
This configuration requires users to log in for any URL starting with /demo/secured/ and defines two
valid users: user and admin. Moreover, the admin user has a ROLE_ADMIN role, which includes the
ROLE_USER role as well (see the role_hierarchy setting).
For readability, passwords are stored in clear text in this simple configuration, but you can use any
hashing algorithm by tweaking the encoders section.
will
You can also force the action to require a given role by using the @Secure annotation on the controller:
1
2
3
4
5
6
7
8
9
10
11
12
13
Listing Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use
3-21
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use JMS\SecurityExtraBundle\Annotation\Secure;
Listing
3-22
/**
* @Route("/hello/admin/{name}", name="_demo_secured_hello_admin")
* @Secure(roles="ROLE_ADMIN")
* @Template()
*/
public function helloAdminAction($name)
{
return array('name' => $name);
}
Now, log in as user (who does not have the ROLE_ADMIN role) and from the secured hello page, click on
the "Hello resource secured" link. Symfony2 should return a 403 HTTP status code, indicating that the
user is "forbidden" from accessing that resource.
The Symfony2 security layer is very flexible and comes with many different user providers (like
one for the Doctrine ORM) and authentication providers (like HTTP basic, HTTP digest, or X509
certificates). Read the "Security" chapter of the book for more information on how to use and
configure them.
Caching Resources
As soon as your website starts to generate more traffic, you will want to avoid generating the same
resource again and again. Symfony2 uses HTTP cache headers to manage resources cache. For simple
caching strategies, use the convenient @Cache() annotation:
1
2
3
4
5
6
7
8
9
10
11
12
13
Listing Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use
3-23
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
Listing
3-24
/**
* @Route("/hello/{name}", name="_demo_hello")
* @Template()
* @Cache(maxage="86400")
*/
public function helloAction($name)
{
return array('name' => $name);
}
In this example, the resource will be cached for a day. But you can also use validation instead of
expiration or a combination of both if that fits your needs better.
Resource caching is managed by the Symfony2 built-in reverse proxy. But because caching is managed
using regular HTTP cache headers, you can replace the built-in reverse proxy with Varnish or Squid and
easily scale your application.
But what if you cannot cache whole pages? Symfony2 still has the solution via Edge Side Includes
(ESI), which are supported natively. Learn more by reading the "HTTP Cache" chapter of the book.
Final Thoughts
That's all there is to it, and I'm not even sure we have spent the full 10 minutes. We briefly introduced
bundles in the first part, and all the features we've learned about so far are part of the core framework
bundle. But thanks to bundles, everything in Symfony2 can be extended or replaced. That's the topic of
the next part of this tutorial.
Chapter 4
The Architecture
You are my hero! Who would have thought that you would still be here after the first three parts? Your
efforts will be well rewarded soon. The first three parts didn't look too deeply at the architecture of
the framework. Because it makes Symfony2 stand apart from the framework crowd, let's dive into the
architecture now.
Listing
//
web/app.php
4-1
require_once __DIR__.'/../app/bootstrap.php.cache';
require_once __DIR__.'/../app/AppKernel.php';
Listing
4-2
use Symfony\Component\HttpFoundation\Request;
$kernel = new AppKernel('prod', false);
$kernel->loadClassCache();
$kernel->handle(Request::createFromGlobals())->send();
The kernel first requires the bootstrap.php.cache file, which bootstraps the framework and registers
the autoloader (see below).
Like any front controller, app.php uses a Kernel Class, AppKernel, to bootstrap the application.
Listing Listing
1
4-3 4-4
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// app/autoload.php
use Symfony\Component\ClassLoader\UniversalClassLoader;
$loader = new UniversalClassLoader();
$loader->registerNamespaces(array(
'Symfony'
=> array(__DIR__.'/../vendor/symfony/src', __DIR__.'/../vendor/
bundles'),
'Sensio'
=> __DIR__.'/../vendor/bundles',
'JMS'
=> __DIR__.'/../vendor/bundles',
'Doctrine\\Common' => __DIR__.'/../vendor/doctrine-common/lib',
'Doctrine\\DBAL'
=> __DIR__.'/../vendor/doctrine-dbal/lib',
'Doctrine'
=> __DIR__.'/../vendor/doctrine/lib',
'Monolog'
=> __DIR__.'/../vendor/monolog/src',
'Assetic'
=> __DIR__.'/../vendor/assetic/src',
'Metadata'
=> __DIR__.'/../vendor/metadata/src',
));
$loader->registerPrefixes(array(
'Twig_Extensions_' => __DIR__.'/../vendor/twig-extensions/lib',
'Twig_'
=> __DIR__.'/../vendor/twig/lib',
));
// ...
$loader->registerNamespaceFallbacks(array(
__DIR__.'/../src',
));
$loader->register();
The UniversalClassLoader1 is used to autoload files that respect either the technical interoperability
standards2 for PHP 5.3 namespaces or the PEAR naming convention3 for classes. As you can see here, all
dependencies are stored under the vendor/ directory, but this is just a convention. You can store them
wherever you want, globally on your server or locally in your projects.
1. http://api.symfony.com/2.0/Symfony/Component/ClassLoader/UniversalClassLoader.html
2. http://symfony.com/PSR0
3. http://pear.php.net/
If you want to learn more about the flexibility of the Symfony2 autoloader, read the "The
ClassLoader Component" chapter.
Registering a Bundle
An application is made up of bundles as defined in the registerBundles() method of the AppKernel
class. Each bundle is a directory that contains a single Bundle class that describes it:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Listing
//
app/AppKernel.php
4-5
public function registerBundles()
{
$bundles = array(
new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
new Symfony\Bundle\SecurityBundle\SecurityBundle(),
new Symfony\Bundle\TwigBundle\TwigBundle(),
new Symfony\Bundle\MonologBundle\MonologBundle(),
new Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle(),
new Symfony\Bundle\DoctrineBundle\DoctrineBundle(),
new Symfony\Bundle\AsseticBundle\AsseticBundle(),
new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(),
new JMS\SecurityExtraBundle\JMSSecurityExtraBundle(),
);
Listing
4-6
In addition to the AcmeDemoBundle that we have already talked about, notice that the kernel also
enables other bundles such as the FrameworkBundle, DoctrineBundle, SwiftmailerBundle, and
AsseticBundle bundle. They are all part of the core framework.
Configuring a Bundle
Each bundle can be customized via configuration files written in YAML, XML, or PHP. Have a look at
the default configuration:
PDF brought to you by
generated on August 29, 2012
Listing Listing
1
4-7 4-8
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# app/config/config.yml
imports:
- { resource: parameters.ini }
- { resource: security.yml }
framework:
secret:
"%secret%"
charset:
UTF-8
router:
{ resource: "%kernel.root_dir%/config/routing.yml" }
form:
true
csrf_protection: true
validation:
{ enable_annotations: true }
templating:
{ engines: ['twig'] } #assets_version: SomeVersionScheme
session:
default_locale: "%locale%"
auto_start:
true
# Twig Configuration
twig:
debug:
"%kernel.debug%"
strict_variables: "%kernel.debug%"
# Assetic Configuration
assetic:
debug:
"%kernel.debug%"
use_controller: false
filters:
cssrewrite: ~
# closure:
#
jar: "%kernel.root_dir%/java/compiler.jar"
# yui_css:
#
jar: "%kernel.root_dir%/java/yuicompressor-2.4.2.jar"
# Doctrine Configuration
doctrine:
dbal:
driver:
"%database_driver%"
host:
"%database_host%"
dbname:
"%database_name%"
user:
"%database_user%"
password: "%database_password%"
charset: UTF8
orm:
auto_generate_proxy_classes: "%kernel.debug%"
auto_mapping: true
# Swiftmailer Configuration
swiftmailer:
transport: "%mailer_transport%"
host:
"%mailer_host%"
username: "%mailer_user%"
password: "%mailer_password%"
jms_security_extra:
secure_controllers: true
secure_all_services: false
Each entry like framework defines the configuration for a specific bundle. For example, framework
configures the FrameworkBundle while swiftmailer configures the SwiftmailerBundle.
Each environment can override the default configuration by providing a specific configuration file. For
example, the dev environment loads the config_dev.yml file, which loads the main configuration (i.e.
config.yml) and then modifies it to add some debugging tools:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#Listing
app/config/config_dev.yml
4-9
imports:
- { resource: config.yml }
Listing
4-10
framework:
router:
{ resource: "%kernel.root_dir%/config/routing_dev.yml" }
profiler: { only_exceptions: false }
web_profiler:
toolbar: true
intercept_redirects: false
monolog:
handlers:
main:
type:
path:
level:
firephp:
type:
level:
stream
"%kernel.logs_dir%/%kernel.environment%.log"
debug
firephp
info
assetic:
use_controller: true
Extending a Bundle
In addition to being a nice way to organize and configure your code, a bundle can extend another bundle.
Bundle inheritance allows you to override any existing bundle in order to customize its controllers,
templates, or any of its files. This is where the logical names (e.g. @AcmeDemoBundle/Controller/
SecuredController.php) come in handy: they abstract where the resource is actually stored.
more interesting when you realize they don't need to be stored on the filesystem. You can easily store
them in a database table for instance.
Extending Bundles
If you follow these conventions, then you can use bundle inheritance to "override" files, controllers
or templates. For example, you can create a bundle - AcmeNewBundle - and specify that it overrides
AcmeDemoBundle. When Symfony loads the AcmeDemoBundle:Welcome:index controller, it will first
look for the WelcomeController class in AcmeNewBundle and, if it doesn't exist, then look inside
AcmeDemoBundle. This means that one bundle can override almost any part of another bundle!
Do you understand now why Symfony2 is so flexible? Share your bundles between applications, store
them locally or globally, your choice.
Using Vendors
Odds are that your application will depend on third-party libraries. Those should be stored in the
vendor/ directory. This directory already contains the Symfony2 libraries, the SwiftMailer library, the
Doctrine ORM, the Twig templating system, and some other third party libraries and bundles.
$ php app/console
Listing
Listing
1
4-13 4-14
Final Thoughts
Call me crazy, but after reading this part, you should be comfortable with moving things around and
making Symfony2 work for you. Everything in Symfony2 is designed to get out of your way. So, feel free
to rename and move directories around as you see fit.
And that's all for the quick tour. From testing to sending emails, you still need to learn a lot to become a
Symfony2 master. Ready to dig into these topics now? Look no further - go to the official The Book and
pick any topic you want.