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) ?>