More Related Content Similar to Symfony Components Similar to Symfony Components (20) More from Fabien Potencier More from Fabien Potencier (19) Symfony Components2. Serial entrepreneur
Developer by passion
Founder of Sensio
Creator and lead developer of Symfony
On Twitter @fabpot
On github http://www.github.com/fabpot
http://fabien.potencier.org/
The Symfony Components – Fabien Potencier
3. Standalone components for PHP 5.3
No dependency between them
Used extensively in Symfony 2, the framework
The Symfony Components – Fabien Potencier
5. Event Dispatcher Stable
Output Escaper Stable
Extracted from symfony 1
YAML Stable
Routing Beta
Console Stable
Dependency Injection Container Stable Written from scratch
for Symfony 2
Request Handler Stable
Templating Stable
The Symfony Components – Fabien Potencier
6. YAML
Event Dispatcher
PHP Quebec 2009
Templating
Dependency Injection Container
Console
Routing ConFoo 2010
Output Escaper
Request Handler
The Symfony Components – Fabien Potencier
11. app/
.../
Symfony/
Components/
Foundation/
Framework/
The Symfony Components – Fabien Potencier
14. PEAR_Log > PEAR/Log.php
Zend_Log > Zend/Log.php
Swift_Mime_Message > Swift/Mime/Message.php
Doctrine_Pager_Range > Doctrine/Pager/Range.php
Twig_Node_For > Twig/Node/For.php
The Symfony Components – Fabien Potencier
15. PEAR_Log > PEAR/Log.php
Zend_Log > Zend/Log.php
Swift_Mime_Message > Swift/Mime/Message.php
Doctrine_Pager_Range > Doctrine/Pager/Range.php
Twig_Node_For > Twig/Node/For.php
Vendor name
The Symfony Components – Fabien Potencier
16. As of PHP 5.3
PHP 5.3 technical
interoperability standards
The Symfony Components – Fabien Potencier
19. PHP 5.3 technical interoperability standards
« … describes the mandatory requirements
that must be adhered to
for autoloader interoperability »
http://groups.google.com/group/php-standards/web/psr-0-final-proposal
The Symfony Components – Fabien Potencier
22. $loader->registerNamespaces(array(
'Symfony' => '/path/to/symfony/src',
'Doctrine' => '/path/to/doctrine/lib',
'pdepend' => '/path/to/reflection/source',
));
PHP 5.3 technical
interoperability standards
The Symfony Components – Fabien Potencier
27. Automate things
code generators
deployment
The Symfony Components – Fabien Potencier
28. Long running tasks
deployment
get « things » from the Internet
The Symfony Components – Fabien Potencier
30. These tasks should never
be run from a browser
The Symfony Components – Fabien Potencier
31. But PHP is
a web language, right?
The Symfony Components – Fabien Potencier
32. So, why not use the right tool
for the job?
… like Perl or Python?
The Symfony Components – Fabien Potencier
33. Don’t want to use/learn another language
Want to share code
The Symfony Components – Fabien Potencier
38. … but the complexity lies in the details
The Symfony Components – Fabien Potencier
39. option / arguments handling
exit codes
shell
output colorization
tests
error messages
…
The Symfony Components – Fabien Potencier
40. $ ./life foo "foo bar" --foo foobar -b
Array
(
[0] => ./life
[1] => foo
[2] => foo bar
[3] => --foo
[4] => foobar
[5] => -b
)
The Symfony Components – Fabien Potencier
43. Let’s create a CLI tool
to get the weather
anywhere in the world
The Symfony Components – Fabien Potencier
45. use LifeYahooWeather;
$weather = new YahooWeather('API_KEY', $argv[1]);
echo $weather->getTitle()."n";
$attrs = $weather->getCurrentConditions();
echo "Current conditions:n";
echo sprintf(" %s, %sCn", $attrs['text'], $attrs['temp']);
$attrs = $weather->getForecast();
echo sprintf("nForecast for %sn", $attrs['date']);
echo sprintf(" %s, low: %s, high: %sn", $attrs['text'],
$attrs['low'], $attrs['high']);
The Symfony Components – Fabien Potencier
47. $command = new Command('weather');
$command->setCode(
function ($input, $output)
{
// do something
}
);
$application->addCommand($command);
The Symfony Components – Fabien Potencier
50. Console
The Output
The Symfony Components – Fabien Potencier
52. $output->writeln(
sprintf('<info>%s</info>', $weather->getTitle())
);
$output->writeln("<comment>Conditions</comment>");
The Symfony Components – Fabien Potencier
55. Console
Getting help
The Symfony Components – Fabien Potencier
57. $application = new Application('Life Tool', '0.1');
The Symfony Components – Fabien Potencier
58. class WeatherCommand extends Command
{
protected function configure()
{
$this->setName('weather')
->setDescription('Displays weather forecast')
->setHelp(<<<EOF
The <info>weather</info> command displays
weather forecast for a given city:
<info>./life weather Paris</info>
You can also change the default degree unit
with the <comment>--unit</comment> option:
<info>./life weather Paris --unit=c</info>
<info>./life weather Paris -u c</info>
EOF
);
}
The Symfony Components – Fabien Potencier
62. Console
The Input
The Symfony Components – Fabien Potencier
63. class WeatherCommand extends Command
{
protected function configure()
{
$definition = array(
new InputArgument('place',
InputArgument::OPTIONAL, 'The place name', 'Paris'),
new InputOption('unit', 'u',
InputOption::PARAMETER_REQUIRED, 'The degree unit',
'c'),
);
$this->setDefinition($definition);
The Symfony Components – Fabien Potencier
65. protected function execute(InputInterface $input,
OutputInterface $output)
{
$city = $input->getArgument('place');
$unit = $input->getOption('unit');
$output->writeln("<comment>Conditions</comment>");
}
The Symfony Components – Fabien Potencier
66. Console
Error codes / Exit status
The Symfony Components – Fabien Potencier
69. protected function execute(InputInterface $input,
OutputInterface $output)
{
$city = $input->getArgument('place');
$unit = $input->getOption('unit');
$output->writeln("<comment>Conditions</comment>");
return 120;
}
The Symfony Components – Fabien Potencier
70. Console
Interact with the user
The Symfony Components – Fabien Potencier
71. protected function interact($input, $output)
{
$city = $this->dialog->ask(
$output,
'<comment>Which city?</comment> (Paris)',
'Paris’
);
$input->setArgument('place', $city);
}
The Symfony Components – Fabien Potencier
73. dialog
ask()
askConfirmation()
askAndValidate()
formatter
formatSection()
formatBlock()
... your own
The Symfony Components – Fabien Potencier
74. class WeatherHelper extends Helper
{
public function __construct()
{
Output::setStyle('weather_hot', array('bg' => 'red', 'fg' => 'yellow'));
Output::setStyle('weather_cold', array('bg' => 'blue', 'fg' => 'white'));
}
public function formatTemperature($temperature, $unit)
{
$style = $temperature < 0 ? 'weather_cold' : 'weather_hot';
return sprintf("<%s> %s%s </%s>", $style, $temperature, strtoupper($unit),
$style);
}
public function getName()
{
return 'weather';
}
}
The Symfony Components – Fabien Potencier
75. $output->writeln(sprintf(
" %s, low: %s, high: %s",
$attrs['text'],
$this->weather->formatTemperature(
$attrs['low'],
$input->getOption('unit')),
$this->weather->formatTemperature(
$attrs['high'],
$input->getOption('unit'))
));
The Symfony Components – Fabien Potencier
77. Console
Testing
The Symfony Components – Fabien Potencier
78. $input = new ArrayInput(
array('place' => 'Paris', '--unit' => 'C')
);
$application->run($input);
$input = new StringInput('Paris --unit=C');
$application->run($input);
The Symfony Components – Fabien Potencier
79. $stream = fopen('php://memory', 'a', false);
$output = new StreamOutput($stream);
$application->run($input, $output);
rewind($output->getStream());
echo stream_get_contents($output->getStream());
The Symfony Components – Fabien Potencier
80. $application = new Application();
// for testing
$application->setCatchExceptions(false);
$application->setAutoExit(false);
The Symfony Components – Fabien Potencier
82. $command = new WeatherCommand();
echo $command->asXml();
The Symfony Components – Fabien Potencier
83. Create a PHAR archive
out of your CLI tool
for distribution
The Symfony Components – Fabien Potencier
84. $pharFile = 'life.phar’;
if (file_exists($pharFile))
unlink($pharFile);
$phar = new Phar($pharFile, 0, $this->application->getName());
$phar->setSignatureAlgorithm(Phar::SHA1);
$phar->startBuffering();
// CLI Component files
foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator(__DIR__.'/../symfony/src/Symfony/Components/
Console'), RecursiveIteratorIterator::LEAVES_ONLY) as $file)
{
$phar['Symfony/Components/Console'.str_replace(__DIR__.'/../symfony/src/Symfony/Components/Console', '', $file)] =
file_get_contents($file);
}
// Life stuff
foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator(__DIR__.'/../Life'),
RecursiveIteratorIterator::LEAVES_ONLY) as $file)
{
$phar['Life'.str_replace(__DIR__.'/../Life', '', $file)] = file_get_contents($file);
}
// Autoloader
$phar['Symfony/Foundation/UniversalClassLoader.php'] = file_get_contents(__DIR__.'/../symfony/src/Symfony/Foundation/
UniversalClassLoader.php');
// Stubs
$phar['_cli_stub.php'] = $this->getCliStub();
$phar['_web_stub.php'] = $this->getWebStub();
$phar->setDefaultStub('_cli_stub.php', '_web_stub.php');
$phar->stopBuffering();
$phar->compressFiles(Phar::GZ);
unset($phar);
The Symfony Components – Fabien Potencier
86. Routing
Pretty and Smart URLs
The Symfony Components – Fabien Potencier
88. Routing is a two-way process
Matching incoming requests (URLs)
Generating URLs
The Symfony Components – Fabien Potencier
90. Symfony one is built
with performance in mind
The Symfony Components – Fabien Potencier
91. Routing
Describing your routes
The Symfony Components – Fabien Potencier
93. $route = new Route(
'/:year/:month/:day/:slug',
array('to' => function ($params) { var_export
($params); }),
array('year' => 'd{4}')
);
$routes->addRoute('blog_post', $route);
The Symfony Components – Fabien Potencier
94. Routing
Matching URLs
The Symfony Components – Fabien Potencier
97. array (
'to' =>
Closure::__set_state(array(
)),
'year' => '2010',
'month' => '03',
'day' => '10',
'slug' => 'confoo',
'_route' => 'blog_post',
)
The Symfony Components – Fabien Potencier
99. Routing
Generating URLs
The Symfony Components – Fabien Potencier
101. $params = array(
'year' => 2010,
'month' => 10,
'day' => 10,
'slug' => 'another-one'
);
echo $generator->generate('blog_post', $params);
The Symfony Components – Fabien Potencier
102. $params = array(
'year' => 'yyyy',
'month' => 10,
'day' => 10,
);
echo $generator->generate('blog_post', $params);
Uncaught exception 'InvalidArgumentException'
with message 'The "blog_post" route has some
missing mandatory parameters (:slug).'
The Symfony Components – Fabien Potencier
104. $generator = new UrlGenerator($routes, array(
'base_url' => '/myapp',
'host' => 'www.example.com',
'is_secure' => false,
));
echo $generator->generate('home', array(), true);
http://www.example.com/myapp/
The Symfony Components – Fabien Potencier
105. The context
makes the routing
decoupled from the rest of the world
base_url
host
is_secure
method
The Symfony Components – Fabien Potencier
106. Routing
Describing your routes with XML or YAML
The Symfony Components – Fabien Potencier
107. home:
pattern: /
defaults: { controller: home, action: index }
blog_post:
pattern: /:year/:month/:day/:slug
defaults:
controller: blog
action: show
requirements:
year: d{4}
The Symfony Components – Fabien Potencier
109. <?xml version="1.0" encoding="UTF-8" ?>
<routes xmlns="http://www.symfony-project.org/schema/routing"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.symfony-project.org/schema/routing
http://www.symfony-project.org/schema/routing/routing-1.0.xsd">
<route id="blog_post" pattern="/:year/:month/:day/:slug">
<default key="controller">blog</default>
<default key="action">show</default>
<requirement key="year">d{4}</requirement>
</route>
<route id="home" pattern="/">
<default key="controller">home</default>
<default key="action">index</default>
</route>
</routes>
The Symfony Components – Fabien Potencier
111. <?xml version="1.0" encoding="UTF-8" ?>
<routes>
<route id="home" pattern="/">
<default key="controller">home</default>
<default key="action">index</default>
</route>
<import resource="blog.yml" prefix="/blog" />
<import resource="forum.xml" prefix="/forum" />
</routes>
The Symfony Components – Fabien Potencier
112. home:
pattern: /
defaults: { controller: home, action: index }
import:
- { resource: blog.yml, prefix: /blog }
- { resource: forum.xml, prefix: /forum }
The Symfony Components – Fabien Potencier
113. $yamlLoader = new YamlFileLoader();
$xmlLoader = new XmlFileLoader();
$routes = new RouteCollection();
$route = new Route(
'/',
array('to' => function () { echo "Home!"; })
);
$routes->addRoute('home', $route);
$routes->addCollection(
$yamlLoader->load('blog.yml'), '/blog');
$routes->addCollection(
$xmlLoader->load('forum.xml'), '/forum');
The Symfony Components – Fabien Potencier
115. Routing
Make it simple & fast
The Symfony Components – Fabien Potencier
117. $loader = function ()
{
$routes = new RouteCollection();
// ...
return $routes;
};
$context = array(
'base_url' => '/myapp',
'host' => 'www.example.com',
'is_secure' => false,
);
$options = array(
'cache_dir' => '/tmp/routing',
'debug' => true,
);
The Symfony Components – Fabien Potencier
118. $router = new Router($loader, $options, $context);
if (false === $params = $router->match('/'))
{
throw new Exception('No route matches.');
}
echo $router->generate('home', array());
The Symfony Components – Fabien Potencier
119. class ProjectUrlMatcher extends SymfonyComponentsRouting
MatcherUrlMatcher
{
// ...
public function match($url)
{
$url = $this->normalizeUrl($url);
if (preg_match('#^/$#x', $url, $matches))
return array_merge($this->mergeDefaults($matches, array
( 'to' => 'foo',)), array('_route' => 'home'));
return false;
}
}
The Symfony Components – Fabien Potencier
120. class ProjectUrlGenerator extends SymfonyComponentsRoutingGeneratorUrlGenerator
{
// ...
public function generate($name, array $parameters, $absolute = false)
{
if (!method_exists($this, $method = 'get'.$name.'RouteInfo'))
{
throw new InvalidArgumentException(sprintf('Route "%s" does not exist.', $name));
}
list($variables, $defaults, $requirements, $tokens) = $this->$method();
return $this->doGenerate($variables, $defaults, $requirements, $tokens, $parameters,
$name, $absolute);
}
protected function gethomeRouteInfo()
{
return array(array (), array_merge($this->defaults, array ( 'to' => 'foo',)), array
(), array ( 0 => array ( 0 => 'text', 1 => '/', 2 => '', 3 => NULL, ),));
}
}
The Symfony Components – Fabien Potencier
122. Routing
Make it really fast
The Symfony Components – Fabien Potencier
125. $options = array(
'cache_dir' => '/tmp/routing',
'debug' => true,
'matcher_class' => 'SymfonyComponents
RoutingMatcherApacheUrlMatcher',
);
The Symfony Components – Fabien Potencier
128. Wraps template variables
Works for
strings
arrays
objects
properties
methods
__call(), __get(), …
Iterators, Coutables, …
…
Works for deep method calls
The Symfony Components – Fabien Potencier
130. use SymfonyComponentsOutputEscaperEscaper;
$article = array(
'title' => 'Foo <br />',
'author' => array(
'name' => 'Fabien <br/>',
)
);
$article = Escaper::escape('htmlspecialchars', $article);
echo $article['title']."n";
echo $article['author']['name']."n";
The Symfony Components – Fabien Potencier
131. 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 method returning
public function __get($key) { return $this->$key; } another object
public function __call($method, $arguments)
{ magic __get()
return $this->{'get'.$method}(); magic __call()
}
}
The Symfony Components – Fabien Potencier
132. class Author
{
protected $name;
public function __construct($name) { $this->name = $name; }
public function getName() { return $this->name; }
}
The Symfony Components – Fabien Potencier
133. use SymfonyComponentsOutputEscaperEscaper;
$article = new Article(
'foo <br />',
new Author('Fabien <br />')
);
$article = Escaper::escape('htmlspecialchars', $article);
echo $article->getTitle()."n";
echo $article->getAuthor()->getName()."n";
echo $article->full_title."n";
echo $article->title."n";
echo $article->title()."n";
The Symfony Components – Fabien Potencier
134. explicitly ask
for raw data
echo $article->getHtmlContent('raw');
echo $article->getTitle('js');
change the default
escaping strategy
The Symfony Components – Fabien Potencier
138. use SymfonyComponentsRequestHandlerResponse;
$response = new Response('Hello World', 200,
array('Content-Type' => 'text/plain'));
$response->send();
$response->setHeader('Content-Type', 'text/plain');
$response->setCookie('foo', 'bar');
$response->setContent('Hello World');
$response->setStatusCode(200);
The Symfony Components – Fabien Potencier
139. Request Handler
Framework to build Frameworks
The Symfony Components – Fabien Potencier
142. Request Handler
A small Framework
The Symfony Components – Fabien Potencier
143. $framework = new Framework(array(
'/' => function ($request)
{
$content = 'Hello '.
$request->getParameter('name');
return new Response($content);
}
));
$framework->run();
The Symfony Components – Fabien Potencier
144. class Framework
{
protected $map;
public function __construct($map)
{
$this->map = $map;
}
public function run()
{
$dispatcher = new EventDispatcher();
$dispatcher->connect('core.load_controller', array($this, 'loadController'));
$handler = new RequestHandler($dispatcher);
$response = $handler->handle(new Request());
$response->send();
}
}
The Symfony Components – Fabien Potencier
145. public function loadController(Event $event)
{
$request = $event['request'];
$routes = new RouteCollection();
foreach ($this->map as $pattern => $to)
{
$route = new Route($pattern, array('to' => $to));
$routes->addRoute(str_replace('/', '_', $pattern), $route);
}
$matcher = new UrlMatcher($routes, array(
'base_url' => $request->getBaseUrl(),
'method' => $request->getMethod(),
'host' => $request->getHost(),
'is_secure' => $request->isSecure(),
));
$parameters = $matcher->match($request->getPathInfo());
if (false === $parameters)
{
return false;
}
$request->setPathParameters($parameters);
$event->setReturnValue(array($parameters['to'], array($request)));
return true;
}
The Symfony Components – Fabien Potencier
146. $framework = new Framework(array(
'/' => function ($request)
{
$content = 'Hello '.
$request->getParameter('name');
return new Response($content);
}
));
$framework->run();
The Symfony Components – Fabien Potencier
148. 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/
The Symfony Components – Fabien Potencier