Contenu connexe Similaire à Symfony Components (20) Plus de 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