dependency injection with php and php 5.3

105
Dependency Injection with PHP and PHP 5.3 Fabien Potencier

Upload: fabien-potencier

Post on 15-Jan-2015

28.454 views

Category:

Technology


6 download

DESCRIPTION

 

TRANSCRIPT

Page 1: Dependency Injection with PHP and PHP 5.3

Dependency Injection with PHP and PHP 5.3

Fabien Potencier

Page 2: Dependency Injection with PHP and PHP 5.3

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

Page 3: Dependency Injection with PHP and PHP 5.3

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

Page 4: Dependency Injection with PHP and PHP 5.3

Fabien Potencier

•  Read my technical blog: http://fabien.potencier.org/

•  Follow me on Twitter: @fabpot

•  Fork my code on Github: http://github.com/fabpot/

Page 5: Dependency Injection with PHP and PHP 5.3

Dependency Injection

A real world « web » example

Page 6: Dependency Injection with PHP and PHP 5.3

In most web applications, you need to manage the user preferences

–  The user language – Whether the user is authenticated or not –  The user credentials – …

Page 7: Dependency Injection with PHP and PHP 5.3

This can be done with a User object

–  setLanguage(), getLanguage() –  setAuthenticated(), isAuthenticated() –  addCredential(), hasCredential() –  ...

Page 8: Dependency Injection with PHP and PHP 5.3

The User information need to be persisted

between HTTP requests

We use the PHP session for the Storage

Page 9: Dependency Injection with PHP and PHP 5.3

