You are on page 1of 108

Symfony Components

What's in for you?


Fabien Potencier

Fabien Potencier
Serial entrepreneur and developer by passion
Founder of Sensio (in 1998)
A services and consulting company
specialized in Web technologies
and Internet marketing (France and USA)
70 people
Open-Source specialists
Big corporate customers
Consulting, training, development, web design, and more
Sponsor of a lot of Open-Source projects
like symfony and Doctrine

Fabien Potencier
Creator and lead developer of symfony
and creator and lead developer of some more OSS:
symfony components
Swift Mailer : Powerful component based mailing library for PHP
Twig : Fexible, fast, and secure template language for PHP
Pirum : Simple PEAR Channel Server Manager
Sismo : PHP continuous integration server
Lime : Easy to use unit testing library for PHP
Twitto : A web framework in a tweet
Twittee : A Dependency Injection Container in a tweet
Pimple : A small PHP 5.3 dependency injection container

Fabien Potencier
Read my technical blog: http://fabien.potencier.org/
Follow me on Twitter: @fabpot
Fork my code on Github: http://github.com/fabpot/

How many of you use


symfony?

symfony
Full-stack framework (MVC architecture)
symfony provides the infrastructure/tools needed for 95% of the web
projects
Open-Source (MIT License) since 2005
Based on
11 years of Sensio experience building websites for its customers
Existing Open-Source projects

But wait,
symfony is a monolithic
framework, right?

A bit of history
symfony 1.0 (January 2007) started as a glue between existing
Open-Source libraries

A bit of history
symfony 1.1 (June 2008) was a big refactoring of the code base
Decoupled the main component: like Forms, Routing, Cache, YAML, ORMs,
sfRequest

sfYAML

sfRouting

sfDatabase

sfLogger

sfForm

sfValidator

sfI18N

sfEventDispatcher

sfWidget

sfUser

sfStorage

sfResponse

sfCache

sfOutputEscaper

sfCoreAutoload

platform

The symfony (1.2/1.3/1.4) MVC


framework is based on a set of
cohesive but decoupled classes,
the symfony components

Symfony Components

Announced in May 2009


Standalone components
Packaged individually
No dependencies
Release cycle independent of Symfony, the framework

Symfony Components

Dedicated website for each component (with code and documentation)


http://components.symfony-project.org/

Dedicated Subversion and Git repository


http://svn.symfony-project.com/components/
http://github.com/fabpot

Symfony Components

They have been migrated to PHP 5.3


http://svn.symfony-project.com/branches/2.0/lib/Symfony/Components

Symfony Components

Each old PHP 5.2 symfony component has been branched


and a 1.0 version will be released soon
http://svn.symfony-project.com/components/

Symfony Components
Extracted from symfony 1
Event Dispatcher
YAML
Output Escaper

Written from scratch for Symfony 2


Dependency Injection Container
Request Handler
Templating

We dont want to duplicate eort done by Zend Framework

Symfony\Components\Templating

Symfony Templating

Thin layer on top of PHP


that adds some template-oriented features

Can work with Template Engines (Smarty, Twig, )

New Templating Framework


4 sub-components
Template Engine
Template Renderers
Template Loaders
Template Storages

use Symfony\Components\Templating\Engine;
use Symfony\Components\Templating\Loader\FilesystemLoader;
$loader = new FilesystemLoader(
'/path/to/templates/%name%.php'
);
$t = new Engine($loader);
echo $t->render('index', array('name' => 'Fabien'));

Template Loaders
No assumption about where and how templates are to be found
Filesystem
Database
Memory,

Built-in loaders: FilesystemLoader, ChainLoader,


Template names are logical names:

CacheLoader

$loader = new FilesystemLoader('/path/to/templates/%name%.php');

Template Renderers
No assumption about the format of the templates
Template names are prexed with the renderer name:
index == php:index
user:index
$t = new Engine($loader, array(
'user' => new ProjectTemplateRenderer(),
'php' => new PhpRenderer(),
));

Template Embedding

Hello <?php echo $name ?>


<?php $this->render('embedded', array('name' => $name)) ?>
<?php $this->render('smarty:embedded') ?>

Template Inheritance
<?php $this->extend('layout') ?>
Hello <?php echo $name ?>
<html>
<head>
</head>
<body>
<?php $this->output('content') ?>
</body>
</html>

Template Slots
<html>
<head>
<title><?php $this->output('title') ?></title>
</head>
<body>
<?php $this->output('content') ?>
</body>
</html>
<?php $this->set('title', 'Hello World! ') ?>
<?php $this->start('title') ?>
Hello World!
<?php $this->stop() ?>

Template Multiple Inheritance

A layout can be decorated by another layout


Each layout can override slots

Templating
A CMS example

http://fabien.potencier.org/talk/33/php-barcelona-symfony-2-0-on-PHP-5-3?position=76

Symfony Components
Dependency Injection

Symfony\Components\DependencyInjection

Dependency Injection is where components


are given their dependencies through their
constructors, methods, or directly into elds.
http://www.picocontainer.org/injection.html

class Message
{
public function __construct()
{
$this->output = new Output();
}
}

class Message
{
public function __construct(OutputInterface $output)
{
$this->output = $output;
}
}

DI Hello World example


class Message
{
public function __construct(OutputInterface $output, array $options)
{
$this->output = $output;
$this->options = array_merge(array('with_newline' => false), $options);
}
public function say($msg)
{
$this->output->render($msg.($this->options['with_newline'] ? "\n" : ''));
}
}

DI Hello World example


interface OutputInterface
{
public function render($msg);
}
class Output implements OutputInterface
{
public function render($msg)
{
echo $msg;
}
}
class FancyOutput implements OutputInterface
{
public function render($msg)
{
echo sprintf("\033[33m%s\033[0m", $msg);
}
}

DI Hello World example

$output = new FancyOutput();


$message = new Message($output, array('with_newline' => true));
$message->say('Hello World');

A DI container facilitates
objects description and object relationships,
congures and instantiates objects

DI Container Hello World example


use Symfony\Components\DependencyInjection\Builder;
use Symfony\Components\DependencyInjection\Reference;
$container = new Builder();
$container->register('output', 'FancyOutput');
$container->
register('message', 'Message')->
setArguments(array(new Reference('output'), array('with_newline' => true)))
;
$container->message->say('Hello World!');

$message = $container->message;
Get the conguration for the message service
The Message constructor must be given an output service
Get the output object from the container
Create a Message object by passing the constructor arguments

$message = $container->message;

is roughly equivalent to
$output = new FancyOutput();
$message = new Message($output, array('with_newline' => true));!

$container = new Builder();


$container->register('output', 'FancyOutput');
$container->
register('message', 'Message')->
setArguments(array(new Reference('output'), array('with_newline' => true)))
;
$container->message->say('Hello World!');

PHP

<container xmlns="http://symfony-project.org/2.0/container">
<services>
<service id="output" class="FancyOutput" />

XML

<service id="message" class="Message">


<argument type="service" id="output" />
<argument type="collection">
<argument key="with_newline">true</argument>
</argument>
</service>
</services>
</container>
$container = new Builder();
$loader = new XmlFileLoader($container);
$loader->load('services.xml');

XML is validated
against an XSD

$container = new Builder();


$container->register('output', 'FancyOutput');
$container->
register('message', 'Message')->
setArguments(array(new sfServiceReference('output'), array('with_newline' => true)))
;
$container->message->say('Hello World!');
services:
output: { class: FancyOutput }
message:
class: Message
arguments:
- @output
- { with_newline: true }
$container = new Builder();
$loader = new YamlFileLoader($container);
$loader->load('services.yml');

PHP
YAML

<container xmlns="http://symfony-project.org/2.0/container">
<parameters>
<parameter key="output.class">FancyOutput</parameter>
<parameter key="message.options" type="collection">
<parameter key="with_newline">true</parameter>
</parameter>
</parameters>
<services>
<service id="output" class="%output.class%" />
<service id="message" class="Message">
<argument type="service" id="output" />
<argument>%message.options%</argument>
</service>
</services>
</container>
$container = new Builder();
$loader = new XmlFileLoader($container);
$loader->load('services.xml');

<container xmlns="http://symfony-project.org/2.0/container">
<imports>
<import resource="config.xml" />
</imports>
<services>
<service id="output" class="%output.class%" />
<service id="message" class="Message">
<argument type="service" id="output" />
<argument>%message.options%</argument>
</service>
</services>
</container>
<container xmlns="http://symfony-project.org/2.0/container">
<parameters>
<parameter key="output.class">FancyOutput</parameter>
<parameter key="message.options" type="collection">
<parameter key="with_newline">true</parameter>
</parameter>
</parameters>
</container>
$container = new Builder();
$loader = new FileXmlFileLoader($container);
$loader->load('services.xml');

<services>
<import resource="config.yml" class="Symfony\Components\DependencyInjection
\Loader\YamlFileLoader" />
<service id="output" class="%output.class%" />
<service id="message" class="Message">
<argument type="service" id="output" />
<argument>%message.options%</argument>
</service>
</services>
parameters:
output.class: FancyOutput
message.options: { with_newline: true }
$container = new Builder();
$loader = new XmlFileLoader($container);
$loader->load('services.xml');

Loaders & Dumpers


IniFileLoader
XmlFileLoader
YamlFileLoader

XmlDumper
YamlDumper
Make your container
PhpDumper
VERY fast
GraphvizDumper

use
use
use
use
use
use
use