class SessionStorage { function __construct($cookieName = 'PHP_SESS_ID') { session_name($cookieName); session_start(); }

function set($key, $value) { $_SESSION[$key] = $value; }

// ... }

Page 10: Dependency Injection with PHP and PHP 5.3

class User { protected $storage;

function __construct() { $this->storage = new SessionStorage(); }

function setLanguage($language) { $this->storage->set('language', $language); }

// ... }

$user = new User();

Very easy to use

Very hard to

customize

Page 11: Dependency Injection with PHP and PHP 5.3

class User { protected $storage;

function __construct($storage) { $this->storage = $storage; } }

$storage = new SessionStorage(); $user = new User($storage);

Slightly more

difficult to use

Very easy to

customize

Page 12: Dependency Injection with PHP and PHP 5.3

That’s Dependency Injection

Nothing more

Page 13: Dependency Injection with PHP and PHP 5.3

Let’s understand why the first example is not a good idea

Page 14: Dependency Injection with PHP and PHP 5.3

I want to change the session cookie name

Page 15: Dependency Injection with PHP and PHP 5.3

class User { protected $storage;

function __construct() { $this->storage = new SessionStorage('SESSION_ID'); }

function setLanguage($language) { $this->storage->set('language', $language); }

// ... }

$user = new User();

Hardcode it in the

User class

Page 16: Dependency Injection with PHP and PHP 5.3

class User { protected $storage;

function __construct() { $this->storage = new SessionStorage(STORAGE_SESSION_NAME); } }

define('STORAGE_SESSION_NAME', 'SESSION_ID');

$user = new User();

Add a global

configuration?

Page 17: Dependency Injection with PHP and PHP 5.3

class User { protected $storage;

function __construct($sessionName) { $this->storage = new SessionStorage($sessionName); } }

$user = new User('SESSION_ID');

Configure via

User?

Page 18: Dependency Injection with PHP and PHP 5.3

class User { protected $storage;

function __construct($storageOptions) { $this->storage = new SessionStorage($storageOptions['session_name']);

$user = new User( array('session_name' => 'SESSION_ID') );

Configure with an

array?

Page 19: Dependency Injection with PHP and PHP 5.3

I want to change the session storage implementation

Filesystem MySQL

Memcached …

Page 20: Dependency Injection with PHP and PHP 5.3

class User { protected $storage;

function __construct() { $this->storage = Registry::get('session_storage'); } }

$storage = new SessionStorage(); Registry::set('session_storage', $storage); $user = new User();

Use a global

registry object?

Page 21: Dependency Injection with PHP and PHP 5.3

Now, the User depends on the Registry

Page 22: Dependency Injection with PHP and PHP 5.3

Instead of harcoding the Storage dependency

inside the User class constructor

Inject the Storage dependency in the User object

Page 23: Dependency Injection with PHP and PHP 5.3

class User { protected $storage;

function __construct($storage) { $this->storage = $storage; } }

$storage = new SessionStorage('SESSION_ID'); $user = new User($storage);

Page 24: Dependency Injection with PHP and PHP 5.3

What are the advantages?

Page 25: Dependency Injection with PHP and PHP 5.3

Use different Storage strategies

Page 26: Dependency Injection with PHP and PHP 5.3

class User { protected $storage;

function __construct($storage) { $this->storage = $storage; } }

$storage = new MySQLSessionStorage('SESSION_ID'); $user = new User($storage);

Use a different

Storage engine

Page 27: Dependency Injection with PHP and PHP 5.3

Configuration becomes natural

Page 28: Dependency Injection with PHP and PHP 5.3

class User { protected $storage;

function __construct($storage) { $this->storage = $storage; } }

$storage = new MySQLSessionStorage('SESSION_ID'); $user = new User($storage);

Configuration

is natural

Page 29: Dependency Injection with PHP and PHP 5.3

Wrap third-party classes (Interface / Adapter)

Page 30: Dependency Injection with PHP and PHP 5.3

class User { protected $storage;

function __construct(SessionStorageInterface $storage) { $this->storage = $storage; } }

interface SessionStorageInterface { function get($key);

function set($key, $value); }

Add an interface

Page 31: Dependency Injection with PHP and PHP 5.3

Mock the Storage object (for testing)

Page 32: Dependency Injection with PHP and PHP 5.3

class User { protected $storage;

function __construct(SessionStorageInterface $storage) { $this->storage = $storage; } }

class SessionStorageForTests implements SessionStorageInterface { protected $data = array();

static function set($key, $value) { self::$data[$key] = $value; } }

Mock the Session

Page 33: Dependency Injection with PHP and PHP 5.3

Use different Storage strategies

Configuration becomes natural

Wrap third-party classes (Interface / Adapter)

Mock the Storage object (for testing)

Easy without changing the User class

Page 34: Dependency Injection with PHP and PHP 5.3

« Dependency Injection is where components are given their dependencies

through their constructors, methods, or directly into fields. »

http://www.picocontainer.org/injection.html

Page 35: Dependency Injection with PHP and PHP 5.3

$storage = new SessionStorage();

// constructor injection $user = new User($storage);

// setter injection $user = new User(); $user->setStorage($storage);

// property injection $user = new User(); $user->storage = $storage;

Page 36: Dependency Injection with PHP and PHP 5.3

A slightly more complex web example

Page 37: Dependency Injection with PHP and PHP 5.3

$request = new Request(); $response = new Response();

$storage = new FileSessionStorage('SESSION_ID'); $user = new User($storage);

$cache = new FileCache( array('dir' => dirname(__FILE__).'/cache') ); $routing = new Routing($cache);

Page 38: Dependency Injection with PHP and PHP 5.3

class Application { function __construct() { $this->request = new WebRequest(); $this->response = new WebResponse();

$storage = new FileSessionStorage('SESSION_ID'); $this->user = new User($storage);

$cache = new FileCache( array('dir' => dirname(__FILE__).'/cache') ); $this->routing = new Routing($cache); } }

$application = new Application();

Page 39: Dependency Injection with PHP and PHP 5.3

Back to square 1

Page 40: Dependency Injection with PHP and PHP 5.3

class Application { function __construct() { $this->request = new WebRequest(); $this->response = new WebResponse();

$storage = new FileSessionStorage('SESSION_ID'); $this->user = new User($storage);

$cache = new FileCache( array('dir' => dirname(__FILE__).'/cache') ); $this->routing = new Routing($cache); } }

$application = new Application();

Page 41: Dependency Injection with PHP and PHP 5.3

We need a Container

Describes objects and their dependencies

Instantiates and configures objects on-demand

Page 42: Dependency Injection with PHP and PHP 5.3

A container SHOULD be able to manage

any PHP object (POPO)

The objects MUST not know that they are managed

by a container

Page 43: Dependency Injection with PHP and PHP 5.3

•  Parameters –  The SessionStorageInterface implementation we want to use (the class name) –  The session name

•  Objects –  SessionStorage –  User

•  Dependencies –  User depends on a SessionStorageInterface implementation

Page 44: Dependency Injection with PHP and PHP 5.3

Let’s build a simple container with PHP 5.3

Page 45: Dependency Injection with PHP and PHP 5.3

DI Container

Managing parameters

Page 46: Dependency Injection with PHP and PHP 5.3

class Container { protected $parameters = array();

public function setParameter($key, $value) { $this->parameters[$key] = $value; }

public function getParameter($key) { return $this->parameters[$key]; } }

Page 47: Dependency Injection with PHP and PHP 5.3

$container = new Container(); $container->setParameter('session_name', 'SESSION_ID'); $container->setParameter('storage_class', 'SessionStorage');

$class = $container->getParameter('storage_class'); $sessionStorage = new $class($container->getParameter('session_name')); $user = new User($sessionStorage);

Decoupling

Customization

Objects creation

Page 48: Dependency Injection with PHP and PHP 5.3

class Container { protected $parameters = array();

public function __set($key, $value) { $this->parameters[$key] = $value; }

public function __get($key) { return $this->parameters[$key]; } }

Using PHP

magic methods

Page 49: Dependency Injection with PHP and PHP 5.3

$container = new Container(); $container->session_name = 'SESSION_ID'; $container->storage_class = 'SessionStorage';

$sessionStorage = new $container->storage_class($container->session_name); $user = new User($sessionStorage);

Interface

is cleaner

Page 50: Dependency Injection with PHP and PHP 5.3

DI Container

Managing objects

Page 51: Dependency Injection with PHP and PHP 5.3

We need a way to describe how to create objects, without actually instantiating anything!

Anonymous functions to the rescue!

Page 52: Dependency Injection with PHP and PHP 5.3

Anonymous Functions / Lambdas

A lambda is a function defined on the fly

with no name

function () { echo 'Hello world!'; };

Page 53: Dependency Injection with PHP and PHP 5.3

Anonymous Functions / Lambdas

A lambda can be stored in a variable

$hello = function () { echo 'Hello world!'; };

Page 54: Dependency Injection with PHP and PHP 5.3

Anonymous Functions / Lambdas

And then it can be used as any other PHP callable

$hello();

call_user_func($hello);

Page 55: Dependency Injection with PHP and PHP 5.3

Anonymous Functions / Lambdas

You can also pass a lambda as an argument to a function or method

function foo(Closure $func) { $func(); }

foo($hello);

Page 56: Dependency Injection with PHP and PHP 5.3

Fonctions anonymes $hello = function ($name) { echo 'Hello '.$name; };

$hello('Fabien');

call_user_func($hello, 'Fabien');

function foo(Closure $func, $name) { $func($name); }

foo($hello, 'Fabien');

Page 57: Dependency Injection with PHP and PHP 5.3

DI Container

Managing objects

Page 58: Dependency Injection with PHP and PHP 5.3

class Container { protected $parameters = array(); protected $objects = array();

public function __set($key, $value) { $this->parameters[$key] = $value; }

public function __get($key) { return $this->parameters[$key]; }

public function setService($key, Closure $service) { $this->objects[$key] = $service; }

public function getService($key) { return $this->objects[$key]($this); } }

Store a lambda

able to create the

object on-demand

Ask the closure to create

the object and pass the

current Container

Page 59: Dependency Injection with PHP and PHP 5.3

$container = new Container(); $container->session_name = 'SESSION_ID'; $container->storage_class = 'SessionStorage'; $container->setService('user', function ($c) { return new User($c->getService('storage')); }); $container->setService('storage', function ($c) { return new $c->storage_class($c->session_name); });

$user = $container->getService('user');

Creating the User

is now as easy as before

Description

Page 60: Dependency Injection with PHP and PHP 5.3

class Container { protected $values = array();

function __set($id, $value) { $this->values[$id] = $value; }

function __get($id) { if (is_callable($this->values[$id])) { return $this->values[$id]($this); } else { return $this->values[$id]; } } }

Simplify the code

Page 61: Dependency Injection with PHP and PHP 5.3

$container = new Container(); $container->session_name = 'SESSION_ID'; $container->storage_class = 'SessionStorage'; $container->user = function ($c) { return new User($c->storage); }; $container->storage = function ($c) { return new $c->storage_class($c->session_name); };

$user = $container->user;

Unified interface

Page 62: Dependency Injection with PHP and PHP 5.3

DI Container

Scope

Page 63: Dependency Injection with PHP and PHP 5.3

For some objects, like the user, the container must always return the same instance

Page 64: Dependency Injection with PHP and PHP 5.3

spl_object_hash($container->user)

!== spl_object_hash($container->user)

Page 65: Dependency Injection with PHP and PHP 5.3

$container->user = function ($c) { static $user;

if (is_null($user)) { $user = new User($c->storage); }

return $user; };

Page 66: Dependency Injection with PHP and PHP 5.3

spl_object_hash($container->user)

=== spl_object_hash($container->user)

Page 67: Dependency Injection with PHP and PHP 5.3

$container->user = $container->asShared(function ($c) { return new User($c->storage); });

Page 68: Dependency Injection with PHP and PHP 5.3

A closure is a lambda that remembers the context

of its creation…

Page 69: Dependency Injection with PHP and PHP 5.3

class Article { public function __construct($title) { $this->title = $title; }

public function getTitle() { return $this->title; } }

$articles = array( new Article('Title 1'), new Article('Title 2'), );

Page 70: Dependency Injection with PHP and PHP 5.3

$mapper = function ($article) { return $article->getTitle(); };

$titles = array_map($mapper, $articles);

Page 71: Dependency Injection with PHP and PHP 5.3

$method = 'getTitle';

$mapper = function ($article) use($method) { return $article->$method(); };

$method = 'getAuthor';

$titles = array_map($mapper, $articles);

Page 72: Dependency Injection with PHP and PHP 5.3

$mapper = function ($method) { return function ($article) use($method) { return $article->$method(); }; };

Page 73: Dependency Injection with PHP and PHP 5.3

$titles = array_map($mapper('getTitle'), $articles);

$authors = array_map($mapper('getAuthor'), $articles);

Page 74: Dependency Injection with PHP and PHP 5.3

$container->user = $container->asShared(function ($c) { return new User($c->storage); });

Page 75: Dependency Injection with PHP and PHP 5.3

function asShared(Closure $lambda) { return function ($container) use ($lambda) { static $object;

if (is_null($object)) { $object = $lambda($container); } return $object; }; }

Page 76: Dependency Injection with PHP and PHP 5.3

class Container { protected $values = array();

function __set($id, $value) { $this->values[$id] = $value; }

function __get($id) { if (!isset($this->values[$id])) { throw new InvalidArgumentException(sprintf('Value "%s" is not defined.', $id)); }

if (is_callable($this->values[$id])) { return $this->values[$id]($this); } else { return $this->values[$id]; } } }

Error management

Page 77: Dependency Injection with PHP and PHP 5.3

class Container { protected $values = array();

function __set($id, $value) { $this->values[$id] = $value; }

function __get($id) { if (!isset($this->values[$id])) { throw new InvalidArgumentException(sprintf('Value "%s" is not defined.', $id)); }

if (is_callable($this->values[$id])) { return $this->values[$id]($this); } else { return $this->values[$id]; } }

function asShared($callable) { return function ($c) use ($callable) { static $object;

if (is_null($object)) { $object = $callable($c); } return $object; }; } }

40 LOC for a fully-

featured container

Page 78: Dependency Injection with PHP and PHP 5.3

More about Dependency Injection

http://fabien.potencier.org/article/17/on-php-5-3-lambda-functions-and-closures

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

http://github.com/fabpot/pimple

http://twittee.org/

Page 79: Dependency Injection with PHP and PHP 5.3

Twittee: A Container in a tweet

•  Implementation does not use PHP 5.3 •  Its usage needs PHP 5.3

class Container { protected $s=array(); function __set($k, $c) { $this->s[$k]=$c; } function __get($k) { return $this->s[$k]($this); } }

twittee.org

Page 80: Dependency Injection with PHP and PHP 5.3

Remember, most of the time, you don’t need a Container

to use Dependency Injection

Page 81: Dependency Injection with PHP and PHP 5.3

You can start to use and benefit from Dependency Injection today

Page 82: Dependency Injection with PHP and PHP 5.3

by implementing it in your projects

by using externals libraries that already use DI

without the need of a container

Page 83: Dependency Injection with PHP and PHP 5.3

Symfony Zend Framework ezComponents

Doctrine Swift Mailer

Page 84: Dependency Injection with PHP and PHP 5.3

Questions?

My slides will be available on slideshare.com/fabpot

Page 85: Dependency Injection with PHP and PHP 5.3

with Matthew Weier O’Pheinney

I will reveal the first alpha release of Symfony 2.0!

symfony-live.com

Page 86: Dependency Injection with PHP and PHP 5.3

with Matthew Weier O’Pheinney

… with Matthew Weier O'Phinney as a special guest

symfony-live.com

Page 87: Dependency Injection with PHP and PHP 5.3

Sensio S.A. 92-98, boulevard Victor Hugo

92 115 Clichy Cedex FRANCE

Tél. : +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/

Page 88: Dependency Injection with PHP and PHP 5.3

Symfony Components Dependency Injection

BONUS

Page 89: Dependency Injection with PHP and PHP 5.3

Symfony\Components\DependencyInjection

Page 90: Dependency Injection with PHP and PHP 5.3

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!');

DI Container Hello World example

Page 91: Dependency Injection with PHP and PHP 5.3

Get the configuration 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;

Page 92: Dependency Injection with PHP and PHP 5.3

$message = $container->message;

is roughly equivalent to

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

Page 93: Dependency Injection with PHP and PHP 5.3

$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!');

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

<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');

PHP

XML XML is validated against an XSD

Page 94: Dependency Injection with PHP and PHP 5.3

$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

Page 95: Dependency Injection with PHP and PHP 5.3

<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');

Page 96: Dependency Injection with PHP and PHP 5.3

<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');

Page 97: Dependency Injection with PHP and PHP 5.3

<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');

Page 98: Dependency Injection with PHP and PHP 5.3

Loaders & Dumpers

•  IniFileLoader •  XmlFileLoader •  YamlFileLoader

•  XmlDumper •  YamlDumper •  PhpDumper •  GraphvizDumper

Make your container VERY fast

Page 99: Dependency Injection with PHP and PHP 5.3

use Symfony\Components\DependencyInjection\Builder; use Symfony\Components\DependencyInjection\Reference; use Symfony\Components\DependencyInjection\Dumper\XmlDumper; use Symfony\Components\DependencyInjection\Dumper\YamlDumper; use Symfony\Components\DependencyInjection\Dumper\PhpDumper; use Symfony\Components\DependencyInjection\Loader\XmlFileLoader; use 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))) ;

Page 100: Dependency Injection with PHP and PHP 5.3

$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();

Page 101: Dependency Injection with PHP and PHP 5.3

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; } }

Page 102: Dependency Injection with PHP and PHP 5.3

use Symfony\Components\DependencyInjection\Dumper\GraphvizDumper;

$dumper = new GraphvizDumper($container); echo $dumper->dump();

Page 103: Dependency Injection with PHP and PHP 5.3

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"]; }

Page 104: Dependency Injection with PHP and PHP 5.3
Page 105: Dependency Injection with PHP and PHP 5.3