Symfony\Components\DependencyInjection\Builder;
Symfony\Components\DependencyInjection\Reference;
Symfony\Components\DependencyInjection\Dumper\XmlDumper;
Symfony\Components\DependencyInjection\Dumper\YamlDumper;
Symfony\Components\DependencyInjection\Dumper\PhpDumper;
Symfony\Components\DependencyInjection\Loader\XmlFileLoader;
Symfony\Components\DependencyInjection\Loader\YamlFileLoader;

$container = new Builder();


$container->register('output', 'FancyOutput');
$container->
register('message', 'Message')->
setArguments(array(new Reference('output'), array('with_newline' => true)))
;

$dumper = new XmlDumper($container);


file_put_contents(__DIR__.'/container.xml', $dumper->dump());
$loader = new XmlFileLoader($container);
$loader->load(__DIR__.'/container.xml');
$dumper = new YamlDumper($container);
file_put_contents(__DIR__.'/container.yml', $dumper->dump());
$loader = new YamlFileLoader($container);
$loader->load(__DIR__.'/container.yml');
$dumper = new PhpDumper($container);
echo $dumper->dump();

use Symfony\Components\DependencyInjection\Container;
use Symfony\Components\DependencyInjection\Reference;
use Symfony\Components\DependencyInjection\Parameter;
class ProjectServiceContainer extends Container
{
protected $shared = array();
protected function getOutputService()
{
if (isset($this->shared['output'])) return $this->shared['output'];
$instance = new FancyOutput();
return $this->shared['output'] = $instance;
}
protected function getMessageService()
{
if (isset($this->shared['message'])) return $this->shared['message'];
$instance = new Message($this->getService('output'), array('with_newline' => true));
return $this->shared['message'] = $instance;
}
}

use Symfony\Components\DependencyInjection\Dumper\GraphvizDumper;
$dumper = new GraphvizDumper($container);
echo $dumper->dump();

digraph sc {
ratio="compress"
node [fontsize="11" fontname="Arial" shape="record"];
edge [fontsize="9" fontname="Arial" color="grey" arrowhead="open" arrowsize="0.5"];
node_output [label="output\nFancyOutput\n", shape=record, fillcolor="#eeeeee", style="filled"];
node_message [label="message\nMessage\n", shape=record, fillcolor="#eeeeee", style="filled"];
node_service_container [label="service_container\nSymfony\\Components\\DependencyInjection\\Builder
\n", shape=record, fillcolor="#9999ff", style="filled"];
node_message -> node_output [label="" style="filled"];
}

Symfony Components
Output Escaper

Symfony\Components\OutputEscaper

Output Escaper
Provides automatic XSS protection for your templates
By wrapping template variables
Works for
strings
arrays
objects

properties
methods
__call(), __get(),
Iterators, Coutables,

Works for deep method calls

use Symfony\Components\OutputEscaper\Escaper;
$title = 'Foo <br />';
echo Escaper::escape(ESC_SPECIALCHARS, $title);

use Symfony\Components\OutputEscaper\Escaper;
$article = array(
'title' => 'Foo <br />',
'author' => array(
'name' => 'Fabien <br/>',
)
);
$article = Escaper::escape(ESC_SPECIALCHARS, $article);
echo $article['title']."\n";
echo $article['author']['name']."\n";

class Article
{
protected $title;
protected $author;
public $full_title;

public property

public function __construct($title, Author $author)


{
$this->title = $title;
$this->full_title = $title;
$this->author = $author;
}
public method
public function getTitle() { return $this->title; }
public function getAuthor() { return $this->author; }
public function __get($key) { return $this->$key; }
public function __call($method, $arguments)
{
magic __call()
return $this->{'get'.$method}();
}
}

public method returning


another object
magic __get()

class Author
{
protected $name;
public function __construct($name) { $this->name = $name; }
public function getName() { return $this->name; }
}

use Symfony\Components\OutputEscaper\Escaper;
$article = new Article(
'foo <br />',
new Author('Fabien <br />')
);
$article = Escaper::escape(ESC_SPECIALCHARS, $article);
echo
echo
echo
echo
echo

$article->getTitle()."\n";
$article->getAuthor()->getName()."\n";
$article->full_title."\n";
$article->title."\n";
$article->title()."\n";

Escaping Strategy
explicitly ask
for raw data
echo $article->getHtmlContent(ESC_RAW);

echo $article->getTitle(ESC_JS);
change the default
escaping strategy

symfony-live.com

with Matthew Weier OPheinney

I will reveal the first alpha release of Symfony 2.0!

symfony-live.com

with Matthew Weier OPheinney

with Matthew Weier O'Phinney as a special guest

Thank you!
My slides on slideshare.com/fabpot
with a bonus: How to use Symfony Components with ZF!

Sensio S.A.
92-98, boulevard Victor Hugo
92 115 Clichy Cedex
FRANCE
Tl. : +33 1 40 99 80 80
Contact
Fabien Potencier
fabien.potencier at sensio.com

http://www.sensiolabs.com/
http://www.symfony-project.org/
http://fabien.potencier.org/

S
U
N
BO
How to use Symfony Components
within a Zend Framework project?

Symfony components in a ZF project


Symfony Components can make your ZF application better
More congurable: symfony YAML
More exible: symfony Event Dispatcher
Faster: symfony Dependency Injection
More secure: symfony Output Escaper

and more fun of course

Symfony Components in a ZF project


The examples in this presentation are just to get you started faster
So, be creative with them. They open all kind of opportunities for your
next Zend Framework project
And please, give me feedback, and tell me what you do with the
Symfony Components

Symfony Components
Event Dispatcher

class Bootstrap extends Zend_Application_Bootstrap_Bootstrap


{
public function run()
{
require_once '/path/to/sfEventDispatcher.php';
$dispatcher = new sfEventDispatcher();
$event = new sfEvent(null, 'bootstrap.prerun');
$dispatcher->notify($event);
parent::run();
$event = new sfevent(null, 'bootstrap.postrun');
$dispatcher->notify($event);
}
}

Symfony Components
Dependency Injection

class Bootstrap extends Zend_Application_Bootstrap_Bootstrap


{
public function getContainer()
{
if (null === $this->_container)
{
$this->setContainer($this->_initContainer());
}
return $this->_container;
}
protected function _initContainer()
{
require_once '/path/to/sfServiceContainerAutoloader.php';
sfServiceContainerAutoloader::register();

Example from Ben Eberlei (he rocks!): http://www.whitewashing.de/blog/articles/118

$container = new sfServiceContainerBuilder();


$loader = new sfServiceContainerLoaderFileXml($container);
$loader->load(dirname(__FILE__).'/configs/resources.xml');
return $container;
}
}

<?xml version="1.0" ?>


<container xmlns="http://symfony-project.org/2.0/container">
<parameters>
<parameter key="mailer.username">foo</parameter>
<parameter key="mailer.password">bar</parameter>
<parameter key="mailer.class">Zend_Mail</parameter>
</parameters>
<services>
<service id="mail.transport" class="Zend_Mail_Transport_Smtp" shared="false">
<argument>smtp.gmail.com</argument>
<argument type="collection">
<argument key="auth">login</argument>
<argument key="username">%mailer.username%</argument>
<argument key="password">%mailer.password%</argument>
<argument key="ssl">ssl</argument>
<argument key="port">465</argument>
</argument>
</service>
<service id="mailer" class="%mailer.class%">
<call method="setDefaultTransport">
<argument type="service" id="mail.transport" />
</call>
</service>
</services>
</container>

parameters:
mailer.username: foo
mailer.password: bar
mailer.class:
Zend_Mail
services:
mail.transport:
class:
Zend_Mail_Transport_Smtp
arguments: [smtp.gmail.com, { auth: login, username:
%mailer.username%, password: %mailer.password%, ssl: ssl, port: 465 }]
shared:
false
mailer:
class: %mailer.class%
calls:
- [setDefaultTransport, [@mail.transport]]

class GuestbookController extends Zend_Controller_Action


{
public function indexAction()
{
$guestbook = new Default_Model_Guestbook();
$this->view->entries = $guestbook->fetchAll();
$container = $this->getInvokeArg('bootstrap')->getContainer();
$mailer = $container->mailer;
}
}

Dependency Injection Container


By default in ZF, new resources can be added to the container but cannot
be lazy-loaded
All resources used by Zend_Application are loaded on every request

By using symfony Service Container, the resources are lazy-loaded


Instances and their dependencies are created the rst time you get them

Interesting for resources like DB

Symfony Components
Output Escaper

require_once '/path/to/sfOutputEscaperAutoloader.php';
sfOutputEscaperAutoloader::register();
class My_View extends Zend_View
{
public function __set($key, $val)
{
if ('_' === substr($key, 0, 1))
{
// throw an exception
}
$this->$key = sfOutputEscaper::escape(array($this, 'escape'), $val);
}
}

<dl>
<?php foreach ($this->entries as $entry): ?>
<dt><?php echo $this->escape($entry->email) ?></dt>
<dd><?php echo $this->escape($entry->comment) ?></dd>
<?php endforeach ?>
</dl>
<dl>
<?php foreach ($this->entries as $entry): ?>
<dt><?php echo $entry->email ?></dt>
<dd><?php echo $entry->comment ?></dd>
<?php endforeach ?>
</dl>
<?php echo $entry->getRawValue()->comment ?>
<?php echo $entry->getComment(ESC_RAW) ?>

You might also like